This commit is contained in:
chaideqing
2023-05-06 17:39:59 +08:00
17 changed files with 508 additions and 265 deletions
+11
View File
@@ -963,3 +963,14 @@ function floatToolbar()
{
return createWg('floatToolbar', func_get_args());
}
/**
* zentao data group.
*
* string label,
* string labelClass?
*/
function dataGroup()
{
return createWg('dataGroup', func_get_args());
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace zin;
class dataGroup extends wg
{
protected static $defineProps = array(
'label:string',
'labelClass?:string'
);
private function label(string $text)
{
$className = $this->prop('labelClass');
return div
(
setClass($className, 'text-gray', 'text-right', 'w-16'),
$text
);
}
protected function build()
{
$text = $this->prop('label');
return div
(
set($this->props->skip(array_keys(static::getDefinedProps()))),
setClass('data-group flex justify-start gap-2 my-4'),
$this->label($text),
$this->children(),
);
}
}
+8 -4
View File
@@ -228,16 +228,20 @@ class block extends control
*/
public function dashboard(string $dashboard, string $projectID = '0')
{
$projectID = (int)$projectID;
$projectID = (int)$projectID; // 强制转换为 int 类型,防止调用 model、tao 方法时报错
/* 获取传入应用对应的区块列表 以及 获取当前应用下区块启用状态 */
$blocks = $this->block->getMyDashboard($dashboard);
$isInitiated = $this->block->fetchBlockInitStatus($dashboard);
/* Init block when vist index first. */
if(empty($blocks) and !$isInitiated and !defined('TUTORIAL') and $this->block->initBlock($dashboard))
/* 判断用户是否为首次登录 ,判断条件 当前用户没有该 app 下的区块数据 且 没有设置过该 app 下的区块启用状态 且不是演示模式 */
if(empty($blocks) and !$isInitiated and !defined('TUTORIAL'))
{
return print(js::reload());
$this->block->initBlock($dashboard); // 初始化该 app 下区块数据
$blocks = $this->block->getMyDashboard($dashboard); // 获取初始化后的区块列表
}
/* */
$blocks = $this->blockZen->processBlockForRender($blocks, $projectID);
if($this->app->getViewType() == 'json') return print(json_encode($blocks));
+6 -6
View File
@@ -298,7 +298,7 @@ class blockModel extends model
/**
* Reset dashboard blocks.
*
* @param string $dashboard
* @param string $dashboard
* @access public
* @return bool
*/
@@ -323,7 +323,7 @@ class blockModel extends model
/**
* Hidden a block.
*
* @param int $blockID
* @param int $blockID
* @access public
* @return bool
*/
@@ -341,7 +341,7 @@ class blockModel extends model
/**
* Delete a block.
*
* @param int $blockID
* @param int $blockID
* @access public
* @return bool
*/
@@ -358,9 +358,9 @@ class blockModel extends model
/**
* Set block order.
*
* @param int $blockID
* @param int $order
*
* @param int $blockID
* @param int $order
* @access public
* @return bool
*/
+2 -2
View File
@@ -3,10 +3,10 @@ declare(strict_types=1);
class blockTao extends blockModel
{
/**
* Get block list of current user.
* 获取当前用户的区块列表.
* Get block list of current user.
*
* @param string $module
* @param string $dashboard
* @param int $hidden 0|1
* @access protected
* @return int[]|false
+12 -9
View File
@@ -153,47 +153,50 @@ class blockZen extends block
*/
protected function processBlockForRender(array $blocks, int $projectID): array
{
/* 根据用户的权限,和当前系统开启的权限 处理区块列表 */
$acls = $this->app->user->rights['acls'];
foreach($blocks as $key => $block)
{
/* 将没有开启功能区块过虑 */
if($block->code == 'waterfallrisk' and !helper::hasFeature("waterfall_risk")) continue;
if($block->code == 'waterfallissue' and !helper::hasFeature("waterfall_issue")) continue;
if($block->code == 'scrumrisk' and !helper::hasFeature("scrum_risk")) continue;
if($block->code == 'scrumissue' and !helper::hasFeature("scrum_issue")) continue;
if(!empty($block->source) and $block->source != 'todo' and !empty($acls['views']) and !isset($acls['views'][$block->source]))
/* 将没有视图权限的区块过滤 */
if(!empty($block->module) and $block->module != 'todo' and !empty($acls['views']) and !isset($acls['views'][$block->module]))
{
unset($blocks[$key]);
continue;
}
/* 处理 params 信息中 count 的值,当没有 count 字段时 ,将 num 字段赋值给 count */
$block->params = json_decode($block->params);
if(isset($block->params->num) and !isset($block->params->count)) $block->params->count = $block->params->num;
$this->getBlockMoreLink($block, $projectID);
/* 补全加载链接 */
$this->computeMoreLink($block, $projectID);
}
return $blocks;
}
/**
* 获取区块的更多链接。
* 补全区块的加载链接。
* Get the more link of the block.
*
* @param object $block
* @param int $projectID
* @return void
*/
private function getBlockMoreLink(object $block, int $projectID): void
private function computeMoreLink(object $block, int $projectID): void
{
$code = $block->code;
$source = empty($block->source) ? 'common' : $block->source;
$module = empty($block->module) ? 'common' : $block->module;
$block->blockLink = $this->createLink('block', 'printBlock', "id=$block->id&module=$block->module");
$block->moreLink = '';
if(isset($this->config->block->modules[$source]->moreLinkList->{$code}))
if(isset($this->config->block->modules[$module]->moreLinkList->{$block->code}))
{
list($moduleName, $method, $vars) = explode('|', sprintf($this->config->block->modules[$source]->moreLinkList->{$code}, isset($block->params->type) ? $block->params->type : ''));
list($moduleName, $method, $vars) = explode('|', sprintf($this->config->block->modules[$module]->moreLinkList->{$block->code}, isset($block->params->type) ? $block->params->type : ''));
/* The list assigned to me jumps to the work page when click more button. */
$block->moreLink = $this->createLink($moduleName, $method, $vars);
+1
View File
@@ -409,6 +409,7 @@ class branchModel extends model
if(is_array($productID))
{
foreach($productID as $id) $this->linkBranch4Project($id);
return;
}
$linkedBranchProject = array();
+28 -13
View File
@@ -16,18 +16,33 @@ $config->product->form->create['status'] = array('type' => 'string', 'contro
$config->product->form->create['desc'] = array('type' => 'string', 'control' => 'editor', 'required' => false, 'default' => '', 'width' => 'full');
$config->product->form->create['acl'] = array('type' => 'string', 'control' => 'radio', 'required' => false, 'default' => 'private', 'width' => 'full', 'options' => $lang->product->aclList);
$config->product->form->create['whitelist'] = array('type' => 'array', 'control' => 'multi-select', 'required' => false, 'default' => '', 'filter' => 'join', 'width' => 'full', 'options' => 'users');
if($config->systemMode != 'ALM') unset($config->product->form->create['program'], $config->product->form->create['line'], $config->product->form->create['lineName']);
$config->product->form->edit = array();
$config->product->form->edit['program'] = array('type' => 'int', 'control' => 'select', 'required' => false, 'default' => 0, 'options' => array());
$config->product->form->edit['line'] = array('type' => 'int', 'control' => 'select', 'required' => false, 'default' => 0, 'options' => array());
$config->product->form->edit['name'] = array('type' => 'string', 'control' => 'text', 'required' => true, 'default' => '', 'filter' => 'trim');
$config->product->form->edit['code'] = array('type' => 'string', 'control' => 'text', 'required' => true, 'default' => '', 'filter' => 'trim');
$config->product->form->edit['PO'] = array('type' => 'account', 'control' => 'select', 'required' => false, 'default' => '', 'options' => array());
$config->product->form->edit['QD'] = array('type' => 'account', 'control' => 'select', 'required' => false, 'default' => '', 'options' => array());
$config->product->form->edit['RD'] = array('type' => 'account', 'control' => 'select', 'required' => false, 'default' => '', 'options' => array());
$config->product->form->edit['reviewer'] = array('type' => 'array', 'control' => 'multi-select', 'required' => false, 'default' => '', 'filter' => 'join', 'options' => 'users');
$config->product->form->edit['type'] = array('type' => 'string', 'control' => 'select', 'required' => false, 'default' => 'normal', 'options' => $lang->product->typeList);
$config->product->form->edit['status'] = array('type' => 'string', 'control' => 'select', 'required' => false, 'default' => 'normal', 'options' => $lang->product->statusList);
$config->product->form->edit['desc'] = array('type' => 'string', 'control' => 'textarea', 'required' => false, 'default' => '');
$config->product->form->edit['acl'] = array('type' => 'string', 'control' => 'radio', 'required' => false, 'default' => 'private', 'options' => $lang->product->aclList);
$config->product->form->edit['whitelist'] = array('type' => 'array', 'control' => 'multi-select', 'required' => false, 'default' => '', 'filter' => 'join', 'options' => 'users');
$config->product->form->edit['program'] = array('type' => 'int', 'control' => 'select', 'required' => false, 'default' => 0, 'options' => array());
$config->product->form->edit['line'] = array('type' => 'int', 'control' => 'select', 'required' => false, 'default' => 0, 'options' => array());
$config->product->form->edit['name'] = array('type' => 'string', 'control' => 'text', 'required' => true, 'default' => '', 'filter' => 'trim');
$config->product->form->edit['code'] = array('type' => 'string', 'control' => 'text', 'required' => true, 'default' => '', 'filter' => 'trim');
$config->product->form->edit['PO'] = array('type' => 'account', 'control' => 'select', 'required' => false, 'default' => '', 'options' => array());
$config->product->form->edit['QD'] = array('type' => 'account', 'control' => 'select', 'required' => false, 'default' => '', 'options' => array());
$config->product->form->edit['RD'] = array('type' => 'account', 'control' => 'select', 'required' => false, 'default' => '', 'options' => array());
$config->product->form->edit['reviewer'] = array('type' => 'array', 'control' => 'multi-select', 'required' => false, 'default' => '', 'filter' => 'join', 'options' => 'users');
$config->product->form->edit['type'] = array('type' => 'string', 'control' => 'select', 'required' => false, 'default' => 'normal', 'options' => $lang->product->typeList);
$config->product->form->edit['status'] = array('type' => 'string', 'control' => 'select', 'required' => false, 'default' => 'normal', 'options' => $lang->product->statusList);
$config->product->form->edit['desc'] = array('type' => 'string', 'control' => 'editor', 'required' => false, 'default' => '');
$config->product->form->edit['acl'] = array('type' => 'string', 'control' => 'radio', 'required' => false, 'default' => 'private', 'options' => $lang->product->aclList);
$config->product->form->edit['whitelist'] = array('type' => 'array', 'control' => 'multi-select', 'required' => false, 'default' => '', 'filter' => 'join', 'options' => 'users');
if($config->systemMode != 'ALM') unset($config->product->form->edit['program'], $config->product->form->edit['line']);
$config->product->form->batchEdit = array();
$config->product->form->batchEdit['program'] = array('type' => 'array', 'control' => 'select', 'required' => false, 'default' => 0, 'options' => array());
$config->product->form->batchEdit['name'] = array('type' => 'array', 'control' => 'text', 'required' => true, 'default' => '');
$config->product->form->batchEdit['line'] = array('type' => 'array', 'control' => 'select', 'required' => false, 'default' => 0, 'options' => array());
$config->product->form->batchEdit['PO'] = array('type' => 'array', 'control' => 'select', 'required' => false, 'default' => '', 'options' => array());
$config->product->form->batchEdit['QD'] = array('type' => 'array', 'control' => 'select', 'required' => false, 'default' => '', 'options' => array());
$config->product->form->batchEdit['RD'] = array('type' => 'array', 'control' => 'select', 'required' => false, 'default' => '', 'options' => array());
$config->product->form->batchEdit['type'] = array('type' => 'array', 'control' => 'select', 'required' => false, 'default' => 'normal', 'options' => $lang->product->typeList);
$config->product->form->batchEdit['status'] = array('type' => 'array', 'control' => 'select', 'required' => false, 'default' => 'normal', 'options' => $lang->product->statusList);
$config->product->form->batchEdit['desc'] = array('type' => 'array', 'control' => 'textarea', 'required' => false, 'default' => '');
$config->product->form->batchEdit['acl'] = array('type' => 'array', 'control' => 'radio', 'required' => false, 'default' => 'private', 'options' => $lang->product->aclList);
if($config->systemMode != 'ALM') unset($config->product->form->batchEdit['program'], $config->product->form->batchEdit['line']);
+39 -77
View File
@@ -16,8 +16,8 @@ class product extends control
/**
* Construct function.
*
* @param moduleName
* @param methodName
* @param string moduleName
* @param string methodName
* @access public
* @return void
*/
@@ -390,8 +390,9 @@ class product extends control
if(!empty($_POST))
{
$data = form::data($this->config->product->form->create);
$product = $this->productZen->prepareCreateExtras($data, $this->post->acl, $this->post->uid);
$formConfig = $this->productZen->appendFlowFields($this->config->product->form->create);
$data = form::data($formConfig);
$product = $this->productZen->prepareCreateExtras($data, $this->post->acl, $this->post->uid);
$productID = $this->product->create($product, $this->post->uid, zget($_POST, 'lineName', ''));
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
@@ -421,8 +422,9 @@ class product extends control
if(!empty($_POST))
{
$data = form::data($this->config->product->form->edit);
$product = $this->productZen->prepareEditExtras($data, $this->post->acl, $this->post->uid);
$formConfig = $this->productZen->appendFlowFields($this->config->product->form->edit);
$data = form::data($formConfig);
$product = $this->productZen->prepareEditExtras($data, $this->post->acl, $this->post->uid);
$changes = $this->product->update($productID, $product, $this->post->uid);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
@@ -441,17 +443,37 @@ class product extends control
}
/**
* 根据POST过来的ID列表,批量编辑相应产品。
* Batch edit products.
*
* @param int $programID
* @param string $programID
* @access public
* @return void
*/
public function batchEdit($programID = 0)
public function batchEdit(string $programID = '0')
{
if($this->post->names)
$programID = (int)$programID;
$_POST['productIdList'][1] = 1;
$_POST['name'][1] = '公司企业网站建设';
$_POST['PO'][1] = 'productManager';
$_POST['QD'][1] = 'testManager';
$_POST['RD'][1] = 'productManager';
$_POST['type'][1] = 'normal';
$_POST['status'][1] = 'normal';
$_POST['desc'][1] = '建立公司企业网站,可以更好对外展示。<br />';
$_POST['acl'][1] = 'open';
if($this->post->name)
{
$allChanges = $this->product->batchUpdate();
$formConfig = $this->productZen->appendFlowFields($this->config->product->form->batchEdit, 'batch');
$data = form::data($formConfig);
$products = $this->productZen->prepareBatchEditExtras($data);
if(dao::isError()) $this->send(array('result' => 'fail', 'message' => dao::getError()));
$allChanges = $this->product->batchUpdate($products);
if(!empty($allChanges))
{
foreach($allChanges as $productID => $changes)
@@ -467,76 +489,16 @@ class product extends control
return print(js::locate($locate, 'parent'));
}
$productIDList = $this->post->productIDList;
if(empty($productIDList)) return print(js::locate($this->session->productList, 'parent'));
/* 获取要修改的产品ID列表。*/
$productIdList = $this->post->productIDList;
$productIdList = '79,40,68';
if(empty($productIdList)) return print(js::locate($this->session->productList, 'parent'));
/* Set menu when page come from program. */
if($this->app->tab == 'program') $this->loadModel('program')->setMenu(0);
/* Set custom. */
foreach(explode(',', $this->config->product->customBatchEditFields) as $field) $customFields[$field] = $this->lang->product->$field;
$this->view->customFields = $customFields;
$this->view->showFields = $this->config->product->custom->batchEditFields;
$products = $this->dao->select('*')->from(TABLE_PRODUCT)->where('id')->in($productIDList)->fetchAll('id');
$appendPoUsers = $appendQdUsers = $appendRdUsers = array();
foreach($products as $product)
{
$appendPoUsers[$product->PO] = $product->PO;
$appendQdUsers[$product->QD] = $product->QD;
$appendRdUsers[$product->RD] = $product->RD;
}
$this->loadModel('user');
$poUsers = $this->user->getPairs('nodeleted|noclosed|pofirst', $appendPoUsers);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["PO"] = $this->config->user->moreLink;
$qdUsers = $this->user->getPairs('nodeleted|noclosed|qdfirst', $appendQdUsers);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["QD"] = $this->config->user->moreLink;
$rdUsers = $this->user->getPairs('nodeleted|noclosed|devfirst', $appendRdUsers);
if(!empty($this->config->user->moreLink)) $this->config->moreLinks["RD"] = $this->config->user->moreLink;
$programs = array();
$unauthorizedPrograms = array();
if($this->config->systemMode == 'ALM')
{
$programs = $this->loadModel('program')->getTopPairs();
/* Get unauthorized programs. */
$programIDList = array();
foreach($products as $product)
{
if($product->program and !isset($programs[$product->program]) and !in_array($product->program, $programIDList)) $programIDList[] = $product->program;
}
$unauthorizedPrograms = $this->program->getPairsByList($programIDList);
/* Get product lines by programs.*/
$lines = array(0 => '');
foreach($programs + $unauthorizedPrograms as $id => $program)
{
$lines[$id] = array('') + $this->product->getLinePairs($id);
}
}
else
{
$lines = array('') + $this->product->getLinePairs();
}
$this->view->title = $this->lang->product->batchEdit;
$this->view->position[] = $this->lang->product->batchEdit;
$this->view->lines = $lines;
$this->view->productIDList = $productIDList;
$this->view->products = $products;
$this->view->poUsers = $poUsers;
$this->view->qdUsers = $qdUsers;
$this->view->rdUsers = $rdUsers;
$this->view->programID = $programID;
$this->view->programs = array('' => '') + $programs;
$this->view->unauthorizedPrograms = $unauthorizedPrograms;
unset($this->lang->product->typeList['']);
$this->display();
/* 构造批量编辑页面表单配置数据。*/
$this->productZen->buildBatchEditForm($programID, explode(',', $productIdList));
}
/**
@@ -875,7 +837,7 @@ class product extends control
$this->product->buildProductSearchForm($param, $actionURL);
/* Get product lines. */
list($productLines, $programLines) = $this->getProductLines();
list($productLines, $programLines) = $this->productZen->getProductLines();
$this->view->title = $this->lang->productCommon;
$this->view->position[] = $this->lang->productCommon;
+40 -66
View File
@@ -206,13 +206,13 @@ class productModel extends model
/**
* Get by idList.
*
* @param array $productIDList
* @param array $productIdList
* @access public
* @return array
*/
public function getByIdList($productIDList)
public function getByIdList(array $productIdList): array
{
return $this->dao->select('*')->from(TABLE_PRODUCT)->where('id')->in($productIDList)->fetchAll('id');
return $this->dao->select('*')->from(TABLE_PRODUCT)->where('id')->in($productIdList)->fetchAll('id');
}
/**
@@ -658,14 +658,8 @@ class productModel extends model
$oldProduct = $this->dao->findById($productID)->from(TABLE_PRODUCT)->fetch();
$this->lang->error->unique = $this->lang->error->repeat;
$this->dao->update(TABLE_PRODUCT)->data($product, 'uid,changeProjects,contactListMenu')->autoCheck()
->checkIF(!empty($product->name), 'name', 'unique', "id != {$productID} and `program` = {$product->program} and `deleted` = '0'")
->checkIF(!empty($product->code), 'code', 'unique', "id != {$productID} and `deleted` = '0'")
->checkFlow()
->where('id')->eq($productID)
->exec();
if(dao::isError()) return false;
$result = $this->productTao->doUpdate($product, $productID, (int)$product->program);
if(!$result) return false;
/* Update objectID field of file recode, that upload by editor. */
$this->loadModel('file')->updateObjectID($uid, $productID, 'product');
@@ -681,77 +675,50 @@ class productModel extends model
}
/**
* 批量更新产品信息。
* Batch update products.
*
* @param array $products
* @access public
* @return array
*/
public function batchUpdate()
public function batchUpdate(array $products): array
{
$products = array();
$allChanges = array();
$data = fixer::input('post')->get();
$oldProducts = $this->getByIdList($this->post->productIDList);
$nameList = array();
if(empty($products)) return array();
$extendFields = $this->getFlowExtendFields();
foreach($data->productIDList as $productID)
{
$productName = $data->names[$productID];
/* 初始化变量。*/
$oldProducts = $this->getByIdList(array_keys($products));
$allChanges = array();
$updateViewProducts = array();
$unlinkProducts = array();
$linkProducts = array();
$productID = (int)$productID;
$products[$productID] = new stdClass();
if($this->config->systemMode == 'ALM' and isset($data->programs[$productID])) $products[$productID]->program = (int)$data->programs[$productID];
if($this->config->systemMode == 'ALM' and isset($data->lines[$productID])) $products[$productID]->line = (int)$data->lines[$productID];
$products[$productID]->name = $productName;
$products[$productID]->PO = $data->POs[$productID];
$products[$productID]->QD = $data->QDs[$productID];
$products[$productID]->RD = $data->RDs[$productID];
$products[$productID]->type = $data->types[$productID];
$products[$productID]->status = $data->statuses[$productID];
$products[$productID]->desc = strip_tags($this->post->descs[$productID], $this->config->allowedTags);
$products[$productID]->acl = $data->acls[$productID];
$products[$productID]->id = $productID;
foreach($extendFields as $extendField)
{
$products[$productID]->{$extendField->field} = $this->post->{$extendField->field}[$productID];
if(is_array($products[$productID]->{$extendField->field})) $products[$productID]->{$extendField->field} = join(',', $products[$productID]->{$extendField->field});
$products[$productID]->{$extendField->field} = htmlSpecialString($products[$productID]->{$extendField->field});
}
}
if(dao::isError()) return print(js::error(dao::getError()));
$unlinkProducts = array();
$linkProducts = array();
/* 根据产品ID,循环更新的产品信息. */
$this->loadModel('personnel');
$this->lang->error->unique = $this->lang->error->repeat;
foreach($products as $productID => $product)
{
$oldProduct = $oldProducts[$productID];
if($this->config->systemMode == 'ALM') $programID = !isset($product->program) ? $oldProduct->program : (empty($product->program) ? 0 : $product->program);
if($this->config->systemMode == 'ALM') $programID = (int)zget($product, 'program', $oldProduct->program);
$this->dao->update(TABLE_PRODUCT)
->data($product)
->autoCheck()
->batchCheck($this->config->product->edit->requiredFields , 'notempty')
->checkIF((!empty($product->name) and $this->config->systemMode == 'ALM'), 'name', 'unique', "id != $productID and `program` = $programID and `deleted` = '0'")
->checkFlow()
->where('id')->eq($productID)
->exec();
if(dao::isError()) return print(js::error('product#' . $productID . dao::getError(true)));
$result = $this->productTao->doUpdate($product, $productID, $programID);
if(!$result) return array('result' => 'fail', 'message' => 'product#' . $productID . dao::getError(true));
/* When acl is open, white list set empty. When acl is private,update user view. */
if($product->acl == 'open') $this->loadModel('personnel')->updateWhitelist(array(), 'product', $productID);
if($product->acl != 'open') $this->loadModel('user')->updateUserView($productID, 'product');
if($product->type == 'normal' and $oldProduct->type != 'normal') $unlinkProducts[] = $productID;
if($product->type != 'normal' and $oldProduct->type == 'normal') $linkProducts[] = $productID;
/* When acl is open, white list set empty. */
if($product->acl == 'open') $this->personnel->updateWhitelist(array(), 'product', $productID);
/* 如果产品类型或权限有修改,则标记该产品,用做后面处理。*/
if($oldProduct->acl != $product->acl and $product->acl != 'open') $updateViewProducts[] = $productID;
if($product->type == 'normal' and $oldProduct->type != 'normal') $unlinkProducts[] = $productID;
if($product->type != 'normal' and $oldProduct->type == 'normal') $linkProducts[] = $productID;
$allChanges[$productID] = common::createChanges($oldProduct, $product);
}
if(!empty($unlinkProducts)) $this->loadModel('branch')->unlinkBranch4Project($unlinkProducts);
if(!empty($linkProducts)) $this->loadModel('branch')->linkBranch4Project($linkProducts);
/* 对标记的产品,批量做对应的后续处理。*/
if(!empty($updateViewProducts)) $this->loadModel('user')->updateUserView($updateViewProducts, 'product');
if(!empty($unlinkProducts)) $this->loadModel('branch')->unlinkBranch4Project($unlinkProducts);
if(!empty($linkProducts)) $this->loadModel('branch')->linkBranch4Project($linkProducts);
return $allChanges;
}
@@ -1729,12 +1696,19 @@ class productModel extends model
/*
* Get all lines.
*
* @param array $programIdList
* @access public
* @return array
*/
public function getLines()
public function getLines(array $programIdList = array()): array
{
return $this->dao->select('*')->from(TABLE_MODULE)->where('type')->eq('line')->andWhere('deleted')->eq(0)->orderBy('`order`')->fetchAll();
return $this->dao->select('*')->from(TABLE_MODULE)
->where('type')->eq('line')
->beginIF($programIdList)->andWhere('root')->in($programIdList)->fi()
->andWhere('deleted')->eq(0)
->orderBy('`order`')
->fetchAll();
}
/**
+18 -6
View File
@@ -562,15 +562,27 @@ class productTao extends productModel
}
/**
* 从module表中查询所有产品线。
* Get all product lines from module table.
* TODO move to the MODULE module.
* 执行SQL,更新到对应的产品信息。
* Do update this product.
*
* @param object $product
* @param int $productID
* @param int $programID
* @access protected
* @return array
* @return bool
*/
protected function getProductLinesTODO(): array
protected function doUpdate(object $product, int $productID, int $programID): bool
{
return $this->dao->select('*')->from(TABLE_MODULE)->where('type')->eq('line')->andWhere('deleted')->eq(0)->orderBy('`order` asc')->fetchAll();
if(empty($productID)) return false;
if(count(get_object_vars($product)) == 0) return false;
$this->dao->update(TABLE_PRODUCT)->data($product)->autoCheck()
->checkIF(!empty($product->name), 'name', 'unique', "id != {$productID} and `program` = {$programID} and `deleted` = '0'")
->checkIF(!empty($product->code), 'code', 'unique', "id != {$productID} and `deleted` = '0'")
->checkFlow()
->where('id')->eq($productID)
->exec();
return !dao::isError();
}
}
+40 -15
View File
@@ -3,28 +3,53 @@
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/product.class.php';
zdTable('product')->gen(10);
/**
title=测试 productModel->batchUpdate();
cid=1
pid=1
批量修改product名称 >> name,正常产品1,批量修改产品1
批量修改produc测试负责人 >> QD,test2,test1
批量修改product状态 >> status,normal,closed
*/
$names = array('1' => '批量修改产品1', '2' => '正常产品2', '3' => '正常产品3');
$QDs = array('1' => 'test1', '2' => 'test1', '3' => 'test3');
$status = array('1' => 'normal', '2' => 'normal', '3' => 'closed');
$changeName = array('names' => $names);
$changeQDs = array('QDs' => $QDs);
$changeStatus = array('statuses' => $status);
$product = new productTest('admin');
r($product->batchUpdateTest($changeName, '1')) && p('0:field,old,new') && e('name,正常产品1,批量修改产品1'); // 批量修改product名称
r($product->batchUpdateTest($changeQDs, '2')) && p('0:field,old,new') && e('QD,test2,test1'); // 批量修改produc测试负责人
r($product->batchUpdateTest($changeStatus, '3')) && p('0:field,old,new') && e('status,normal,closed'); // 批量修改product状态
$products = array();
$products[1] = new stdclass();
$products[1]->program = 1;
$products[1]->name = '批量修改产品1';
$products[1]->type = 'branch';
$products[1]->acl = 'private';
$changes = $product->batchUpdateTest($products);
$changes = $changes[1];
r(array_shift($changes)) && p('field,old,new') && e('program,0,1');
r(array_shift($changes)) && p('field,old,new') && e('name,正常产品1,批量修改产品1');
r(array_shift($changes)) && p('field,old,new') && e('type,normal,branch');
r(array_shift($changes)) && p('field,old,new') && e('acl,open,private');
$products = array();
$products[2] = new stdclass();
$products[2]->program = 1;
$products[2]->name = '批量修改产品1';
$products[2]->type = 'branch';
$products[2]->acl = 'private';
$changes = $product->batchUpdateTest($products);
$changes['message'] = str_replace('product#', 'product:', $changes['message']);
r($changes) && p('result,message') && e('fail,product:2『产品名称』已经有『批量修改产品1』这条记录了。\n'); //验证唯一。
r($product->batchUpdateTest(array())) && p() && e('0'); //不传任何数据。
$products = array();
$products[1] = new stdclass();
$products[1]->program = 1;
$products[1]->name = '批量修改产品1';
$products[1]->type = 'normal';
$products[1]->acl = 'open';
$changes = $product->batchUpdateTest($products);
$changes = $changes[1];
r(array_shift($changes)) && p('field,old,new') && e('type,branch,normal');
r(array_shift($changes)) && p('field,old,new') && e('acl,private,open');
+20 -29
View File
@@ -564,40 +564,31 @@ class productTest
/**
* Test batch update products.
*
* @param array $param
* @param int $productID
* @param array $products
* @access public
* @return void
* @return array
*/
public function batchUpdateTest($param, $productID)
public function batchUpdateTest(array $products): array
{
$batchUpdateFields['productIDList'] = array('1' => '1', '2' => '2', '3' => '3');
$batchUpdateFields['programs'] = array('1' => '', '2' => '1', '3' => '2');
$batchUpdateFields['names'] = array('1' => '正常产品1', '2' => '正常产品2', '3' => '正常产品3');
$batchUpdateFields['lines'] = array('1' => '0', '2' => '1', '3' => '2');
$batchUpdateFields['POs'] = array('1' => 'po1', '2' => 'po2', '3' => 'po3');
$batchUpdateFields['QDs'] = array('1' => 'test1', '2' => 'test2', '3' => 'test3');
$batchUpdateFields['RDs'] = array('1' => 'dev1', '2' => 'dev2', '3' => 'dev3');
$batchUpdateFields['types'] = array('1' => 'normal', '2' => 'normal', '3' => 'normal');
$batchUpdateFields['statuses'] = array('1' => 'normal', '2' => 'normal', '3' => 'normal');
$batchUpdateFields['descs'] = array('1' => '&lt;div&gt; &lt;p&gt;&lt;h1&gt;一、禅道项目管理软件是做什么的?&lt;/h1&gt; 禅道由 青岛易软天创网络科技有限公司开发,国产开源项目管理软件。它集产品管理、项目管理、质量管理、文档管理、组织管理和事务管理于一体,是一款专业的研发项目管理软件,完整覆盖了研发项目管理的核心流程。禅道管理思想注重实效,功能完备丰富,操作简洁高效,界面美观大方,搜索功能强大,统计报表丰富多样,软件架构合理,扩展灵活,有完善的API可以调用。禅道,专注研发项目管理 &lt;/p&gt; &lt;p&gt;我是数字符号23@#$%#^$ &lt;/p&gt; &lt;p&gt;我是英文dashcuscbrewg &lt;/p&gt; &lt;/div&gt;', '2' => '&lt;div&gt; &lt;p&gt;&lt;h1&gt;一、禅道项目管理软件是做什么的?&lt;/h1&gt; 禅道由 青岛易软天创网络科技有限公司开发,国产开源项目管理软件。它集产品管理、项目管理、质量管理、文档管理、组织管理和事务管理于一体,是一款专业的研发项目管理软件,完整覆盖了研发项目管理的核心流程。禅道管理思想注重实效,功能完备丰富,操作简洁高效,界面美观大方,搜索功能强大,统计报表丰富多样,软件架构合理,扩展灵活,有完善的API可以调用。禅道,专注研发项目管理 &lt;/p&gt; &lt;p&gt;我是数字符号23@#$%#^$ &lt;/p&gt; &lt;p&gt;我是英文dashcuscbrewg &lt;/p&gt; &lt;/div&gt;', '3' => 'i&lt;div&gt; &lt;p&gt;&lt;h1&gt;一、禅道项目管理软件是做什么的?&lt;/h1&gt; 禅道由 青岛易软天创网络科技有限公司开发,国产开源项目管理软件。它集产品管理、项目管理、质量管理、文档管理、组织管理和事务管理于一体,是一款专业的研发项目管理软件,完整覆盖了研发项目管理的核心流程。禅道管理思想注重实效,功能完备丰富,操作简洁高效,界面美观大方,搜索功能强大,统计报表丰富多样,软件架构合理,扩展灵活,有完善的API可以调用。禅道,专注研发项目管理 &lt;/p&gt; &lt;p&gt;我是数字符号23@#$%#^$ &lt;/p&gt; &lt;p&gt;我是英文dashcuscbrewg &lt;/p&gt; &lt;/div&gt;');
$batchUpdateFields['acls'] = array('1' => 'open', '2' => 'open', '3' => 'open');
return $this->objectModel->batchUpdate($products);
}
foreach($batchUpdateFields as $field => $defaultValue) $_POST[$field] = $defaultValue;
/**
* 测试doUpdate方法
* Test doUpdate method
*
* @param object $product
* @param int $productID
* @param int $programID
* @access public
* @return object|array|null
*/
public function doUpdateTest(object $product, int $productID, int $programID): object|array|false
{
$this->objectModel->doUpdate($product, $productID, $programID);
foreach($param as $key => $value) $_POST[$key] = $value;
$changes = $this->objectModel->batchUpdate();
unset($_POST);
if(dao::isError())
{
return dao::getError();
}
else
{
return $changes[$productID];
}
if(dao::isError()) return dao::getError();
return $this->objectModel->getById($productID);
}
/**
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/product.class.php';
zdTable('product')->gen(10);
/**
title=测试productModel->fetchPairs();
cid=1
pid=1
*/
$product = new productTest('admin');
$productID = 1;
$programID = 0;
$data = new stdclass();
$data->program = 1;
$data->name = '修改产品1';
$data->code = 'newcode1';
$data->type = 'branch';
$data->acl = 'private';
r($product->doUpdateTest($data, $productID, $programID)) && p('program,name,code,type,acl') && e('1,修改产品1,newcode1,branch,private');
$data->name = '修改产品2';
$data->code = 'newcode2';
r($product->doUpdateTest($data, 0, 1)) && p() && e('0');
r($product->doUpdateTest($data, 0, 0)) && p() && e('0');
r($product->doUpdateTest($data, 0, -1)) && p() && e('0');
r($product->doUpdateTest($data, -1, 1)) && p() && e('0');
r($product->doUpdateTest($data, -1, 0)) && p() && e('0');
r($product->doUpdateTest($data, -1, -1)) && p() && e('0');
r($product->doUpdateTest(new stdclass(), 3, 2)) && p('name,code') && e('正常产品3,code3');
$productID = 2;
$programID = 1;
$data = new stdclass();
$data->program = 1;
$data->name = '修改产品1';
$data->code = 'newcode1';
$data->type = 'branch';
$data->acl = 'private';
$result = $product->doUpdateTest($data, $productID, $programID);
r(array_shift($result['name'])) && p() && e('『产品名称』已经有『修改产品1』这条记录了。如果您确定该记录已删除,请到后台-系统-数据-回收站还原。');
r(array_shift($result['code'])) && p() && e('『产品代号』已经有『newcode1』这条记录了。如果您确定该记录已删除,请到后台-系统-数据-回收站还原。');
+5 -5
View File
@@ -73,19 +73,19 @@
</tr>
</thead>
<tbody>
<?php foreach($productIDList as $productID):?>
<?php foreach(array_keys($products) as $productID):?>
<?php
if(!empty($this->config->moreLinks["PO"])) $this->config->moreLinks["POs[$productID]"] = $this->config->moreLinks["PO"];
if(!empty($this->config->moreLinks["QD"])) $this->config->moreLinks["QDs[$productID]"] = $this->config->moreLinks["QD"];
if(!empty($this->config->moreLinks["RD"])) $this->config->moreLinks["RDs[$productID]"] = $this->config->moreLinks["RD"];
?>
<tr>
<td><?php echo sprintf('%03d', $productID) . html::hidden("productIDList[$productID]", $productID);?></td>
<td><?php echo sprintf('%03d', $productID) . html::hidden("productIdList[$productID]", $productID);?></td>
<?php if($this->config->systemMode == 'ALM'):?>
<?php if(isset($unauthorizedPrograms[$products[$productID]->program])):?>
<td class='text-left<?php echo zget($visibleFields, 'program', ' hidden')?>' style='overflow:visible'><?php echo html::select("programs[$productID]", $unauthorizedPrograms, $products[$productID]->program, "class='form-control' disabled");?></td>
<?php if(isset($unauthPrograms[$products[$productID]->program])):?>
<td class='text-left<?php echo zget($visibleFields, 'program', ' hidden')?>' style='overflow:visible'><?php echo html::select("programs[$productID]", $unauthPrograms, $products[$productID]->program, "class='form-control' disabled");?></td>
<?php else:?>
<td class='text-left<?php echo zget($visibleFields, 'program', ' hidden')?>' style='overflow:visible'><?php echo html::select("programs[$productID]", $programs, $products[$productID]->program, "class='form-control picker-select' onchange='loadProductLines(this.value, $productID)'");?></td>
<td class='text-left<?php echo zget($visibleFields, 'program', ' hidden')?>' style='overflow:visible'><?php echo html::select("programs[$productID]", $authPrograms, $products[$productID]->program, "class='form-control picker-select' onchange='loadProductLines(this.value, $productID)'");?></td>
<?php endif;?>
<?php endif;?>
<td title='<?php echo $products[$productID]->name?>'><?php echo html::input("names[$productID]", $products[$productID]->name, "class='form-control'");?></td>
+183 -23
View File
@@ -201,28 +201,29 @@ class productZen extends product
*/
private function getFormFields4Create(int $programID = 0, array $fields = array()): array
{
if(empty($fields)) $fields = $this->config->product->form->create;
if(empty($fields)) $fields = $this->appendFlowFields($this->config->product->form->create);
/* 准备数据。*/
$this->loadModel('user');
$poUsers = $this->user->getPairs('nodeleted|pofirst|noclosed');
$qdUsers = $this->user->getPairs('nodeleted|qdfirst|noclosed');
$rdUsers = $this->user->getPairs('nodeleted|devfirst|noclosed');
$users = $this->user->getPairs('nodeleted|noclosed');
/* 追加字段的name、title属性,展开user数据。 */
foreach($fields as $field => $attr)
{
if(isset($attr['options']) and $attr['options'] == 'users') $fields[$field]['options'] = $users;
$fields[$field]['name'] = $field;
$fields[$field]['title'] = zget($this->lang->product, $field);
if(!isset($fields[$field]['name'])) $fields[$field]['name'] = $field;
if(!isset($fields[$field]['title'])) $fields[$field]['title'] = zget($this->lang->product, $field);
}
$fields['program']['options'] = array('') + $this->loadModel('program')->getTopPairs('', 'noclosed');
$fields['PO']['options'] = $poUsers;
$fields['QD']['options'] = $qdUsers;
$fields['RD']['options'] = $rdUsers;
if($programID) $fields['line']['options'] = array('') + $this->product->getLinePairs($programID);
if(empty($programID) or $this->config->systemMode != 'ALM') unset($fields['line'], $fields['lineName']);
/* 设置下拉菜单内容。 */
$fields['PO']['options'] = $poUsers;
$fields['QD']['options'] = $qdUsers;
$fields['RD']['options'] = $rdUsers;
if(isset($fields['program'])) $fields['program']['options'] = array('') + $this->loadModel('program')->getTopPairs('', 'noclosed');
if($programID and isset($fields['line'])) $fields['line']['options'] = array('') + $this->product->getLinePairs($programID);
return $fields;
}
@@ -239,12 +240,13 @@ class productZen extends product
{
/* Init fields. */
$programID = (int)$product->program;
$fields = $this->getFormFields4Create($programID, $this->config->product->form->edit);
$fields = $this->getFormFields4Create($programID, $this->appendFlowFields($this->config->product->form->edit));
$fields['changeProjects'] = array('type' => 'string', 'control' => 'hidden', 'required' => false, 'default' => '');
/* Check program priv, and append to program list that is not exist product's program. */
$hasPrivPrograms = $this->app->user->view->programs;
if($programID and strpos(",{$hasPrivPrograms},", ",{$programID},") === false) $fields['program']['control'] = 'hidden';
if(!isset($fields['program']['options'][$programID]) and $programID)
if(isset($fields['program']) and !isset($fields['program']['options'][$programID]) and $programID)
{
$program = $this->program->getByID($programID);
$fields['program']['options'] += array($programID => $program->name);
@@ -255,7 +257,28 @@ class productZen extends product
{
if(isset($product->{$field})) $fields[$field]['default'] = $product->{$field};
}
$fields['changeProjects'] = array('type' => 'string', 'control' => 'hidden', 'required' => false, 'default' => '');
return $fields;
}
/**
* 获取批量编辑产品页面的表单配置。
* Get form fields config for batch edit page.
*
* @access private
* @return array
*/
private function getFormFields4BatchEdit(): array
{
/* Init fields. */
$fields = $this->getFormFields4Create(0, $this->appendFlowFields($this->config->product->form->batchEdit, 'batch'));
/* Remove not show fields. */
$showFields = explode(',', $this->config->product->custom->batchEditFields);
foreach($fields as $field => $attr)
{
if(!in_array($field, $showFields) and !$attr['required']) unset($fields[$field]);
}
return $fields;
}
@@ -263,23 +286,45 @@ class productZen extends product
/**
* Get product lines and product lines of program.
*
* @param array $programIdList
* @param string $params hasempty
* @access protected
* @return array
*/
protected function getProductLines(): array
protected function getProductLines(array $programIdList = array(), string $params = ''): array
{
/* Get all product lines. */
$productLines = $this->product->getProductLinesTODO();
$productLines = $this->product->getLines($programIdList);
/* Collect product lines of program lines. */
$programLines = array();
foreach($productLines as $productLine)
{
if(!isset($programLines[$productLine->root])) $programLines[$productLine->root] = array();
$programLines[$productLine->root][$productLine->id] = $productLine->name;
}
$initArray = strpos($params, 'hasempty') !== false ? array('') : array();
$linePairs = $initArray;
foreach($programIdList as $programID) $linePairs[$programID] = $initArray;
foreach($productLines as $line) $linePairs[$programID][$line->id] = $line->name;
return array($productLines, $programLines);
return array($productLines, $linePairs);
}
/**
* 根据产品和已授权项目集,获取关联产品的未授权项目集。
* Get unauthorized programs by products and authorized programs.
*
* @param array $products
* @param array $authPrograms
* @access private
* @return array
*/
private function getUnauthProductPrograms(array $products, array $authPrograms): array
{
$unauthPrograms = array();
$programIdList = array();
foreach($products as $product)
{
if($product->program and !isset($authPrograms[$product->program])) $programIdList[$product->program] = $product->program;
}
if($programIdList) $unauthPrograms = $this->program->getPairsByList($programIdList);
return $unauthPrograms;
}
/**
@@ -378,6 +423,45 @@ class productZen extends product
$this->display();
}
/**
* 构建批量编辑产品页面数据。
* Build form for batch edit page
*
* @param int $programID
* @param array $productIdList
* @access protected
* @return void
*/
protected function buildBatchEditForm(int $programID, array $productIdList)
{
$products = $this->product->getByIdList($productIdList);
/* 获取项目集和产品线,并将项目集分成授权和非授权。以便view的下拉菜单使用对应的数据。 */
$authPrograms = array();
$unauthPrograms = array();
$lines = array();
if($this->config->systemMode == 'ALM')
{
$authPrograms = $this->loadModel('program')->getTopPairs();
$unauthPrograms = $this->getUnauthProductPrograms($products, $authPrograms);
/* Get product lines by programs.*/
$programIdList = array_merge(array_keys($authPrograms), array_keys($unauthPrograms));
list(, $lines) = $this->getProductLines($programIdList);
}
/* 给view层赋值。 */
$this->view->title = $this->lang->product->batchEdit;
$this->view->lines = $lines;
$this->view->products = $products;
$this->view->programID = $programID;
$this->view->authPrograms = array('' => '') + $authPrograms;
$this->view->unauthPrograms = $unauthPrograms;
unset($this->lang->product->typeList['']);
$this->display();
}
/**
* 为view层设置要用数据。
* Set view variables and display for project page.
@@ -448,13 +532,56 @@ class productZen extends product
*/
protected function prepareEditExtras(form $data, string $acl, string $uid = ''): object
{
$product = fixer::input('post')->setIF($acl == 'open', 'whitelist', '')
$product = $data->setIF($acl == 'open', 'whitelist', '')
->stripTags($this->config->product->editor->edit['id'], $this->config->allowedTags)
->get();
return $this->loadModel('file')->processImgURL($product, $this->config->product->editor->edit['id'], $uid);
}
/**
* 预处理批量编辑产品数据,将按字段为分组数据重组为产品分组的数据。
* Prepare batch edit extras.
*
* @param form $data
* @access protected
* @return array
*/
protected function prepareBatchEditExtras(form $data): array
{
$formConfig = $data->rawconfig;
$editForm = $this->config->product->form->edit;
$data = $data->get();
$products = array();
/* 将按字段为分组数据重组为产品分组的数据。*/
foreach($data->name as $productID => $productName)
{
$productID = (int)$productID;
/* 根据表单配置构造产品数据。*/
$product = new stdClass();
$product->id = $productID;
foreach($formConfig as $field => $attr)
{
/* 获取对应的字段数据。*/
$product->{$field} = zget($data->{$field}, $productID, $attr['default']);
/* 根据配置规则,格式化字段数据。 */
if(isset($editForm[$field]) and $editForm[$field]['type'] == 'int') $product->{$field} = (int)$product->{$field};
if(!empty($attr['filter']) and $attr['filter'] == 'trim') $product->{$field} = trim($product->{$field});
if(is_array($product->$field)) $product->{$field} = implode(',', $product->{$field});
/* 检查必填项。 */
if(!empty($attr['required']) and validater::checkEmpty($product->{$field})) dao::$errors[] = 'product #' . $productID . sprintf($this->lang->error->notempty, zget($attr, 'title', zget($this->lang->product, $field)));
}
$products[$productID] = $product;
}
return $products;
}
/**
* 成功插入产品数据后,其他的额外操作。
* Process after create product.
@@ -596,4 +723,37 @@ class productZen extends product
return $data;
}
/**
* 追加工作流配置字段。
* Append flow fields.
*
* @param array $fields
* @param string $type single|batch
* @access protected
* @return array
*/
protected function appendFlowFields(array $fields, string $type = 'single'): array
{
$extendFields = $this->product->getFlowExtendFields();
if(empty($extendFields)) return $fields;
/* 构造表单属性,并追加到表单配置中。 */
foreach($extendFields as $extendField)
{
$control = $extendField->control;
if($control == 'richtext') $control = 'editor';
if($control == 'input') $control = 'text';
$field = $extendField;
$fields[$field] = array();
$fields[$field]['type'] = $type == 'single' ? 'string' : 'array';
$fields[$field]['control'] = $control;
$fields[$field]['title'] = $extendField->name;
$fields[$field]['default'] = $extendField->default;
$fields[$field]['options'] = $extendField->options;
}
return $fields;
}
}
+10 -10
View File
@@ -136,13 +136,13 @@ fields:
postfix: ""
loop: 0
format: ""
- field: finishedDate
note: "完成时间"
range: ""
prefix: ""
postfix: ""
loop: 0
format: ""
#- field: finishedDate
# note: "完成时间"
# range: ""
# prefix: ""
# postfix: ""
# loop: 0
# format: ""
- field: closedBy
note: "由谁关闭"
range: ""
@@ -150,6 +150,6 @@ fields:
postfix: ""
loop: 0
format: ""
- field: closedDate
note: "关闭时间"
range: ""
#- field: closedDate
# note: "关闭时间"
# range: ""