This commit is contained in:
lanzongjun
2023-05-04 15:53:34 +08:00
37 changed files with 1543 additions and 443 deletions
+41 -99
View File
@@ -27,10 +27,10 @@ class block extends control
/**
* Create a block under a dashboard.
*
* @param string $dashboard
*
* @param string $dashboard
* @param string $module
* @param string $code
* @param string $code
* @access public
* @return void
*/
@@ -61,11 +61,11 @@ class block extends control
}
/**
* Update a block.
*
* @param string $dashboard
* @param string $module
* @param string $code
* Update a block.
*
* @param string $dashboard
* @param string $module
* @param string $code
* @access public
* @return void
*/
@@ -97,81 +97,39 @@ class block extends control
$this->display();
}
public function set($id, $type, $module = '')
{
$block = $this->block->getByID($id);
if($block and empty($type)) $type = $block->code;
if(isset($block->params->num) and !isset($block->params->count))
{
$block->params->count = $block->params->num;
unset($block->params->num);
}
if(isset($this->lang->block->moduleList[$module]))
{
$params = $this->block->getParams($type, $module);
$this->view->params = json_decode($params, true);
}
elseif($type == 'assigntome')
{
$params = $this->block->getParams('assignedToMe');
$this->view->params = json_decode($params, true);
}
$this->view->source = $module;
$this->view->type = $type;
$this->view->id = $id;
$this->view->block = ($block) ? $block : array();
$this->display();
}
/**
* Delete block
* Delete or hidd block by blockid.
*
* @param int $id
* @param string $sys
* @param string $type
* @access public
* @return void
*/
public function delete($id, $module = 'my', $type = 'delete')
public function delete($blockID, $type = 'delete')
{
if($type == 'hidden')
{
$this->dao->update(TABLE_BLOCK)->set('hidden')->eq(1)->where('`id`')->eq($id)->andWhere('account')->eq($this->app->user->account)->andWhere('module')->eq($module)->exec();
}
else
{
$this->dao->delete()->from(TABLE_BLOCK)->where('`id`')->eq($id)->andWhere('account')->eq($this->app->user->account)->andWhere('module')->eq($module)->exec();
}
$blockID = (int)$blockID;
if($type == 'delete') $this->block->deleteBlock($blockID);
if($type == 'hidden') $this->block->hidden($blockID);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->loadModel('score')->create('block', 'set');
return $this->send(array('result' => 'success'));
}
/**
* Sort block.
* Sort dashboard blocks.
*
* @param string $oldOrder
* @param string $newOrder
* @param string $module
* @param string $orders
* @access public
* @return void
*/
public function sort($orders, $module = 'my')
public function sort($orders)
{
$orders = explode(',', $orders);
$blockList = $this->block->getMyDashboard($module);
foreach ($orders as $order => $blockID)
{
$block = $blockList[$blockID];
if(!isset($block)) continue;
$block->order = $order;
$this->dao->replace(TABLE_BLOCK)->data($block)->exec();
}
$orders = explode(',', $orders);
foreach($orders as $order => $blockID) $this->block->setOrder($blockID, $order);
if(dao::isError()) return $this->send(array('result' => 'fail'));
$this->loadModel('score')->create('block', 'set');
return $this->send(array('result' => 'success'));
}
@@ -182,26 +140,22 @@ class block extends control
* @access public
* @return void
*/
public function resize($id, $type, $data)
public function resize($blockID, $type, $data)
{
$block = $this->block->getByID($id);
if($block)
{
$field = '';
if($type == 'vertical') $field = 'height';
if($type == 'horizontal') $field = 'grid';
if(empty($field)) return $this->send(array('result' => 'fail', 'code' => 400));
$block = $this->block->getByID($blockID);
if(!$block) return $this->send(array('result' => 'fail', 'code' => 404));
$block->$field = $data;
$block->params = helper::jsonEncode($block->params);
$this->dao->replace(TABLE_BLOCK)->data($block)->exec();
if(dao::isError()) return $this->send(array('result' => 'fail', 'code' => 500));
return $this->send(array('result' => 'success'));
}
else
{
return $this->send(array('result' => 'fail', 'code' => 404));
}
$field = '';
if($type == 'vertical') $field = 'height';
if($type == 'horizontal') $field = 'grid';
if(empty($field)) return $this->send(array('result' => 'fail', 'code' => 400));
$block->$field = $data;
$block->params = helper::jsonEncode($block->params);
$this->block->update($block);
if(dao::isError()) return $this->send(array('result' => 'fail', 'code' => 500));
return $this->send(array('result' => 'success'));
}
/**
@@ -1862,7 +1816,7 @@ class block extends control
/* load pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager(0, 3, 1);
$this->view->projects = $this->loadModel('project')->getInfoList('all', 'id_desc', $pager, 1);
$this->view->projects = $this->loadModel('project')->getList('all', 'id_desc', 1, $pager);
}
/**
@@ -2155,30 +2109,18 @@ class block extends control
}
/**
* Ajax reset.
* Reset dashboard blocks.
*
* @param string $module
* @param string $confirm
* @param string $dashboard
* @access public
* @return void
*/
public function ajaxReset($module, $confirm = 'no')
public function reset($dashboard)
{
if($confirm != 'yes') return print(js::confirm($this->lang->block->confirmReset, inlink('ajaxReset', "module=$module&confirm=yes")));
$this->block->reset($dashboard);
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$this->dao->delete()->from(TABLE_BLOCK)
->where('module')->eq($module)
->andWhere('vision')->eq($this->config->vision)
->andWhere('account')->eq($this->app->user->account)
->exec();
$this->dao->delete()->from(TABLE_CONFIG)
->where('module')->eq($module)
->andWhere('vision')->eq($this->config->vision)
->andWhere('owner')->eq($this->app->user->account)
->andWhere('`key`')->eq('blockInited')
->exec();
return print(js::reload('parent'));
return $this->send(array('result' => 'success'));
}
/**
+4
View File
@@ -1,3 +1,7 @@
.welcome-block {background-image: linear-gradient( 90deg, #EDF7FE 0%, #FFF 20%);}
.border-right {position: relative;}
.border-right::after {content: ''; position: absolute; right: 0px; width: 1px; height: 40px; background: #EEE; top: 50%; margin-top: -20px;}
.avatar-border-one {width: 4.2rem; height: 4.2rem; background: #f4f5f3; position: relative;}
.avatar-border-two {width: 3.6rem; height: 3.6rem; background: #F5EBDE; position: absolute;}
.welcome-avatar {border: 2px solid #FBC962; position: absolute;}
.tile-amount {font-size: 32px; font-weight: 700; line-height: 56px; }
+24
View File
@@ -6,3 +6,27 @@ function getForm(event)
const url = $.createLink('block', 'create', 'dashboard='+ dashboard +'&module=' + module + '&code=' + (code ? code : ''));
loadPage(url, '#codesRow, #paramsRow');
}
function onParamsTypeChange(event)
{
const lang = config.clientLang;
const $code = document.querySelector('#code');
if($('#module').val() == 'scrumtest' && $('#paramstype').val() != 'all')
{
$('#title').val($code.options[$code.selectedIndex].text);
}
else
{
if(lang.indexOf('zh') >= 0)
{
const $paramstype = document.querySelector('#paramstype');
const blockTitle = $paramstype.options[$paramstype.selectedIndex].text + of + $code.options[$code.selectedIndex].text;
$('#title').val(blockTitle);
}
else
{
/* TODO */
}
}
}
+75
View File
@@ -295,6 +295,81 @@ class blockModel extends model
return (int)$formData->id;
}
/**
* Reset dashboard blocks.
*
* @param string $dashboard
* @access public
* @return bool
*/
public function reset(string $dashboard): bool
{
$this->dao->delete()->from(TABLE_BLOCK)
->where('dashboard')->eq($dashboard)
->andWhere('vision')->eq($this->config->vision)
->andWhere('account')->eq($this->app->user->account)
->exec();
$this->dao->delete()->from(TABLE_CONFIG)
->where('module')->eq($dashboard)
->andWhere('vision')->eq($this->config->vision)
->andWhere('owner')->eq($this->app->user->account)
->andWhere('`key`')->eq('blockInited')
->exec();
return true;
}
/**
* Hidden a block.
*
* @param int $blockID
* @access public
* @return bool
*/
public function hidden(int $blockID): bool
{
$this->dao->update(TABLE_BLOCK)->set('hidden')->eq(1)
->where('id')->eq($blockID)
->andWhere('account')->eq($this->app->user->account)
->andWhere('vision')->eq($this->config->vision)
->exec();
return true;
}
/**
* Delete a block.
*
* @param int $blockID
* @access public
* @return bool
*/
public function deleteBlock(int $blockID = 0): bool
{
$this->dao->delete()->from(TABLE_BLOCK)
->where('id')->eq($blockID)
->andWhere('account')->eq($this->app->user->account)
->andWhere('vision')->eq($this->config->vision)
->exec();
return true;
}
/**
* Set block order.
*
* @param int $blockID
* @param int $order
* @access public
* @return bool
*/
public function setOrder(int $blockID, int $order): bool
{
$this->dao->update(TABLE_BLOCK)->set('order')->eq($order)->where('id')->eq($blockID)->exec();
return true;
}
/**
* Save a block.
*
+6 -3
View File
@@ -13,8 +13,9 @@ namespace zin;
set::title($title);
jsVar('dashboard', $dashboard);
jsVar('of', $lang->block->of);
$paramsRows = array();
$paramsRows = array();
$showModules = ($dashboard == 'my' and $modules);
$showCodes = (($showModules and $module and $codes) or $dashboard != 'my');
@@ -28,6 +29,7 @@ foreach($params as $key => $row)
set::value(zget($row, 'default', '')),
set::control(array
(
'id' => "params$key",
'type' => $row['control'],
'items' => isset($row['options']) ? $row['options'] : null
))
@@ -38,10 +40,11 @@ form
(
on::change('#module', 'getForm'),
on::change('#code', 'getForm'),
on::change('#paramstype', 'onParamsTypeChange'),
formGroup
(
set::class($showModules ? '' : 'hidden'),
set::value($showModules ? $block : $dashboard),
set::value($showModules ? $code : $dashboard),
set::label($lang->block->lblModule),
set::name('module'),
set::control($showModules ? array
@@ -76,8 +79,8 @@ form
(
set::label($lang->block->name),
set::name('title'),
set::value(zget($codes, $code)),
set::class('form-row'),
set::value(zget($modules, $module, '') . zget($codes, $code, '')),
set::control('input')
),
$paramsRows,
+4 -4
View File
@@ -30,9 +30,9 @@ foreach($longBlocks as $index => $block)
([
['text' => $lang->block->refresh, 'url' => ''],
['text' => $lang->edit, 'url' => $this->createLink("block", "edit", "blockID=$block->id"), 'data-toggle' => 'modal'],
['text' => $lang->block->hidden, 'url' => ''],
['text' => $lang->block->hidden, 'url' => $this->createLink("block", "delete", "blockID=$block->id&type=hidden")],
['text' => $lang->block->createBlock, 'url' => $this->createLink("block", "create", "dashboard=$dashboard"), 'data-toggle' => 'modal'],
['text' => $lang->block->reset, 'url' => ''],
['text' => $lang->block->reset, 'url' => $this->createLink("block", "reset", "dashboard=$dashboard")],
]),
)
)
@@ -78,9 +78,9 @@ foreach($shortBlocks as $index => $block)
([
['text' => $lang->block->refresh, 'url' => ''],
['text' => $lang->edit, 'url' => $this->createLink("block", "edit", "blockID=$block->id"), 'data-toggle' => 'modal'],
['text' => $lang->block->hidden, 'url' => ''],
['text' => $lang->block->hidden, 'url' => $this->createLink("block", "delete", "blockID=$block->id&type=hidden")],
['text' => $lang->block->createBlock, 'url' => $this->createLink("block", "create", "dashboard=$dashboard"), 'data-toggle' => 'modal'],
['text' => $lang->block->reset, 'url' => ''],
['text' => $lang->block->reset, 'url' => $this->createLink("block", "reset", "dashboard=$dashboard")],
]),
)
)
+146
View File
@@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
/**
* The welcome view file of block module of ZenTaoPMS.
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author LiuRuoGu <liuruogu@easycorp.ltd>
* @package block
* @link https://www.zentao.net
*/
namespace zin;
panel
(
set('class', 'welcome-block'),
div
(
setClass('flex'),
col
(
setStyle(['width' => '20%', 'height' => '176px']),
set('class', 'border-right p-3'),
set('align', 'center'),
center
(
set('class', 'font-bold'),
sprintf($lang->block->welcomeList[$welcomeType], $app->user->realname)
),
center
(
set('class', 'rounded-full avatar-border-one m-5'),
center
(
set('class', 'rounded-full avatar-border-two'),
userAvatar
(
set('size', 'lg'),
set('class', 'welcome-avatar'),
set('user', $this->app->user)
)
)
)
),
cell
(
set('width', '45%'),
set('class', 'border-right p-3'),
div
(
set('class', 'font-bold'),
'待我评审:'
),
div
(
setClass('flex items-center justify-around pt-6'),
center
(
col
(
set('justify', 'center'),
set('class', 'text-center'),
span
(
set('class', 'tile-amount text-primary'),
81
),
span('研发需求数')
)
),
center
(
col
(
set('justify', 'center'),
set('class', 'text-center'),
span
(
set('class', 'tile-amount text-primary'),
81
),
span('研发需求数')
)
),
center
(
col
(
set('justify', 'center'),
set('class', 'text-center'),
span
(
set('class', 'tile-amount text-primary'),
81
),
span('研发需求数')
)
)
)
),
cell
(
set::width('35%'),
set('class', 'p-3'),
div
(
set('class', 'font-bold'),
'待我评审:'
),
div
(
setClass('flex items-center justify-around pt-6'),
center
(
col
(
set('justify', 'center'),
set('class', 'text-center'),
span
(
set('class', 'tile-amount text-primary'),
81
),
span('研发需求数')
)
),
center
(
col
(
set('justify', 'center'),
set('class', 'text-center'),
span
(
set('class', 'tile-amount text-primary'),
81
),
span('研发需求数')
)
)
)
)
)
);
render();
+4 -4
View File
@@ -2,8 +2,8 @@
class blockZen extends block
{
/**
* Get module options when adding or editing blocks.
* 添加或编辑区块时获取可使用的模块选项
* Get module options when adding or editing blocks.
*
* @param string $dashboard
* @access protected
@@ -51,8 +51,8 @@ class blockZen extends block
}
/**
* Get block options when adding or editing blocks.
* 添加或编辑区块时获取可使用的区块选项
* Get block options when adding or editing blocks.
*
* @param string $dashboard
* @param string $module
@@ -112,12 +112,12 @@ class blockZen extends block
}
/**
* Get other form items when adding or editing blocks
* 添加或编辑区块时获取其他表单项
* Get other form items when adding or editing blocks
*
* @param string $dashboard
* @param string $module
* @param string $block
* @param string $code
* @access protected
* @return array
*/
+3 -2
View File
@@ -37,8 +37,9 @@ $config->project->form->edit['acl'] = array('type' => 'string', 'required
$config->project->form->edit['whitelist'] = array('type' => 'array', 'required' => false, 'default' => '', 'filter' => 'join');
$config->project->form->edit['auth'] = array('type' => 'array', 'required' => false, 'default' => '');
$config->project->form->edit['model'] = array('type' => 'string', 'required' => false, 'default' => '');
$config->project->form->edit['plans'] = array('type' => 'array', 'required' => false, 'default' => '');
$config->project->form->edit['products'] = array('type' => 'array', 'required' => false, 'default' => '');
$config->project->form->edit['plans'] = array('type' => 'array', 'required' => false, 'default' => '');
$config->project->form->edit['products'] = array('type' => 'array', 'required' => false, 'default' => '');
$config->project->form->edit['product'] = array('type' => 'array', 'required' => false, 'default' => '');
$config->project->form->start['realBegan'] = array('type' => 'date', 'required' => true, 'filter' => 'trim');
+9 -11
View File
@@ -73,8 +73,7 @@ class project extends control
if(isset($fields['hasProduct'])) $fields['hasProduct'] = $projectLang->type;
$involved = $this->cookie->involved ? $this->cookie->involved : 0;
$projects = $this->project->getInfoList($status, $orderBy, '', $involved);
$projects = $this->project->getList($status, $orderBy, null);
$users = $this->loadModel('user')->getPairs('noletter');
$this->loadModel('product');
@@ -316,7 +315,7 @@ class project extends control
if($project->model == 'kanban' and $this->config->vision != 'lite')
{
/* Load pager and get kanban list. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$kanbanList = $this->loadModel('execution')->getList($projectID, 'all', $browseType, 0, 0, 0, $pager);
@@ -370,7 +369,7 @@ class project extends control
$browseType = strtolower($browseType);
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$queryID = ($browseType == 'bysearch') ? (int)$param : 0;
@@ -519,9 +518,8 @@ class project extends control
$this->loadModel('program');
$this->loadModel('execution');
$projectID = (int)$projectID;
$project = $this->project->getByID($projectID);
$programID = $project->parent;
$projectID = (int)$projectID;
$project = $this->project->getByID($projectID);
$this->project->setMenu($projectID);
if($project->model == 'kanban')
@@ -736,7 +734,7 @@ class project extends control
}
/* Load pager. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
$pager = new pager(0, 30, 1);
/* Check exist extend fields. */
@@ -865,8 +863,8 @@ class project extends control
$orderBy = $direction == 'next' ? 'date_desc' : 'date_asc';
/* Set the pager. */
$this->app->loadClass('pager', $static = true);
$pager = new pager($recTotal, $recPerPage = 50, $pageID = 1);
$this->app->loadClass('pager', true);
$pager = new pager($recTotal, 50, 1);
/* Set the user and type. */
$account = 'all';
@@ -934,7 +932,7 @@ class project extends control
if(!empty($project->model) and $project->model == 'kanban' and !(defined('RUN_MODE') and RUN_MODE == 'api')) return print(js::locate($this->createLink('project', 'index', "projectID=$projectID")));
/* Load pager and get tasks. */
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
$pager = new pager($recTotal, $recPerPage, $pageID);
$allExecution = $this->execution->getStatData($projectID, 'all');
+13 -25
View File
@@ -47,10 +47,8 @@ class projectModel extends model
{
if(defined('TUTORIAL')) return true;
echo(js::alert($this->lang->project->accessDenied));
$this->session->set('project', '');
return print(js::locate(helper::createLink('project', 'index')));
return print(js::alert($this->lang->project->accessDenied) . js::locate(helper::createLink('project', 'index')));
}
/**
@@ -148,8 +146,8 @@ class projectModel extends model
*/
public function getMultiLinkedProducts($projectID)
{
$linkedProducts = $this->dao->select('product')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($projectID)->fetchPairs();
$multiLinkedProducts = $this->dao->select('t3.id,t3.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
$linkedProducts = $this->dao->select('product')->from(TABLE_PROJECTPRODUCT)->where('project')->eq($projectID)->fetchPairs();
return $this->dao->select('t3.id,t3.name')->from(TABLE_PROJECTPRODUCT)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->leftJoin(TABLE_PRODUCT)->alias('t3')->on('t1.product = t3.id')
->where('t1.product')->in($linkedProducts)
@@ -158,8 +156,6 @@ class projectModel extends model
->andWhere('t2.deleted')->eq('0')
->andWhere('t3.deleted')->eq('0')
->fetchPairs('id', 'name');
return $multiLinkedProducts;
}
/*
@@ -235,7 +231,8 @@ class projectModel extends model
}
/**
* Get project info.
* 根据状态和和我参与的查询项目列表。
* Get project list by status and with my participation.
*
* @param string $status
* @param string $orderBy
@@ -244,19 +241,14 @@ class projectModel extends model
* @access public
* @return array
*/
public function getInfoList($status = 'undone', $orderBy = 'order_desc', $pager = null, $involved = 0)
public function getList($status = 'undone', $orderBy = 'order_desc', $pager = null, $involved = 0)
{
/* Init vars. */
$projects = $this->loadModel('program')->getProjectList(0, $status, 0, $orderBy, $pager, 0, $involved);
$projects = $this->projectTao->fetchProjectList($status, $orderBy, $involved, $pager);
if(empty($projects)) return array();
$projectIdList = array_keys($projects);
$teams = $this->dao->select('t1.root, count(t1.id) as count')->from(TABLE_TEAM)->alias('t1')
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
->where('t1.root')->in($projectIdList)
->andWhere('t2.deleted')->eq(0)
->groupBy('t1.root')
->fetchAll('root');
$teams = $this->projectTao->fetchMemberCountByIdList($projectIdList);
$estimates = $this->dao->select("t2.project as project, sum(estimate) as estimate")->from(TABLE_TASK)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.execution = t2.id')
@@ -267,7 +259,7 @@ class projectModel extends model
->groupBy('t2.project')
->fetchAll('project');
$this->app->loadClass('pager', $static = true);
$this->app->loadClass('pager', true);
foreach($projects as $projectID => $project)
{
$orderBy = $project->model == 'waterfall' ? 'id_asc' : 'id_desc';
@@ -868,12 +860,11 @@ class projectModel extends model
->orderBy('grade desc')
->fetch();
$projects = $this->dao->select('id')->from(TABLE_PROJECT)
return $this->dao->select('id')->from(TABLE_PROJECT)
->where('type')->eq('project')
->andWhere('deleted')->eq(0)
->andWhere('path')->like("{$parentProgram->path}%")
->fetchPairs('id');
return $projects;
}
/**
@@ -1398,7 +1389,7 @@ class projectModel extends model
$this->lang->error->unique = $this->lang->error->repeat;
if(!empty($executionsCount) and $oldProject->multiple) $this->checkDatesValidByProject($projectID ,$project);
$this->projectTao->doUpdate($projectID, $project);
$this->projectTao->doUpdate($projectID, $project, $oldProject);
if(dao::isError()) return false;
if(!$oldProject->hasProduct and ($oldProject->name != $project->name or $oldProject->parent != $project->parent or $oldProject->acl != $project->acl)) $this->updateShadowProduct($project);
@@ -1548,7 +1539,7 @@ class projectModel extends model
$parentID = !isset($project->parent) ? $oldProject->parent : $project->parent;
$this->dao->update(TABLE_PROJECT)->data($project)
->autoCheck($skipFields = 'begin,end')
->autoCheck('begin,end')
->batchCheck($this->config->project->edit->requiredFields, 'notempty')
->checkIF($project->begin != '', 'begin', 'date')
->checkIF($project->end != '', 'end', 'date')
@@ -3065,10 +3056,7 @@ class projectModel extends model
{
foreach($plans as $planList)
{
foreach($planList as $planIDList)
{
foreach($planIDList as $planID) $newPlans[$planID] = $planID;
}
foreach($planList as $planID) $newPlans[$planID] = $planID;
}
}
if(empty($newPlans)) return;
+62 -2
View File
@@ -101,13 +101,14 @@ class projectTao extends projectModel
*
* @param int $projectID
* @param object $project
* @param object $oldProject
* @access protected
* @return bool
*/
protected function doUpdate(int $projectID ,object $project): bool
protected function doUpdate(int $projectID ,object $project, object $oldProject): bool
{
$this->dao->update(TABLE_PROJECT)->data($project)
->autoCheck($skipFields = 'begin,end')
->autoCheck('begin,end')
->batchcheck($requiredFields, 'notempty')
->checkIF($project->begin != '', 'begin', 'date')
->checkIF($project->end != '', 'end', 'date')
@@ -529,4 +530,63 @@ class projectTao extends projectModel
$oldExecutionProducts = $this->dao->select('project,product')->from(TABLE_PROJECTPRODUCT)->where('project')->in($executionIDs)->fetchGroup('project', 'product');
return !dao::isError();
}
/**
* 根据状态和和我参与的查询项目列表。
* Get project list by status and with my participation.
*
* @param string $status
* @param string $orderBy
* @param int $involved
* @param object $pager
* @access protected
* @return array
*/
protected function fetchProjectList(string $status, string $orderBy, int $involved, object|null $pager): array
{
return $this->dao->select('DISTINCT t1.*')->from(TABLE_PROJECT)->alias('t1')
->leftJoin(TABLE_TEAM)->alias('t2')->on('t1.id=t2.root')
->leftJoin(TABLE_STAKEHOLDER)->alias('t3')->on('t1.id=t3.objectID')
->where('t1.deleted')->eq('0')
->andWhere('t1.vision')->eq($this->config->vision)
->andWhere('t1.type')->eq('project')
->beginIF(!in_array($status, array('all', 'undone', 'review', 'unclosed'), true))->andWhere('t1.status')->eq($status)->fi()
->beginIF($status == 'undone' or $status == 'unclosed')->andWhere('t1.status')->in('wait,doing')->fi()
->beginIF($status == 'review')
->andWhere("FIND_IN_SET('{$this->app->user->account}', t1.reviewers)")
->andWhere('t1.reviewStatus')->eq('doing')
->fi()
->beginIF($this->cookie->involved or $involved)
->andWhere('t2.type')->eq('project')
->andWhere('t1.openedBy', true)->eq($this->app->user->account)
->orWhere('t1.PM')->eq($this->app->user->account)
->orWhere('t2.account')->eq($this->app->user->account)
->orWhere('(t3.user')->eq($this->app->user->account)
->andWhere('t3.deleted')->eq(0)
->markRight(1)
->orWhere("CONCAT(',', t1.whitelist, ',')")->like("%,{$this->app->user->account},%")
->markRight(1)
->fi()
->orderBy($orderBy)
->page($pager, 't1.id')
->fetchAll('id');
}
/**
* 根据项目ID列表查询团队成员数量。
* Get project team member count by project id list.
*
* @param array $projectIdList
* @access protected
* @return array
*/
protected function fetchMemberCountByIdList(array $projectIdList): array
{
return $this->dao->select('t1.root, count(t1.id) as count')->from(TABLE_TEAM)->alias('t1')
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
->where('t1.root')->in($projectIdList)
->andWhere('t2.deleted')->eq(0)
->groupBy('t1.root')
->fetchAll('root');
}
}
@@ -2,32 +2,27 @@
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
su('admin');
zdTable('project')->gen(90);
/**
title=测试 projectModel::getInfoList;
timeout=0
cid=1
pid=1
查询正在进行的项目数量 >> 44
查询wait状态的Id为11的项目名称 >> 项目1
查询暂停状态的项目数量 >> 11
查询关闭状态的项目数量 >> 11
查询所有状态的项目数量 >> 110
*/
global $tester;
$tester->loadModel('project');
$doingProjects = $tester->project->getInfoList('doing');
$waitProjects = $tester->project->getInfoList('wait');
$suspendedProjects = $tester->project->getInfoList('suspended');
$closedProjects = $tester->project->getInfoList('closed');
$allProjects = $tester->project->getInfoList('all');
$doingProjects = $tester->project->getList('doing');
$waitProjects = $tester->project->getList('wait');
$suspendedProjects = $tester->project->getList('suspended');
$closedProjects = $tester->project->getList('closed');
$allProjects = $tester->project->getList('all');
r(count($doingProjects)) && p() && e('44'); //查询正在进行的项目数量
r($waitProjects) && p('11:name') && e('项目1'); //查询wait状态的Id为11的项目名称
r(count($suspendedProjects)) && p() && e('11'); //查询暂停状态的项目数量
r(count($closedProjects)) && p() && e('11'); //查询关闭状态的项目数量
r(count($allProjects)) && p() && e('110'); //查询所有状态的项目数量
r(count($allProjects)) && p() && e('90'); //查询所有状态的项目数量
+23 -9
View File
@@ -162,10 +162,10 @@ class Project
/**
* Activate a project.
*
* @param int $projectID
* @param object $project
* @param int $projectID
* @param object $project
* @access public
* @return array $changes
* @return array
*/
public function activate($projectID, $project)
{
@@ -175,8 +175,8 @@ class Project
/**
* Do activate a project.
*
* @param int $projectID
* @param object $project
* @param int $projectID
* @param object $project
* @access public
* @return bool
*/
@@ -190,7 +190,7 @@ class Project
*
* @param int $budget
* @access public
* @return int|string $projectBudget
* @return int|string
*/
public function getBudgetWithUnit($budget)
{
@@ -202,13 +202,27 @@ class Project
*
* @param int $budget
* @access public
* @return int|string $projectBudget
* @return int|string
*/
public function addProjectAdminTest($projectID)
{
global $tester;
$tester->loadModel('project')->addProjectAdmin($projectID);
$this->project->addProjectAdmin($projectID);
return $tester->dao->select('*')->from(TABLE_PROJECTADMIN)->fetch();
}
/**
* Test fetchProjectList function.
*
* @param int $status
* @param string $orderBy
* @param int $involved
* @access public
* @return array
*/
public function testFetchProjectList($status, $involved = 0)
{
$projects = $this->project->fetchProjectList($status, 'id_desc', $involved, null);
return $projects;
}
}
@@ -0,0 +1,50 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/project.class.php';
su('admin');
function initData()
{
$project = zdTable('project');
$project->id->range('11-19');
$project->project->range('11-19');
$project->name->prefix("项目")->range('11-19');
$project->code->prefix("project")->range('11-19');
$project->model->range("scrum");
$project->auth->range("[]");
$project->path->range("[]");
$project->type->range("project");
$project->grade->range("1");
$project->days->range("1");
$project->status->range("wait,doing,suspended,closed");
$project->desc->range("[]");
$project->budget->range("100000,200000");
$project->budgetUnit->range("CNY");
$project->percent->range("0-0");
$project->openedDate->range("`2023-05-01 10:00:10`");
$project->gen(9);
zdTable('team')->gen(10);
zdTable('user')->gen(10);
}
/**
title=测试 projectModel::fetchProjectList();
timeout=0
cid=1
*/
initData();
$noneIDList = array();
$wrongIDList = array('1', '2');
$realIDList = array('11', '12');
global $tester;
$projectTester = $tester->loadModel('project');
r($projectTester->fetchMemberCountByIdList($noneIDList)) && p() && e('0'); // 查询没有项目ID的情况
r($projectTester->fetchMemberCountByIdList($wrongIDList)) && p() && e('0'); // 查询错误ID的情况
r($projectTester->fetchMemberCountByIdList($realIDList)) && p('11:count') && e('1'); // 查询正常ID的情况
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env php
<?php
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/project.class.php';
su('admin');
function initData()
{
$project = zdTable('project');
$project->id->range('11-19');
$project->project->range('11-19');
$project->name->prefix("项目")->range('11-19');
$project->code->prefix("project")->range('11-19');
$project->model->range("scrum");
$project->auth->range("[]");
$project->path->range("[]");
$project->type->range("project");
$project->grade->range("1");
$project->days->range("1");
$project->status->range("wait,doing,suspended,closed");
$project->desc->range("[]");
$project->budget->range("100000,200000");
$project->budgetUnit->range("CNY");
$project->percent->range("0-0");
$project->openedDate->range("`2023-05-01 10:00:10`");
$project->gen(9);
zdTable('team')->gen(10);
$stakeholder = zdTable('stakeholder');
$stakeholder->id->range('1-9');
$stakeholder->objectID->range('11-19');
$stakeholder->objectType->range('program,project');
$stakeholder->user->range("admin");
$stakeholder->type->range("inside");
$stakeholder->from->range("[]");
$stakeholder->createdBy->range("admin");
$stakeholder->createdDate->range("`2023-05-01 10:00:10`");
$stakeholder->gen(9);
}
/**
title=测试 projectModel::fetchProjectList();
timeout=0
cid=1
*/
initData();
$statusList = array('', 'all', 'undone', 'unclosed', 'error');
$projectTester = new Project();
r($projectTester->testFetchProjectList($statusList[0])) && p() && e('0'); // 查询状态为空的项目
r(count($projectTester->testFetchProjectList($statusList[1]))) && p() && e('9'); // 获取所有项目数量
r($projectTester->testFetchProjectList($statusList[2])) && p('11:code') && e('project11'); // 查询未完成的第一个项目的code
r($projectTester->testFetchProjectList($statusList[3], 1)) && p('12:name') && e('项目12'); // 获取我参与的一个项目的项目名
r($projectTester->testFetchProjectList($statusList[4])) && p() && e('0'); // 获取错误类型的项目
+16
View File
@@ -1,6 +1,7 @@
<?php
declare(strict_types=1);
helper::import(dirname(__FILE__) . 'config/form.php');
helper::import(dirname(dirname(dirname(__FILE__))) . '/lib/date/date.class.php');
$config->todo = new stdclass();
$config->todo->batchCreate = 8;
@@ -49,3 +50,18 @@ $config->todo->sessionUri['bugList'] = 'qa';
$config->todo->sessionUri['taskList'] = 'execution';
$config->todo->sessionUri['storyList'] = 'product';
$config->todo->sessionUri['testtaskList'] = 'qa';
$config->todo->dateRange = array();
$config->todo->dateRange['all'] = array('begin' => '1970-01-01', 'end' => '2109-01-01');
$config->todo->dateRange['assignedtoother'] = array('begin' => '1970-01-01', 'end' => '2109-01-01');
$config->todo->dateRange['today'] = array('begin' => date::today(), 'end' => date::today());
$config->todo->dateRange['future'] = array('begin' => '2030-01-01', 'end' => '2030-01-01');
$config->todo->dateRange['before'] = array('begin' => '1970-01-01', 'end' => date::today());
$config->todo->dateRange['cycle'] = array('begin' => '', 'end' => '');
$config->todo->dateRange['yesterday'] = array('begin' => date::yesterday(), 'end' => date::yesterday());
$config->todo->dateRange['thisweek'] = array('begin' => date::getThisWeek()['begin'], 'end' => date::getThisWeek()['end']);
$config->todo->dateRange['lastweek'] = array('begin' => date::getLastWeek()['begin'], 'end' => date::getLastWeek()['end']);
$config->todo->dateRange['thismonth'] = array('begin' => date::getThisMonth()['begin'], 'end' => date::getThisMonth()['end']);
$config->todo->dateRange['lastmonth'] = array('begin' => date::getLastMonth()['begin'], 'end' => date::getLastMonth()['end']);
$config->todo->dateRange['thisseason'] = array('begin' => date::getThisSeason()['begin'], 'end' => date::getThisSeason()['end']);
$config->todo->dateRange['thisyear'] = array('begin' => date::getThisYear()['begin'], 'end' => date::getThisYear()['end']);
+8 -26
View File
@@ -73,15 +73,17 @@ class todo extends control
}
/**
* Batch create todo
* 批量创建待办。
* Batch create todo.
*
* @param string $date
* @access public
* @return void
*/
public function batchCreate($date = 'today')
public function batchCreate(string $date = 'today')
{
if($date == 'today') $date = date(DT_DATE1, time());
if(!empty($_POST))
{
$todoIDList = $this->todo->batchCreate();
@@ -89,37 +91,17 @@ class todo extends control
/* Locate the browser. */
$date = str_replace('-', '', $this->post->date);
if($date == '')
{
$date = 'future';
}
else if($date == date('Ymd'))
{
$date= 'today';
}
if($date == '') $date = 'future';
if($date == date('Ymd')) $date= 'today';
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'idList' => $todoIDList));
if(isonlybody()) return print(js::reload('parent.parent'));
return print(js::locate($this->createLink('my', 'todo', "type=$date"), 'parent'));
return print(js::locate($this->createLink('my', 'todo', "type={$date}"), 'parent'));
}
unset($this->lang->todo->typeList['cycle']);
/* Set Custom*/
foreach(explode(',', $this->config->todo->list->customBatchCreateFields) as $field) $customFields[$field] = $this->lang->todo->$field;
$this->view->customFields = $customFields;
$this->view->showFields = $this->config->todo->custom->batchCreateFields;
$this->view->title = $this->lang->todo->common . $this->lang->colon . $this->lang->todo->batchCreate;
$this->view->position[] = $this->lang->todo->common;
$this->view->position[] = $this->lang->todo->batchCreate;
$this->view->date = (int)$date == 0 ? $date : date('Y-m-d', strtotime($date));
$this->view->times = date::buildTimeList($this->config->todo->times->begin, $this->config->todo->times->end, $this->config->todo->times->delta);
$this->view->time = date::now();
$this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted|noempty');
$this->display();
$this->todoZen->buildBatchCreateView($date);
}
/**
+10
View File
@@ -0,0 +1,10 @@
.private-row .checkbox-primary {margin-top: 5px;}
.every-checkbox .checkbox-primary {width: 50px;}
.cycle-config .tab-content>.tab-pane {padding-left: 0; padding-right: 0;}
.cycle-config .tab-content>.tab-pane:first-child,
.cycle-config .tab-content>.input-group {width: 44.5%;}
.switch-time {min-height: 32px;}
.form-row .form-group.align-center {align-items: center;}
#nameInputBox {width: 80%;}
#nameInputBox .form-control {width: 99.8%;}
+192
View File
@@ -0,0 +1,192 @@
/**
* 切换日期选择的禁用状态。
* Toggle the disabled state of the date select.
*
* @param object switcher
* @return void
*/
function toggleDateTodo(switcher)
{
$('#date').prop('disabled', switcher.checked);
}
/**
* Load data.
* @param type $type Type of selected todo.
* @param id $id ID of selected todo.
* @param objectID $objectID ID of the closed todo type.
* @return void
*/
function loadList(type, id, objectID)
{
if(id)
{
divClass = '.name-box' + id;
divID = '#nameBox' + id;
}
else
{
divClass = '.name-box';
divID = '#nameBox';
}
id = id ? id : '';
var param = 'userID=' + userID + '&id=' + id;
if(type == "task") param += '&status=wait,doing';
if(defaultType && type == defaultType && objectID != 0) param += '&objectID=' + objectID;
if(moduleList.indexOf(type) !== -1)
{
link = '/index.php?m=' + type + '&f=' + objectsMethod[type] + '&' + param + '&t=html';
$.get(link, function(data, status)
{
if(data.length != 0)
{
$(divClass).find('#nameInputBox').html(data).find('select').chosen();
if(config.currentMethod == 'edit' || type == 'feedback') $(divClass).find('select').val(objectID).trigger('chosen:updated');
if($(divClass + " select").val() == null) $(divClass + " select").attr("data-placeholder", noOptions.replace("%s", chosenType[type])).trigger('chosen:updated');
}
else
{
$(divClass).html("<select id="+ type +" class='form-control'></select>").find('select').chosen();
}
});
}
else
{
$(divClass).html($(divID).html());
}
if(nameBoxLabel) return;
var formLabel = type == 'custom' ||(vision && vision == 'rnd') ? nameBoxLabel.custom : nameBoxLabel.objectID;
$('#nameBox .form-label').text(formLabel);
}
/**
* 选择开始时间后,自动给出默认终止时间。
* After selecting the start time, the default end time is automatically given.
*
* @return viod
*/
function selectNext()
{
$("#end ")[0].selectedIndex = $("#begin ")[0].selectedIndex + 3;
$('#end').trigger('chosen:updated');
}
function setBeginsAndEnds(i, beginOrEnd)
{
if(!i)
{
for(j = 0; j < batchCreateNum; j++)
{
if(j != 0) $("#begins" + j)[0].selectedIndex = $("#ends" + (j - 1))[0].selectedIndex;
$("#ends" + j)[0].selectedIndex = $("#begins" + j)[0].selectedIndex + 3;
$("#begins" + j, "#ends" + j).trigger('chosen:updated');
}
}
else
{
if(beginOrEnd == 'begin')
{
$("#ends" + i)[0].selectedIndex = $("#begins" + i)[0].selectedIndex + 3;
$("#ends" + i).trigger('chosen:updated');
}
if(batchCreateNum)
{
for(j = i+1; j < batchCreateNum; j++)
{
$("#begins" + j)[0].selectedIndex = $("#ends" + (j - 1))[0].selectedIndex;
$("#ends" + j)[0].selectedIndex = $("#begins" + j)[0].selectedIndex + 3;
$("#begins" + j, "#ends" + j).trigger('chosen:updated');
}
}
}
}
function switchTimeList(number)
{
if($('#switchTime' + number).prop('checked'))
{
$('#begins' + number, '#ends' + number).attr('disabled', 'disabled').trigger('chosen:updated');
}
else
{
$('#begins' + number, '#ends' + number).removeAttr('disabled').trigger('chosen:updated');
}
}
function switchDateFeature(switcher)
{
if(switcher.checked)
{
$('#begin, #end').attr('disabled','disabled').trigger('chosen:updated');
}
else
{
$('#begin, #end').removeAttr('disabled').trigger('chosen:updated');
}
}
/**
* 周期待办切换指定复选框时的交互展示。
* Interactive display when switching the specified checkbox for cycle.
*
* @param object switcher
* @return void
*/
function showSpecifiedDate(switcher)
{
if(switcher.checked)
{
$('#everyInput').attr('disabled','disabled');
$('.specify').removeClass('hidden');
$('.every').addClass('hidden')
$('#configEvery').prop('checked', false);
}
}
/**
* 切换周期复选框的回调函数,用于页面交互展示。
* Switch the cycle checkbox for page interactive display.
*
* @param object switcher
* @return void
*/
function showEvery(switcher)
{
if(switcher.checked)
{
$('#everyInput').removeAttr('disabled');
$('.specify').addClass('hidden');
$('.every').removeClass('hidden');
$('#cycleYear').removeAttr('checked');
$('#configSpecify, #configEvery').prop('checked', false);
}
}
/**
* 周期设置为天并为指定时,更改月份时获取天数。
* When the cycle is set to days and specified, obtain the number of days when changing the month.
*
* @param int $specifiedMonth
* @return void
*/
function setDays(specifiedMonth)
{
/* Get last day in specified month. */
var date = new Date();
date.setMonth(specifiedMonth);
var month = date.getMonth() + 1;
date.setMonth(month);
date.setDate(0);
var specifiedMonthLastDay = date.getDate();
$('#specifiedDay').empty('');
for(var i = 1; i <= specifiedMonthLastDay; i++)
{
html = "<option value='" + i + "' title='" + i + "' data-keys='" + i + "'>" + i + "</option>";
$('#specifiedDay').append(html);
}
}
+60
View File
@@ -0,0 +1,60 @@
/**
* 切换周期类型,用于展示周期类型的交互。
* Toggle cycle, used to display the interaction of cycle.
*
* @param object tab
* @return void
*/
function toggleCycle(switcher)
{
if(switcher.checked)
{
$('#date').attr('disabled','disabled');
$('.cycle-config').removeClass('hidden');
$('#switchDate').closest('.checkbox-primary').addClass('hidden');
$('#type').closest('.form-row').addClass('hidden');
$('#type').val('custom');
loadList('custom'); //Fix bug 3278.
}
else
{
$('#date').removeAttr('disabled');
$('.cycle-config').addClass('hidden');
$('#switchDate').closest('.checkbox-primary').removeClass('hidden');
$('#type').closest('.form-row').removeClass('hidden');
}
}
/**
* 更改待办日期。
* Change todo date.
*
* @param object tab
* @return void
*/
function changeCreateDate(dateInput)
{
var selectTime = $(dateInput).val() != today ? start : nowTime;
$('#begin').val(selectTime);
$('#begin').trigger("chosen:updated");
selectNext();
}
/**
* 切换标签页,用于更新标签页的样式和更新类型。
* Toggle tabs to update the style and type.
*
* @param object tab
* @return void
*/
function toggleNavTabs(tab)
{
$(tab).parent().siblings().find('a').prop('class', '');
$(tab).addClass('active');
if($(tab).data('type'))$('input[id*=type][id*=config]').val($(tab).data('type'));
}
function changeType(typeSelect)
{
loadList($(typeSelect).find('select').val(), '');
}
+16 -101
View File
@@ -17,12 +17,13 @@ class todoModel extends model
* Create todo data.
*
* @param object $todo
* @param object $formData
* @access public
* @return int|false
*/
public function create(object $todo): int|false
public function create(object $todo, object $formData): int|false
{
$processedTodo = $this->todoTao->processCreateData($todo);
$processedTodo = $this->todoTao->processCreateData($todo, $formData);
if(!$processedTodo) return false;
$todoID = $this->todoTao->insert($processedTodo);
@@ -256,18 +257,9 @@ class todoModel extends model
$todo = $this->loadModel('file')->replaceImgURL((object)$todo, 'desc');
if($setImgSize) $todo->desc = $this->file->setImgSize($todo->desc);
if($todo->type == 'story') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_STORY)->fetch('title');
if($todo->type == 'task') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_TASK)->fetch('name');
if($todo->type == 'bug') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_BUG)->fetch('title');
if($todo->type == 'issue' and $this->config->edition == 'max') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_ISSUE)->fetch('title');
if($todo->type == 'risk' and $this->config->edition == 'max') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_RISK)->fetch('name');
if($todo->type == 'opportunity' and $this->config->edition == 'max') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_OPPORTUNITY)->fetch('name');
if($todo->type == 'review' and $this->config->edition == 'max') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_REVIEW)->fetch('title');
if($todo->type == 'testtask') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_TESTTASK)->fetch('name');
if($todo->type == 'feedback') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_FEEDBACK)->fetch('title');
$todo->date = str_replace('-', '', $todo->date);
return $todo;
return $this->todoTao->setTodoNameByType($todo);
}
/**
@@ -280,93 +272,21 @@ class todoModel extends model
* @param object $pager
* @param string $orderBy
* @access public
* @return void
* @return array
*/
public function getList($type = 'today', $account = '', $status = 'all', $limit = 0, $pager = null, $orderBy="date, status, begin")
public function getList(string $type = 'today', string $account = '', string|array $status = 'all', int $limit = 0, object $pager = null, string $orderBy="date, status, begin"): array
{
$this->app->loadClass('date');
$todos = array();
$type = strtolower($type);
if($type == 'all' or $type == 'assignedtoother')
{
$begin = '1970-01-01';
$end = '2109-01-01';
}
elseif($type == 'today')
{
$begin = date::today();
$end = $begin;
}
elseif($type == 'yesterday')
{
$begin = date::yesterday();
$end = $begin;
}
elseif($type == 'thisweek')
{
extract(date::getThisWeek());
}
elseif($type == 'lastweek')
{
extract(date::getLastWeek());
}
elseif($type == 'thismonth')
{
extract(date::getThisMonth());
}
elseif($type == 'lastmonth')
{
extract(date::getLastMonth());
}
elseif($type == 'thisseason')
{
extract(date::getThisSeason());
}
elseif($type == 'thisyear')
{
extract(date::getThisYear());
}
elseif($type == 'future')
{
$begin = '2030-01-01';
$end = $begin;
}
elseif($type == 'before')
{
$begin = '1970-01-01';
$end = date::today();
}
elseif($type == 'cycle')
{
$begin = $end = '';
}
else
{
$begin = $end = $type;
}
$dateRange = $this->config->todo->dateRange[$type] ? $this->config->todo->dateRange[$type] : array('begin' => $type, 'end' => $type);
$begin = (string)$dateRange['begin'];
$end = (string)$dateRange['end'];
if(empty($account)) $account = $this->app->user->account;
$stmt = $this->dao->select('*')->from(TABLE_TODO)
->where('deleted')->eq('0')
->andWhere('vision')->eq($this->config->vision)
->beginIF($type == 'assignedtoother')->andWhere('account', true)->eq($account)->fi()
->beginIF($type != 'assignedtoother')->andWhere('assignedTo', true)->eq($account)->fi()
->orWhere('finishedBy')->eq($account)
->orWhere('closedBy')->eq($account)
->markRight(1)
->beginIF($begin)->andWhere('date')->ge($begin)->fi()
->beginIF($end)->andWhere('date')->le($end)->fi()
->beginIF($status != 'all' and $status != 'undone')->andWhere('status')->in($status)->fi()
->beginIF($status == 'undone')->andWhere('status')->notin('done,closed')->fi()
->beginIF($type == 'cycle')->andWhere('cycle')->eq('1')->fi()
->beginIF($type != 'cycle')->andWhere('cycle')->eq('0')->fi()
->beginIF($type == 'assignedtoother')->andWhere('assignedTo')->notin(array($account, ''))->fi()
->orderBy($orderBy)
->beginIF($limit > 0)->limit($limit)->fi()
->page($pager)
->query();
$stmt = $this->todoTao->getListQuery($type, $account, $status, $begin, $end, $pager, $limit, $orderBy);
/* Set session. */
$sql = explode('WHERE', $this->dao->get());
@@ -375,15 +295,7 @@ class todoModel extends model
while($todo = $stmt->fetch())
{
if($todo->type == 'story') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_STORY)->fetch('title');
if($todo->type == 'task') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_TASK)->fetch('name');
if($todo->type == 'bug') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_BUG)->fetch('title');
if($todo->type == 'testtask') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_TESTTASK)->fetch('name');
if($todo->type == 'issue' && $this->config->edition == 'max') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_ISSUE)->fetch('title');
if($todo->type == 'risk' && $this->config->edition == 'max') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_RISK)->fetch('name');
if($todo->type == 'opportunity' && $this->config->edition == 'max') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_OPPORTUNITY)->fetch('name');
if($todo->type == 'review' && $this->config->edition == 'max') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_REVIEW)->fetch('title');
if($todo->type == 'feedback' and $this->config->edition != 'open') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_FEEDBACK)->fetch('title');
$todo = $this->todoTao->setTodoNameByType($todo);
$todo->begin = date::formatTime($todo->begin);
$todo->end = date::formatTime($todo->end);
@@ -397,11 +309,14 @@ class todoModel extends model
}
/**
* Get by id list.
* 通过包含一个或多个待办ID的列表获取待办列表,这个列表是以todoID为key、以todo对象为value的数组。
* 如果待办ID列表为空则返回所有待办。
* Get a array with todos which todoID as key, todo object as value by todo id list.
* Return all todos if the todo id list is empty.
*
* @param array $todoIDList
* @access public
* @return object
* @return array
*/
public function getByList($todoIDList = 0)
{
+70 -1
View File
@@ -340,7 +340,7 @@ class todoTao extends todoModel
/**
* 获取用户的待办事项数量。
* Get todo count on the account.
*
*
* @param string $account
* @access protected
* @return int
@@ -357,4 +357,73 @@ class todoTao extends todoModel
->markRight(1)
->fetch('count');
}
/**
* 根据待办类型设置待办名称。
* Set todo name by its type.
*
* @param object $todo
* @access protected
* @return object
*/
protected function setTodoNameByType(object $todo): object
{
if($todo->type == 'story') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_STORY)->fetch('title');
if($todo->type == 'task') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_TASK)->fetch('name');
if($todo->type == 'bug') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_BUG)->fetch('title');
if($todo->type == 'testtask') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_TESTTASK)->fetch('name');
if($this->config->edition == 'max')
{
if($todo->type == 'risk' ) $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_RISK)->fetch('name');
if($todo->type == 'issue') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_ISSUE)->fetch('title');
if($todo->type == 'review') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_REVIEW)->fetch('title');
if($todo->type == 'opportunity') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_OPPORTUNITY)->fetch('name');
}
if($this->config->edition == 'biz' || $this->config->edition == 'max')
{
if($todo->type == 'feedback') $todo->name = $this->dao->findByID($todo->objectID)->from(TABLE_FEEDBACK)->fetch('title');
}
return $todo;
}
/**
* 构造待办列表查询语句。
* Build query for todo list.
*
* @param string $type
* @param string $account
* @param string|array $status
* @param string $begin
* @param string $end
* @param object $pager
* @param int $limit
* @param string $orderBy
* @access protected
* @return object
*/
protected function getListQuery(string $type, string $account, array|string $status, string $begin, string $end, object $pager, int $limit, string $orderBy): object
{
return $this->dao->select('*')->from(TABLE_TODO)
->where('deleted')->eq('0')
->andWhere('vision')->eq($this->config->vision)
->beginIF($type == 'assignedtoother')->andWhere('account', true)->eq($account)->fi()
->beginIF($type != 'assignedtoother')->andWhere('assignedTo', true)->eq($account)->fi()
->orWhere('finishedBy')->eq($account)
->orWhere('closedBy')->eq($account)
->markRight(1)
->beginIF($begin)->andWhere('date')->ge($begin)->fi()
->beginIF($end)->andWhere('date')->le($end)->fi()
->beginIF($status != 'all' and $status != 'undone')->andWhere('status')->in($status)->fi()
->beginIF($status == 'undone')->andWhere('status')->notin('done,closed')->fi()
->beginIF($type == 'cycle')->andWhere('cycle')->eq('1')->fi()
->beginIF($type != 'cycle')->andWhere('cycle')->eq('0')->fi()
->beginIF($type == 'assignedtoother')->andWhere('assignedTo')->notin(array($account, ''))->fi()
->orderBy($orderBy)
->beginIF($limit > 0)->limit($limit)->fi()
->page($pager)
->query();
}
}
+41 -84
View File
@@ -1,105 +1,62 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
include dirname(__FILE__, 5) . "/test/lib/init.php";
include dirname(__FILE__, 2) . '/todo.class.php';
su('admin');
function initData()
{
$todo = zdTable('todo');
$todo->id->range('1');
$todo->name->prefix("待办")->range('1');
$todo->date->range('`2023-04-23`');
$todo->type->range('custom');
$todo->gen(1, '', true);
}
su('admin');
/**
title=测试 todoModel->create();
timeout=0
cid=1
pid=1
- 执行todoTest模块的createTest方法,参数是$todoWithoutName, $formData @0
- 执行todoTest模块的createTest方法,参数是$todoInvalidEnd, $formData @0
- 执行todoTest模块的createTest方法,参数是$todo, $formData @2
- 执行todoTest模块的createTest方法,参数是$todoWithCycle, $formData @3
*/
$accountList = array('admin', 'dev1', 'test1');
$b_noname = new stdclass();
$b_noname->name = '';
$b_noname->date = 'today';
$b_noname->type = 'custom';
$todo1 = new stdclass();
$todo1->name = '时间待定的月周期待办';
$todo1->type = 'custom';
$todo1->date = '+3 days';
$todo1->config = array('day' => '', 'specify' => array('month' => 0, 'day' => 1), 'month' => array(1,3,5), 'type' => 'month', 'beforeDays' => 2, 'end' => '2025-01-01');
$todo2 = new stdclass();
$todo2->name = 'bug待办';
$todo2->type = 'bug';
$todo2->date = 'today';
$todo2->bug = '313';
$todo2->status = 'doing';
$todo2->uid = '313';
$todo3 = new stdclass();
$todo3->name = 'task待办';
$todo3->type = 'task';
$todo3->date = 'today';
$todo3->task = '1';
$todo3->status = 'done';
$todo3->uid = '1';
$todo4 = new stdclass();
$todo4->name = 'story待办';
$todo4->type = 'story';
$todo4->date = 'today';
$todo4->story = '1';
$todo4->status = 'closed';
$todo4->uid = '1';
$todo = new todoTest();
global $tester;
$tester->loadModel('todo');
initData();
zdTable('todo')->config('create')->gen(1);
$todoWithoutName = new stdclass;
$today = date('Y-m-d');
$formData = new stdClass;
$formData->rawdata = new stdclass;
$formData->rawdata->uid = '';
$todo = new stdclass;
$todo->name = 'TODO Create Test';
$todo->account = 'admin';
$todo->date = date('Y-m-d');
$todo->type = 'custom';
$todo->begin = '0800';
$todo->end = '1700';
$todo->assignedTo = 'admin';
$todoWithoutName = clone $todo;
$todoWithoutName->name = '';
$todoWithoutName->date = date('Y-m-d');
$todoWithoutName->type = 'custom';
$todoInvalidDate = new stdclass;
$todoInvalidDate->name = 'todoInvalidDate';
$todoInvalidDate->date = 'today';
$todoInvalidDate->type = 'custom';
$todoInvalidEnd = clone $todo;
$todoInvalidEnd->name = 'todoInvalidDate';
$todoInvalidEnd->begin = '1000';
$todoInvalidEnd->end = '0800';
$todoValid = new stdclass;
$todoValid->name = 'todoValid';
$todoValid->date = date('Y-m-d');
$todoValid->type = 'custom';
$todoWithCycle = clone $todo;
$todoWithCycle->type = 'cycle';
$todoWithCycle->cycle = 1;
$todoWithCycle->config = array('day' => 1, 'specify' => array('month' => 0, 'day' => 1), 'type' => 'day', 'beforeDays' => 1, 'end' => '');
$todoWithCycle->objectID = 0;
$todoValid1 = new stdclass;
$todoValid1->name = 'todoValid1';
$todoValid1->date = date('Y-m-d');
$todoValid1->type = 'custom';
/**
* 1. 如果r函数返回的是一个复杂的结构,比如混合内容的数组或者嵌套的数组,p函数是无法获取返回值中的某些特定值的,需要自己编写助手函数,比如在../todo.class.php中编写。
* 2. 如果ztf执行没有生成注释,需要检查php脚本是否执行报错,建议先使用php执行编写的测试用例再用ztf执行。
*/
r($tester->todo->create($todoValid)) && p() && e('2');
r($tester->todo->create($todoWithoutName)) && p() && e('0');
r($tester->todo->create($todoInvalidDate)) && p() && e('0');
r($tester->todo->create($todoValid1)) && p() && e('3');
exit(0);
r($todo->createTest($accountList[0], $b_noname)) && p() && e('『待办名称』不能为空。');
r($todo->createTest($accountList[1], $todo1)) && p('name,type,status') && e('时间待定的月周期待办,custom,wait');
r($todo->createTest($accountList[2], $todo2)) && p('name,type,status') && e('测试单转Bug13,bug,doing');
r($todo->createTest($accountList[0], $todo3)) && p('name,type,status') && e('开发任务11,task,done');
r($todo->createTest($accountList[0], $todo4)) && p('name,type,status') && e('用户需求1,story,closed');
$todoTest = new todoTest();
r($todoTest->createTest($todoWithoutName, $formData)) && p() && e('0');
r($todoTest->createTest($todoInvalidEnd, $formData)) && p() && e('0');
r($todoTest->createTest($todo, $formData)) && p() && e('2');
r($todoTest->createTest($todoWithCycle, $formData)) && p() && e('3');
+8 -10
View File
@@ -10,18 +10,16 @@ title=测试 todoModel->getByList();
cid=1
pid=1
获取todo 1 2 3 4的名称 >> 自定义1的待办,BUG2的待办,任务3的待办,需求4的待办
获取todo 5 6 7 8的名称 >> 测试单5的待办,自定义6的待办,BUG7的待办,任务8的待办
获取todo 9 10 11 12的名称 >> 需求9的待办,测试单10的待办,自定义11的待办,BUG12的待办
*/
$todoIDList1 = array('1', '2', '3', '4');
$todoIDList2 = array('5', '6', '7', '8');
$todoIDList3 = array('9', '10', '11', '12');
$todoIDList1 = array('1');
$todoIDList2 = array('1', '2', '3', '4');
$todoIDList3 = array('9', '10', '11', '12'); /* Test not existed items. */
$todoIDList4 = array(); /* Test empty todoIDList. */
$todo = new todoTest();
r($todo->getByListTest($todoIDList1)) && p() && e('自定义1的待办,BUG2的待办,任务3的待办,需求4的待办'); // 获取todo 1 2 3 4的名称
r($todo->getByListTest($todoIDList2)) && p() && e('测试单5的待办,自定义6的待办,BUG7的待办,任务8的待办'); // 获取todo 5 6 7 8的名称
r($todo->getByListTest($todoIDList3)) && p() && e('需求9的待办,测试单10的待办,自定义11的待办,BUG12的待办'); // 获取todo 9 10 11 12的名称
r($todo->getByListTest($todoIDList1)) && p() && e('1');
r($todo->getByListTest($todoIDList2)) && p() && e('1234');
r($todo->getByListTest($todoIDList3)) && p() && e('');
r($todo->getByListTest($todoIDList4)) && p() && e('12345');
@@ -0,0 +1,22 @@
title: Create todos for todoModel::create UTC.
desc: Create todos for todoModel::create UTC.
author: xushenjie
version: 1.0.0
fields:
- field: id
range: 1
- field: account
range: 'admin'
- field: name
range: '待办'
- field: date
range: '`2023-04-23`'
- field: type
range: 'custom'
- field: begin
range: '0800'
- field: end
range: '1700'
- field: assignedTo
range: 'admin'
@@ -27,5 +27,11 @@ fields:
prefix: '这是待办描述'
- field: status
range: 'wait,doing,done,closed,closed'
- field: finishedDate
range: 1-9
prefix: '2023-04-2'
- field: closedDate
range: 1-9
prefix: '2023-04-2'
@@ -0,0 +1,37 @@
title: zt_getbylist
author: jukui
version: "1.0"
fields:
- field: id
range: 1-5
- field: account
range: 'admin'
- field: date
range: 1-9
prefix: '2023-04-2'
- field: begin
range: 1000-1100
- field: end
range: 1200-1300
- field: type
range: 'custom'
- field: cycle
range: 0
- field: pri
range: 3
- field: name
range: 1-5
prefix: '自定义的待办'
- field: desc
range: 1-5
prefix: '这是待办描述'
- field: status
range: 'wait,doing,done,closed,closed'
- field: finishedDate
range: 1-9
prefix: '2023-04-2'
- field: closedDate
range: 1-9
prefix: '2023-04-2'
+16 -38
View File
@@ -12,37 +12,16 @@ class todoTest
/**
* Test create a todo.
*
* @param string $account
* @param array $param
* @param object $todoData
* @param object $formData
* @access public
* @return object
* @return int
*/
public function createTest($account, $param = array())
public function createTest($todoData, $formData)
{
$config = array('day' => '', 'specify' => array('month' => 0, 'day' => 1), 'type' => 'day', 'beforeDays' => 0, 'end' => '');
if(isset($param->date)) $param->date = $param->date == 'today' ? date('Y-m-d',time()) : date('Y-m-d',strtotime('+3 days'));
$objectID = $this->objectModel->create($todoData, $formData);
$createFields['config'] = $config;
$createFields['type'] = 'custom';
$createFields['name'] = '';
$createFields['pri'] = 3;
$createFields['desc'] = '';
$createFields['status'] = 'wait';
$createFields['begin'] = '0830';
$createFields['end'] = '0900';
foreach($createFields as $field => $defaultValue) $_POST[$field] = $defaultValue;
foreach($param as $key => $value) $_POST[$key] = $value;
$objectID = $this->objectModel->create(date('Y').date('m'), $account);
unset($_POST);
if(dao::isError()) return array_values(dao::getError())[0][0];
$object = $objectID ? $this->objectModel->getByID($objectID) : 0;
return $object;
return $objectID ?: 0;
}
/**
@@ -238,23 +217,22 @@ class todoTest
}
/**
* Test get todo by id list.
* Test todoModel::getByList.
*
* @parami array $todoIDList
* @param array $todoIDList
* @access public
* @return void
* @return string
*/
public function getByListTest($todoIDList = 0)
{
$objects = $this->objectModel->getByList($todoIDList);
$name = '';
foreach($objects as $todo) $name .= ',' . $todo->name;
$name = trim($name, ',');
if(dao::isError()) return dao::getError();
return $name;
$result = '';
foreach($objects as $id => $todo)
{
$result .= (string) $todo->id;
}
if(empty($result)) return "pass";
return $result;
}
/**
+458
View File
@@ -0,0 +1,458 @@
<?php
declare(strict_types=1);
/**
* The ui file of todo module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Zemei Wang<wangzemei@easycorp.ltd>
* @package todo
* @link http://www.zentao.net
*/
namespace zin;
jsVar('noTodo', $lang->todo->noTodo);
jsVar('moduleList', $config->todo->moduleList);
jsVar('objectsMethod', $config->todo->getUserObjectsMethod);
jsVar('nameBoxLabel', array('custom' => $lang->todo->name, 'idvalue' => isset($lang->todo->idvalue) ? $lang->todo->idvalue : null));
jsVar('vision', $config->vision);
jsVar('noOptions', $lang->todo->noOptions);
jsVar('chosenType', $lang->todo->typeList);
jsVar('today', date('Y-m-d'));
jsVar('nowTime', $time);
jsVar('start', key($times));
jsVar('userID', $app->user->id);
jsVar('defaultType', '');
/**
* 构建周期设置的天的标签内容,待办为周期类型进行展示。
* Build tab-pane content of day.
*
* @return mixed Any type supported by zin widget function 任何 zin 部件函数参数支持的类型。
*/
function buildDayPane(): mixed
{
global $lang, $app;
return fragment
(
inputGroup
(
set::class('every'),
span
(
set::class('input-group-addon'),
$lang->todo->every
),
input
(
set::id('everyInput'),
set::name('config[day]')
),
span
(
set::class('input-group-addon'),
$lang->todo->cycleDay
),
div
(
set::class('pl-3 flex items-center input-group-addon every-checkbox'),
checkbox
(
set::id('configSpecify'),
set::name('config[specifiedDate]'),
set::text($lang->todo->specify),
set::value(1),
on::change('showSpecifiedDate(this)')
)
)
),
inputGroup
(
set::class('specify hidden'),
span
(
set::class('input-group-addon'),
$lang->todo->specify
),
select
(
set::id('config[specify][month]'),
set::name('config[specify][month]'),
set::items($lang->datepicker->monthNames),
set::value(0),
set::multiple(false),
on::change('setDays(this.value)')
),
select
(
set::id('specifiedDay'),
set::name('config[specify][day]'),
set::items($lang->todo->specifiedDay),
set::multiple(false),
set::value(1)
),
span
(
set::class('input-group-addon', strpos($app->getClientLang(), 'zh') !== false ? '' : 'hidden'),
$lang->todo->day
),
div
(
set::class('w-36 pl-3 flex items-center gap-3 input-group-addon'),
checkbox
(
set::id('cycleYear'),
set::name('config[cycleYear]'),
set::value(1),
set::text($lang->todo->everyYear)
),
checkbox
(
set::id('configEvery'),
set::name('configEvery'),
set::value(1),
set::text($lang->todo->every),
on::change('showEvery(this)')
)
)
)
);
}
/**
* 构建月的标签页,用于周期设置。
* Build tab-pane content of month.
*
* @return mixed Any type supported by zin widget function 任何 zin 部件函数参数支持的类型。
*/
function buildMonthDays(): mixed
{
$days = array();
for($i = 1; $i <= 31; $i ++) $days[$i] = $i;
return checkList
(
set::class('flex-wrap gap-4'),
set::name('config[month]'),
set::inline(true),
set::items($days),
);
}
/**
* 构建周期设置的标签导航,待办为周期类型进行展示。
* Build navTabs header for cycle.
*
* @return mixed Any type supported by zin widget function 任何 zin 部件函数参数支持的类型。
*/
function buildNavTabsBar(): mixed
{
global $lang;
$navTabs = [
[
'type' => 'day',
'text' => $lang->todo->cycleDay,
'class' => ' active'
],
[
'type' => 'week',
'text' => $lang->todo->cycleWeek,
'class' => ''
],
[
'type' => 'month',
'text' => $lang->todo->cycleMonth,
'class' => '',
],
];
$nav = ul(set::class('nav nav-tabs'));
foreach($navTabs as $tab)
{
$nav->add(
li
(
set::class('nav-item'. $tab['class']),
a
(
set::href('#' . $tab['type']),
set::class($tab['class']),
set('data-toggle', 'tab'),
$tab['text'],
on::click('toggleNavTabs(this)')
)
),
);
}
return $nav;
}
/**
* 构建周期设置的标签页,当待办为周期时进行展示。
* Build navTabs for cycle.
*
* @return mixed Any type supported by zin widget function 任何 zin 部件函数参数支持的类型。
*/
function buildNavTabs(): mixed
{
global $lang;
return div
(
set::class('w-full'),
buildNavTabsBar(),
div
(
set::class('tab-content'),
div
(
set::class('tab-pane active'),
set::id('day'),
buildDayPane(),
),
div
(
set::class('tab-pane'),
set::id('week'),
checkList
(
set::primary(true),
set::id('config[week]'),
set::name('config[week]'),
set::inline(true),
set::items($lang->todo->dayNames),
),
),
div
(
set::class('tab-pane h-28'),
set::id('month'),
buildMonthDays(),
),
inputGroup
(
div
(
set::class('input-group-addon'),
'提前',
),
input
(
set::id('name'),
set::name('config[beforeDays]'),
set::value(0),
),
div
(
set::class('input-group-addon'),
'天生成待办',
),
),
),
);
}
formPanel
(
formRow
(
formGroup
(
set::width('1/2'),
set::label($lang->todo->date),
set::strong(true),
set::class('align-center'),
inputGroup
(
input
(
set::name('date'),
set::value(date('Y-m-d')),
set::type('date'),
set::width('1/3'),
on::change('changeCreateDate(this)')
),
div
(
set::class('flex items-center gap-3 pl-3 input-group-addon'),
checkbox
(
set::id('switchDate'),
set::name('switchDate'),
set::text($lang->todo->periods['future']),
set::width('100px'),
on::change('toggleDateTodo(this)')
),
checkbox
(
set::id('cycle'),
set::name('cycle'),
set::value(1),
set::text($lang->todo->cycle),
on::change('toggleCycle(this)')
)
)
)
)
),
formRow
(
set::class('cycle-config hidden'),
formGroup
(
set::label($lang->todo->cycleConfig),
set::strong(true),
buildNavTabs()
)
),
formRow
(
set::class('cycle-config hidden'),
formGroup
(
set::width('1/2'),
set::label($lang->todo->deadline),
set::strong(true),
input
(
set::type('date'),
set::name('config[end]')
)
)
),
formGroup
(
set::width('1/2'),
set::name('type'),
set::strong(true),
set::label($lang->todo->type),
set::items($lang->todo->typeList),
on::change('changeType(this)'),
),
formGroup
(
set::width('1/2'),
set::name('assignedTo'),
set::strong(true),
set::label($lang->todo->assignTo),
set::items($users),
set::value($app->user->account)
),
formRow
(
formGroup
(
set::id('nameBox'),
set::class('name-box'),
set::label($lang->todo->name),
set::strong(true),
set::required(true),
inputGroup
(
set::class('title-group'),
div
(
set::id('nameInputBox'),
input
(
set::id('name'),
set::name('name')
)
),
div
(
set::class('input-group-addon fix-border br-0'),
$lang->todo->pri
),
select
(
set::class('w-20'),
set::id('pri'),
set::name('pri'),
set::items($lang->todo->priList),
set::value(3)
)
)
)
),
formGroup
(
set::name('desc'),
set::strong(true),
set::type('textarea'),
set::label($lang->todo->desc)
),
formGroup
(
set::width('1/2'),
set::id('status'),
set::name('status'),
set::items($lang->todo->statusList),
set::label($lang->todo->status),
set::strong(true)
),
formRow
(
set::class('items-center'),
formGroup
(
set::width('1/2'),
set::label($lang->todo->beginAndEnd),
set::strong(true),
inputGroup
(
select
(
set::id('begin'),
set::name('begin'),
set::items($times),
set::value(date('Y-m-d') != $date ? key($times) : $time),
on::change('selectNext()')
),
select
(
set::id('end'),
set::name('end'),
set::items($times)
)
),
div
(
set::class('ml-3 flex items-center switch-time'),
checkbox
(
set::id('switchTime'),
set::name('switchTime'),
set::text($lang->todo->lblDisableDate),
on::change('switchDateFeature(this)')
)
)
)
),
formGroup
(
set::label($lang->todo->private),
set::strong(true),
set::class('private-row'),
checkbox
(
set::id('private'),
set::name('private'),
set::value(1)
)
)
);
render();
+27
View File
@@ -22,6 +22,33 @@ class todoZen extends todo
$this->display();
}
/**
* 生成批量创建待办视图数据。
* Build batch create form data.
*
* @param string $date
* @access protected
* @return void
*/
protected function buildBatchCreateView(string $date)
{
/* Set Custom. */
foreach(explode(',', $this->config->todo->list->customBatchCreateFields) as $field) $customFields[$field] = $this->lang->todo->$field;
$this->view->customFields = $customFields;
$this->view->showFields = $this->config->todo->custom->batchCreateFields;
$this->view->title = $this->lang->todo->common . $this->lang->colon . $this->lang->todo->batchCreate;
$this->view->position[] = $this->lang->todo->common;
$this->view->position[] = $this->lang->todo->batchCreate;
$this->view->date = (int)$date == 0 ? $date : date('Y-m-d', strtotime($date));
$this->view->times = date::buildTimeList($this->config->todo->times->begin, $this->config->todo->times->end, $this->config->todo->times->delta);
$this->view->time = date::now();
$this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted|noempty');
$this->display();
}
/**
* 生成编辑待办视图数据。
* Build create form data.
+1 -1
View File
@@ -3,4 +3,4 @@ sonar.sourceEncoding=UTF-8
sonar.qualitygate.wait=true
sonar.coverage.exclusions=**/*.*
sonar.inclusions=**/**.php
sonar.exclusions=**/*.bak,**/*.sql,**/*.js,**/*.css,**/*.yaml,**/*.zip,**/*.out,**/lang/*,**/test/**
sonar.exclusions=**/*.bak,**/*.sql,**/*.js,**/*.css,**/*.yaml,**/*.zip,**/*.out,**/lang/*,**/test/**,doc/*
+5
View File
@@ -81,5 +81,10 @@ fields:
range: 5-10000:5
- field: openedVersion
range: "16.5"
- field: openedDate
range: "(-3M)-(+M):1D"
type: timestamp
format: "YY/MM/DD"
postfix: "\t"
- field: deleted
range: 0
+1 -1
View File
@@ -32,7 +32,7 @@ fields:
- field: account1
range: admin,user{98},test{100},pm{100},po{100}
- field: account2
range: [],3-100,1-100,1-100,1-100
range: "[],3-100,1-100,1-100,1-100"
prefix: ""
postfix: ""
loop: 0
+6 -6
View File
@@ -26,7 +26,7 @@ fields:
- field: account1
range: admin,user{99},test{100},dev{100},pm{100},po{100},td{100},pd{100},qd{100},top{100},outside{100},others{100},a,bb,ccc,qwuiadsd?!2as@#%$aasd~aj1!@#1
- field: account2
range: [],1-99,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,[]{4}
range: "[],1-99,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,[]{4}"
- field: password
note: "密码"
range: 123Qwe!@#{346}
@@ -53,7 +53,7 @@ fields:
- field: commiter1
range: admin,user{99},test{100},dev{100},pm{100},po{100},td{100},pd{100},qd{100},top{100},outside{100},others{100},a,bb,ccc,qwuiadsd?!2as@#%$aasd~aj1!@#1
- field: commiter2
range: [],1-99,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,[]{4}
range: "[],1-99,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,1-100,[]{4}"
- field: avatar
note: ""
range: ""
@@ -78,7 +78,7 @@ fields:
- field: email2
range: 1000-9999:2
- field: email3
range: [@qq.com,@163.com,@gmail.com]
range: "[@qq.com,@163.com,@gmail.com]"
- field: skype
note: "Skype"
range: Skype
@@ -125,9 +125,8 @@ fields:
note: "入职日期"
range: "(M)-(w)"
type: timestamp
prefix: "DateTime"
postfix: ""
format: "YY/MM/DD"
format: "YY/MM/DD hh:mm:ss"
- field: visits
note: "访问次数"
range: 0-10000:R
@@ -154,7 +153,7 @@ fields:
range: 1-10000
- field: locked
note: "锁定时间"
range: 0
range: "`2023-01-02 10:00:00`"
- field: company
note: "公司"
range: 1
@@ -170,6 +169,7 @@ fields:
range: 1-10000:R
- field: scoreLevel
note: "积分等级"
range: 0
- field: deleted
note: "是否删除"
range: 0
+5 -3
View File
@@ -115,7 +115,7 @@ function p($keys = '', $delimiter = ',')
{
$values = getValues($_result, $part, $delimiter);
if(!is_array($values)) continue;
foreach($values as $value) echo $value . "\n";
}
@@ -272,7 +272,7 @@ function genModuleAndMethod($rParams)
{
$param = trim($param, "'");
if($param[0] != '$') $param = trim(strchr($param, '$'), ')');
$objArrowCount = substr_count($param, '->');
$rParamsStructureList = explode('->', $param);
@@ -474,7 +474,9 @@ function zdImport($table, $yaml, $count = 10)
function su($account)
{
$userModel = new userModel();
$user = $userModel->identify($account, '123Qwe!@#');
$user = $userModel->getByID($account);
if($user) return $userModel->login($user);
$user = $userModel->identify($account, $user->password);
return false;
}
+7
View File
@@ -100,6 +100,13 @@ class h extends wg
return new h(prop('tagName', $tagName), $args);
}
public static function a()
{
$a = static::create('a', func_get_args());
if($a->prop('target') === '_blank' && !$a->hasProp('rel')) $a->prop('rel', 'noopener noreferrer');
return $a;
}
public static function button()
{
return static::create('button', prop('type', 'button'), func_get_args());