需求背景
Bug Status 扩容
- 禅道原生只有 已激活,已解决,已关闭三种状态。无法满足线上问题扭转需求。
- 扩容后为
- active,resolved,closed,fixed,in_process,postpone
- Bug 描述字段扩容
- 为了满足线上问题的需求,新增字段如下图·
``sql -- 新增bug描述字段 2021.2.1 -- ALTER tablezt_bug` ADD COLUMN /**/ — ‘customerPackage’ = ‘客户产品包’; — ‘customer’ = ‘客户’; — ‘env’ = ‘环境’; — ‘issueTime’ = ‘问题所属时间’; — ‘founded’ = ‘发现问题方’; — ‘issueModule’ = ‘问题fix所属模块’; — ‘issueApp’ = ‘问题所属数栈应用’; — ‘issueAppModule’ = ‘产品包所属应用模块’; — ‘engine’ = ‘计算引擎’; — ‘version’ = ‘目标版本’;
- 为了满足线上问题的需求,新增字段如下图·
<a name="am7jn"></a># 二开思路 & 线上问题字段扩容支持<a name="a1c5a"></a>## DB 方面- -> 找到 维护bug信息的表,在禅道中为 zt_bug 。观察 status字段,发现其为enum,即枚举格式,需要对其进行修改,就是需要增加枚举的值。```sql-- 测试环境集成 增量sql 2021.1.29ALTER TABLE `zt_bug`ADD(`customerPackage` varchar(50) default null comment '客户产品包',`customer` varchar(50) default null comment '客户',`env` varchar(50) default null comment 'env',`mergeTime` date default null comment '问题所属时间',`founded` varchar(50) default null comment '发现问题方',`issueModule` varchar(50) default null comment '问题fix所属模块',`issueApp` varchar(50) default null comment '问题所属数栈应用',`issueAppModule` varchar(50) default null comment '产品包所属应用模块',`engine` varchar(50) default null comment '计算引擎',`version` varchar(50) default null comment '目标版本');
后端方面
- 首先要根据url访问路径大致确认要修改的代码在源代码的 module所处位置。
- 直接看实例
http://localhost:8888/www/bug-create-3-0-moduleID=0.html这样一个链接,首先可以确定到 module 为 bug,涉及到的视图文件名为 create 。
找到模块
进入到源码目录下,找bug module

目录解释
在视图文件中去寻找自己要修改的目标字段,因为本例中为添加新的字段,因此是没有类似的字段,但是可以参考源码中已经有的字段的写法。下见图

- 可以看到 os ,browse 字段的编写模式,按照源码的方式增加自定义的字段。
$customerList 为下拉框的值展示,其支持存在于 $lang->customerList 中,需要事前维护,或者是联用custom 模块对其字段进行自定义维护,后面涉及到custom模块的改造的时候会详细解释
修改字库
找到bug字段对应的字库,对其进行拓展,要注意的是
- 需要有单数形式的字库支持,同时也要定义 单数字库名List 的数组,来支持下拉框框选
修改方法
- 创建bug , 即create方法。
- 找到方法之后,就需要研读代码。读懂代码最好的方式是打断点进行debug,一个bug D上一天,虽愚也会有所闻的。
按照 规定 在 zentaopms/module/bug/ext/model/create.php 下进行修改,没有的话请创建,这是前面几张文章提到的开发规范
<?php/*** Create a bug.* 对 bug module 下的创建方法进行扩展** @param string $from object that is transfered to bug.* @access public* @return int|bool*/public function create($from = ''){$now = helper::now();$bug = fixer::input('post')// 取到当前登陆用户account信息->setDefault('openedBy', $this->app->user->account)// 取当前时间为 bug创建时间->setDefault('openedDate', $now)// 项目,需求,任务 默认值设置为0->setDefault('project,story,task', 0)// openedBuild->setDefault('openedBuild', '')// 默认截止时间->setDefault('deadline', '0000-00-00')->setIF(strpos($this->config->bug->create->requiredFields, 'deadline') !== false, 'deadline', $this->post->deadline)->setIF($this->post->assignedTo != '', 'assignedDate', $now)->setIF($this->post->story != false, 'storyVersion', $this->loadModel('story')->getVersion($this->post->story))->setIF(strpos($this->config->bug->create->requiredFields, 'project') !== false, 'project', $this->post->project)/* FIXME 新增线上问题支持字段$lang->bug->customer = '客户';$lang->bug->customerPackage = '客户产品包';$lang->bug->env = '环境';$lang->bug->mergeTime = '代码合并时间';$lang->bug->founded = '发现问题方';$lang->bug->issueModule = '问题fix所属模块';$lang->bug->issueAppModule = '产品包所属应用模块';$lang->bug->issueApp = '所属数栈应用';$lang->bug->engine = '计算引擎';$lang->bug->version = '目标版本';*/// 如果不为空,则进行赋值->setIF($this->post->customer != '','customer',$this->post->customer)->setIF($this->post->customerPackage != '','customerPackage',$this->post->customerPackage)->setIF($this->post->env != '','env',$this->post->env)->setIF($this->post->mergeTime != '','mergeTime',$this->post->mergeTime)->setIF($this->post->founded != '','founded',$this->post->founded)->setIF($this->post->issueModule != '','issueModule',$this->post->issueModule)->setIF($this->post->issueAppModule != '','issueAppModule',$this->post->issueAppModule)->setIF($this->post->issueApp != '','issueApp',$this->post->issueApp)->setIF($this->post->engine != '','engine',$this->post->engine)->setIF($this->post->version != '','version',$this->post->version)// ------end --------->stripTags($this->config->bug->editor->create['id'], $this->config->allowedTags)->cleanInt('product,project,module,severity')->join('openedBuild', ',')->join('mailto', ',')->remove('files, labels,uid,oldTaskID,contactListMenu')->get();/* Check repeat bug. */$result = $this->loadModel('common')->removeDuplicate('bug', $bug, "product={$bug->product}");if($result['stop']) return array('status' => 'exists', 'id' => $result['duplicate']);$bug = $this->loadModel('file')->processImgURL($bug, $this->config->bug->editor->create['id'], $this->post->uid);$this->dao->insert(TABLE_BUG)->data($bug)->autoCheck()->batchCheck($this->config->bug->create->requiredFields, 'notempty')->exec();if(!dao::isError()){$bugID = $this->dao->lastInsertID();$this->file->updateObjectID($this->post->uid, $bugID, 'bug');$this->file->saveUpload('bug', $bugID);empty($bug->case) ? $this->loadModel('score')->create('bug', 'create', $bugID) : $this->loadModel('score')->create('bug', 'createFormCase', $bug->case);/* Callback the callable method to process the related data for object that is transfered to bug. */if($from && is_callable(array($this, $this->config->bug->fromObjects[$from]['callback']))) call_user_func(array($this, $this->config->bug->fromObjects[$from]['callback']), $bugID);return array('status' => 'created', 'id' => $bugID);}return false;}
上面的代码是已经修改了的代码,其中 this->post->customer 等值是前端页面发送回来的 bug 线上字段 相关信息,因为php的语法有些特殊,所以看起来有些奇怪。
同时需要注意的时,由前端发送回的信息,需要与create中后端方法接收的变量一直,否则会出现问题。
衍生需求
在去对线上问题字段进行支持的时候,发现很多原生字段支持,但是新增字段未进行支持的字段。大概涉及下面几种,
Bug 详情页展示,展示包含线上问题字段。
- Bug 搜索,支持对线上问题字段信息进行检索。
- Custom 模块对 线上问题相关字段的维护,增删查改。
- Bug 导出功能 对线上问题相关字段的导出支持。
-
Bug 详情页展示,包含线上问题字段

-
根据Url找到对应源码
-
观察原生代码实现,tab点击切换原理

上图中为 视图中的tab
分析之后发现点击之后跳转至 href后的id,对应表单在下方进行了定义。
- 去字库中查询对应信息。

- 对字库信息进行拓展,这里因为两个视图中都用到此字段,于是一个字段设置解决
-
模仿原生的写法,添加线上问题的tab,并进行显示
<!-- FIXME 线上问题 tab --><?php if ($config->global->flow != 'onlyTest' and $productName == '线上问题统计'): ?><div class='tab-pane' id='onlineIssue'><table class='table table-data'><tbody class="text-center"><!-- 需要展示的字段$lang->bug->customer = '客户';$lang->bug->customerPackage = '客户产品包';$lang->bug->env = '环境';$lang->bug->mergeTime = '代码合并时间';$lang->bug->founded = '发现问题方';$lang->bug->issueModule = '问题fix所属模块';$lang->bug->issueAppModule = '产品包所属应用模块';$lang->bug->issueApp = '所属数栈应用';$lang->bug->engine = '计算引擎';$lang->bug->version = '目标版本';--><tr><th class='w-120px '><?php echo $lang->bug->customer; ?></th>这里调用的是字库中的信息,$bug对象中存放的customer 之类的信息全部都是key,需要去字库的list中key value对应取值<td><?php if (isset($lang->bug->customerList[$bug->customer])) echo $lang->bug->customerList[$bug->customer]; else echo $bug->customer; ?></td></tr><tr><th class='w-120px'><?php echo $lang->bug->customerPackage; ?></th><td><?php if (isset($lang->bug->customerPackageList[$bug->customerPackage])) echo $lang->bug->customerPackageList[$bug->customerPackage]; else echo $bug->customerPackage; ?></td></tr><tr><th class='w-120px'><?php echo $lang->bug->env; ?></th><td><?php if (isset($lang->bug->envList[$bug->env])) echo $lang->bug->envList[$bug->env]; else echo $bug->env; ?></td></tr><tr><th class='w-120px'><?php echo $lang->bug->mergeTime; ?></th><td><?php echo $bug->mergeTime ?></td></tr><tr><th class='w-120px'><?php echo $lang->bug->founded; ?></th><td><?php if (isset($lang->bug->foundedList[$bug->founded])) echo $lang->bug->foundedList[$bug->founded]; else echo $bug->founded; ?></td></tr><tr><th class='w-120px'><?php echo $lang->bug->issueModule; ?></th><td><?php if (isset($lang->bug->issueModuleList[$bug->issueModule])) echo $lang->bug->issueModuleList[$bug->issueModule]; else echo $bug->issueModule; ?></td></tr><tr><th class='w-140px'><?php echo $lang->bug->issueAppModule; ?></th><td><?php if (isset($lang->bug->issueAppModuleList[$bug->issueAppModule])) echo $lang->bug->issueAppModuleList[$bug->issueAppModule]; else echo $bug->issueAppModule; ?></td></tr><tr><th class='w-120px'><?php echo $lang->bug->issueApp; ?></th><td><?php if (isset($lang->bug->issueAppList[$bug->issueApp])) echo $lang->bug->issueAppList[$bug->issueApp]; else echo $bug->issueApp; ?></td></tr><tr><th class='w-120px'><?php echo $lang->bug->engine; ?></th><td><?php if (isset($lang->bug->engineList[$bug->engine])) echo $lang->bug->engineList[$bug->engine]; else echo $bug->engine; ?></td></tr><tr><th class='w-120px'><?php echo $lang->bug->version; ?></th><td><?php if (isset($lang->bug->versionList[$bug->version])) echo $lang->bug->versionList[$bug->version]; else echo $bug->version; ?></td></tr></tbody></table></div><?php endif; ?><!-- FIXME 线上问题 tab end -->
Bug 搜索,支持对线上问题字段信息进行检索

同上面所叙述,原生字段支持,而新增字段不支持,需要对其进行修改。
确定要修改的文件
浏览 config 文件下发现search相关字段

- 对其进行拓展,以支持新增线上字段 ```php <?php // TODO global lang 搜索字段在线添加,以支持搜索字段 $config->bug->search[‘fields’][‘status’] = $lang->bug->status; $config->bug->search[‘fields’][‘confirmed’] = $lang->bug->confirmed; $config->bug->search[‘fields’][‘customer’] = $lang->bug->customer; $config->bug->search[‘fields’][‘customerPackage’] = $lang->bug->customerPackage; $config->bug->search[‘fields’][‘env’] = $lang->bug->env; $config->bug->search[‘fields’][‘founded’] = $lang->bug->founded; $config->bug->search[‘fields’][‘issueModule’] = $lang->bug->issueModule; $config->bug->search[‘fields’][‘issueAppModule’] = $lang->bug->issueAppModule; $config->bug->search[‘fields’][‘issueApp’] = $lang->bug->issueApp; $config->bug->search[‘fields’][‘engine’] = $lang->bug->engine; $config->bug->search[‘fields’][‘version’] = $lang->bug->version;
// 字段后的搜索显示为下拉框 $config->bug->search[‘params’][‘customer’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->customerList); $config->bug->search[‘params’][‘customerPackage’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->customerPackageList); $config->bug->search[‘params’][‘env’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->envList); $config->bug->search[‘params’][‘founded’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->foundedList); $config->bug->search[‘params’][‘issueModule’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->issueModuleList); $config->bug->search[‘params’][‘issueAppModule’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->issueAppModuleList); $config->bug->search[‘params’][‘issueApp’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->issueAppList); $config->bug->search[‘params’][‘engine’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->engineList); $config->bug->search[‘params’][‘version’] = array(‘operator’ => ‘=’, ‘control’ => ‘select’, ‘values’ => $lang->bug->versionList);
<a name="y5dlf"></a>## Custom模块对线上问题相关字段维护,CRUD- - 上图为已经修改之后的效果。<a name="4Zb6m"></a>### 根据Url找到对应源码- custom 模块 set 视图,<a name="GXfQ2"></a>### 对config进行拓展- config 文件下配置了canadd 也就是可以增加的字段配置- - 对其进行拓展```php<?php// FIXME 增加自定义页面下可以编辑添加的字段$config->custom->canAdd['bug'] = 'priList,severityList,osList,browserList,typeList,resolutionList,customerPackageList,customerList,envList,foundedList,issueModuleList,issueAppList,issueAppModuleList,engineList,versionList';// FIXME ============end-----------------------------------
对字库进行拓展
<?php// FIXME 考虑redmine线上问题迁移过来需要控制的字段。$lang->custom->bug->fields['customerPackageList'] = '客户产品包';$lang->custom->bug->fields['customerList'] = '客户';$lang->custom->bug->fields['envList'] = '环境';$lang->custom->bug->fields['foundedList'] = '发现问题方';$lang->custom->bug->fields['issueModuleList'] = '问题fix所属模块';$lang->custom->bug->fields['issueAppList'] = '问题所属数栈应用';$lang->custom->bug->fields['issueAppModuleList'] = '产品包所属应用模块';$lang->custom->bug->fields['engineList'] = '计算引擎';$lang->custom->bug->fields['versionList'] = '目标版本';// --------------------------------------------
custom模块总结
- 对 字段的维护,存放在 zt_lang 表中

- 每次对字段进行自定义更新,都会存放在 zt_lang下,如果没有对其进行更新的话,默认取值为上面bug新增字段中定义的customerList中的值

添加 custom 自定义支持之后,对客户,环境,发现问题方等字段可以通过页面ui进行维护
Bug 导出功能,对线上问题相关字段进行支持
直接通过查看前端源码拿到url
-
分析Url 找到源码
bug 模块下 export 视图
-
没有线索,延伸查找control层

发现目标。模仿源码进行扩展修改。
-
config模块下扩展输出字段控制
注意点
值得一提的是,bug字段信息在禅道中的流转都是按照key来存储的,值是存放在 lang 或者是 zt_lang扩展的字段下,所以不按照数组取值的方式去输出,或者进行前端展示的话,展示出来的都只是key。而非值
注意:开源版的功能中导出Bug不能导出相关截图,留作日后研发课题。
Bug表单自定义显示支持线上字段
分析调用
找到源码
datatable 模块 ajaxCustom 视图

- 顺着调用去找到module层下面的 getFieldList方法

查看到该方法为 module 为参数,返回为读取 config->module->datatable->fieldList
查找bug模块下config下面是否有相关配置信息

对其进行拓展,然后测试
-
测试结果
但是经此修改之后,有列显示,但没有值
继续进行debug,发现是bug 模块下的 printcell方法控制了值的显示,对其进行扩展修改之后正常显示内容。 ```php <?php /**
- Print cell data. *
- @param object $col
- @param object $bug
- @param array $users
- @param array $builds
- @param array $branches
- @param array $modulePairs
- @param array $projects
- @param array $plans
- @param array $stories
- @param array $tasks
- @param string $mode
- @access public
@return void */ public function printCell($col, $bug, $users, $builds, $branches, $modulePairs, $projects = array(), $plans = array(), $stories = array(), $tasks = array(), $mode = ‘datatable’) { $canBatchEdit = common::hasPriv(‘bug’, ‘batchEdit’); $canBatchConfirm = common::hasPriv(‘bug’, ‘batchConfirm’); $canBatchClose = common::hasPriv(‘bug’, ‘batchClose’); $canBatchActivate = common::hasPriv(‘bug’, ‘batchActivate’); $canBatchChangeBranch = common::hasPriv(‘bug’, ‘batchChangeBranch’); $canBatchChangeModule = common::hasPriv(‘bug’, ‘batchChangeModule’); $canBatchResolve = common::hasPriv(‘bug’, ‘batchResolve’); $canBatchAssignTo = common::hasPriv(‘bug’, ‘batchAssignTo’);
$canBatchAction = $canBatchEdit or $canBatchConfirm or $canBatchClose or $canBatchActivate or $canBatchChangeBranch or $canBatchChangeModule or $canBatchResolve or $canBatchAssignTo;
$canView = common::hasPriv(‘bug’, ‘view’);
$hasCustomSeverity = false; foreach ($this->lang->bug->severityList as $severityKey => $severityValue) {
if (!empty($severityKey) and (string)$severityKey != (string)$severityValue) {$hasCustomSeverity = true;break;}
}
$bugLink = inlink(‘view’, “bugID=$bug->id”); $account = $this->app->user->account; $id = $col->id; if ($col->show) {
$class = "c-$id";$title = '';if ($id == 'id') $class .= ' cell-id';if ($id == 'status') {$class .= ' bug-' . $bug->status;$title = "title='" . $this->processStatus('bug', $bug) . "'";}if ($id == 'confirmed') {$class .= ' text-center';}if ($id == 'title') {$class .= ' text-left';$title = "title='{$bug->title}'";}if ($id == 'type') {$title = "title='" . zget($this->lang->bug->typeList, $bug->type) . "'";}if ($id == 'assignedTo') {$class .= ' has-btn text-left';if ($bug->assignedTo == $account) $class .= ' red';}if ($id == 'deadline' && isset($bug->delay)) $class .= ' delayed';if (strpos(',project,story,plan,task,openedBuild,', ",{$id},") !== false) $class .= ' text-ellipsis';echo "<td class='" . $class . "' $title>";if (isset($this->config->bizVersion)) $this->loadModel('flow')->printFlowCell('bug', $bug, $id);switch ($id) {case 'id':if ($canBatchAction) {echo html::checkbox('bugIDList', array($bug->id => '')) . html::a(helper::createLink('bug', 'view', "bugID=$bug->id"), sprintf('%03d', $bug->id));} else {printf('%03d', $bug->id);}break;case 'severity':if ($hasCustomSeverity) {echo "<span class='label-severity-custom' data-severity='{$bug->severity}' title='" . zget($this->lang->bug->severityList, $bug->severity) . "'>" . zget($this->lang->bug->severityList, $bug->severity) . "</span>";} else {echo "<span class='label-severity' data-severity='{$bug->severity}' title='" . zget($this->lang->bug->severityList, $bug->severity) . "'></span>";}break;case 'pri':echo "<span class='label-pri label-pri-" . $bug->pri . "' title='" . zget($this->lang->bug->priList, $bug->pri, $bug->pri) . "'>";echo zget($this->lang->bug->priList, $bug->pri, $bug->pri);echo "</span>";break;case 'confirmed':$class = 'confirm' . $bug->confirmed;echo "<span class='$class'>" . zget($this->lang->bug->confirmedList, $bug->confirmed, $bug->confirmed) . "</span> ";break;case 'title':if ($bug->branch and isset($branches[$bug->branch])) echo "<span class='label label-outline label-badge'>{$branches[$bug->branch]}</span> ";if ($bug->module and isset($modulePairs[$bug->module])) echo "<span class='label label-gray label-badge'>{$modulePairs[$bug->module]}</span> ";echo $canView ? html::a($bugLink, $bug->title, null, "style='color: $bug->color'") : "<span style='color: $bug->color'>{$bug->title}</span>";break;case 'branch':echo zget($branches, $bug->branch, '');break;case 'project':echo zget($projects, $bug->project, '');break;case 'plan':echo zget($plans, $bug->plan, '');break;case 'story':if (isset($stories[$bug->story])) {$story = $stories[$bug->story];echo common::hasPriv('story', 'view') ? html::a(helper::createLink('story', 'view', "storyID=$story->id", 'html', true), $story->title, '', "class='iframe'") : $story->title;}break;case 'task':if (isset($tasks[$bug->task])) {$task = $tasks[$bug->task];echo common::hasPriv('task', 'view') ? html::a(helper::createLink('task', 'view', "taskID=$task->id", 'html', true), $task->name, '', "class='iframe'") : $task->name;}break;case 'type':echo zget($this->lang->bug->typeList, $bug->type);break;case 'status':echo "<span class='status-bug status-{$bug->status}'>";echo $this->processStatus('bug', $bug);echo '</span>';break;case 'activatedCount':echo $bug->activatedCount;break;case 'activatedDate':echo substr($bug->activatedDate, 5, 11);break;case 'keywords':echo $bug->keywords;break;case 'os':echo zget($this->lang->bug->osList, $bug->os);break;case 'browser':echo zget($this->lang->bug->browserList, $bug->browser);break;case 'customer':echo zget($this->lang->bug->customerList, $bug->customer);break;case 'customerPackage':echo zget($this->lang->bug->customerPackageList, $bug->customerPackage);break;case 'env':echo zget($this->lang->bug->envList, $bug->env);break;case 'founded':echo zget($this->lang->bug->foundedList, $bug->founded);break;case 'issueModule':echo zget($this->lang->bug->issueModuleList, $bug->issueModule);break;case 'issueAppModule':echo zget($this->lang->bug->issueAppModuleList, $bug->issueAppModule);break;case 'issueApp':echo zget($this->lang->bug->issueAppList, $bug->issueApp);break;case 'engine':echo zget($this->lang->bug->engineList, $bug->engine);break;case 'version':echo zget($this->lang->bug->versionList, $bug->version);break;case 'mergeTime':echo $bug->mergeTime;break;case 'mailto':$mailto = explode(',', $bug->mailto);foreach ($mailto as $account) {$account = trim($account);if (empty($account)) continue;echo zget($users, $account) . " ";}break;case 'found':echo zget($users, $bug->found);break;case 'openedBy':echo zget($users, $bug->openedBy);break;case 'openedDate':echo substr($bug->openedDate, 5, 11);break;case 'openedBuild':$builds = array_flip($builds);foreach (explode(',', $bug->openedBuild) as $build) {$buildID = zget($builds, $build, '');if ($buildID == 'trunk') {echo $build;} elseif ($buildID and common::hasPriv('build', 'view')) {echo html::a(helper::createLink('build', 'view', "buildID=$buildID"), $build, '', "title='$bug->openedBuild'");} else {echo $build;}}break;case 'assignedTo':$this->printAssignedHtml($bug, $users);break;case 'assignedDate':echo substr($bug->assignedDate, 5, 11);break;case 'deadline':echo $bug->deadline;break;case 'resolvedBy':echo zget($users, $bug->resolvedBy, $bug->resolvedBy);break;case 'resolution':echo zget($this->lang->bug->resolutionList, $bug->resolution);break;case 'resolvedDate':echo substr($bug->resolvedDate, 5, 11);break;case 'resolvedBuild':echo $bug->resolvedBuild;break;case 'closedBy':echo zget($users, $bug->closedBy);break;case 'closedDate':echo substr($bug->closedDate, 5, 11);break;case 'lastEditedBy':echo zget($users, $bug->lastEditedBy);break;case 'lastEditedDate':echo substr($bug->lastEditedDate, 5, 11);break;case 'actions':$params = "bugID=$bug->id";common::printIcon('bug', 'confirmBug', $params, $bug, 'list', 'confirm', '', 'iframe', true);common::printIcon('bug', 'resolve', $params, $bug, 'list', 'checked', '', 'iframe', true);common::printIcon('bug', 'close', $params, $bug, 'list', '', '', 'iframe', true);common::printIcon('bug', 'edit', $params, $bug, 'list');common::printIcon('bug', 'create', "product=$bug->product&branch=$bug->branch&extra=$params", $bug, 'list', 'copy');break;}echo '</td>';
} }

