Merge branch 'sprint/168' into 'sprint/168_dingna_task_43401'
# Conflicts: # module/execution/control.php # module/execution/view/kanban.html.php # module/kanban/lang/zh-cn.php # module/kanban/model.php
This commit is contained in:
+48
-48
@@ -14,53 +14,53 @@ $lang->api->common = 'API';
|
||||
$lang->api->getModel = 'Super Model API';
|
||||
$lang->api->sql = 'SQL Query API';
|
||||
|
||||
$lang->api->index = 'Api Doc Home';
|
||||
$lang->api->editLib = 'Edit Api Doc';
|
||||
$lang->api->releases = 'Releases';
|
||||
$lang->api->index = 'Home';
|
||||
$lang->api->editLib = 'Edit';
|
||||
$lang->api->releases = 'Release';
|
||||
$lang->api->deleteRelease = 'Delete Release';
|
||||
$lang->api->deleteLib = 'Delete Api Doc';
|
||||
$lang->api->deleteLib = 'Delete API Doc';
|
||||
$lang->api->createRelease = 'Publish';
|
||||
$lang->api->createLib = 'Create Api Library';
|
||||
$lang->api->createApi = 'Create Api';
|
||||
$lang->api->createLib = 'Create API Library';
|
||||
$lang->api->createApi = 'Create API Document';
|
||||
$lang->api->createAB = 'Create';
|
||||
$lang->api->edit = 'Edit';
|
||||
$lang->api->delete = 'Delete';
|
||||
$lang->api->position = 'Positions';
|
||||
$lang->api->position = 'Position';
|
||||
$lang->api->startLine = "%s,%s";
|
||||
$lang->api->desc = 'Description';
|
||||
$lang->api->debug = 'Debug';
|
||||
$lang->api->submit = 'Submit';
|
||||
$lang->api->url = 'Url';
|
||||
$lang->api->url = 'URL';
|
||||
$lang->api->result = 'Result';
|
||||
$lang->api->status = 'Status';
|
||||
$lang->api->data = 'Content';
|
||||
$lang->api->noParam = 'Get debugging does not require input parameters,';
|
||||
$lang->api->noModule = 'There is no directory under the interface library. Please maintain the directory first';
|
||||
$lang->api->noModule = 'No directory in the API library. Please add the directory first';
|
||||
$lang->api->post = 'Please refer to the page form for post debugging';
|
||||
$lang->api->noUniqueName = 'Api library name already exists。';
|
||||
$lang->api->noUniqueVersion = 'Version already exists。';
|
||||
$lang->api->noUniqueName = 'The API library name exists.';
|
||||
$lang->api->noUniqueVersion = 'The version exists.';
|
||||
$lang->api->version = 'Version';
|
||||
$lang->api->createStruct = 'Create Data Structure';
|
||||
$lang->api->editStruct = 'Edit Data Structure';
|
||||
$lang->api->deleteStruct = 'Delete Data Structure';
|
||||
$lang->api->create = 'Ceate Doc';
|
||||
$lang->api->title = 'Interface Name';
|
||||
$lang->api->pageTitle = 'Api Library';
|
||||
$lang->api->create = 'Create API';
|
||||
$lang->api->title = 'Name';
|
||||
$lang->api->pageTitle = 'API Library';
|
||||
$lang->api->module = 'Directory';
|
||||
$lang->api->apiDoc = 'Interface';
|
||||
$lang->api->apiDoc = 'API';
|
||||
$lang->api->manageType = 'Manage Directory';
|
||||
$lang->api->managePublish = 'Manage Version';
|
||||
$lang->api->doing = 'Doing';
|
||||
$lang->api->done = 'Done';
|
||||
$lang->api->basicInfo = 'Essential Information';
|
||||
$lang->api->apiDesc = 'Interface Description';
|
||||
$lang->api->confirmDelete = "Are you sure to delete this interface?";
|
||||
$lang->api->confirmDeleteLib = "Are you sure to delete this interface library?";
|
||||
$lang->api->basicInfo = 'Basic Information';
|
||||
$lang->api->apiDesc = 'Description';
|
||||
$lang->api->confirmDelete = "Do you want to delete this API?";
|
||||
$lang->api->confirmDeleteLib = "Do you want to delete this interface library?";
|
||||
$lang->api->filterStruct = "use struct";
|
||||
$lang->api->defaultVersion = "Current Version";
|
||||
|
||||
/* Common access control lang. */
|
||||
$lang->api->whiteList = 'White list';
|
||||
$lang->api->whiteList = 'Whitelist';
|
||||
$lang->api->aclList['open'] = 'Open';
|
||||
$lang->api->aclList['private'] = 'Private';
|
||||
$lang->api->aclList['custom'] = 'Custom';
|
||||
@@ -68,9 +68,9 @@ $lang->api->group = 'Group';
|
||||
$lang->api->user = 'User';
|
||||
|
||||
$lang->api->noticeAcl = array(
|
||||
'open' => 'Users who can access the api library which the library belongs can access it.',
|
||||
'custom' => 'Users in the whiltelist can access it.',
|
||||
'private' => 'Only the one who created it can access it.',
|
||||
'open' => 'Users who can access the API library can access it.',
|
||||
'custom' => 'Users on the whiltelist can access it.',
|
||||
'private' => 'Only the one who creates it can access it.',
|
||||
);
|
||||
|
||||
/* fields of struct */
|
||||
@@ -81,7 +81,7 @@ $lang->struct->field = 'Field';
|
||||
$lang->struct->paramsType = 'Type';
|
||||
$lang->struct->required = 'Require';
|
||||
$lang->struct->desc = 'Description';
|
||||
$lang->struct->descPlaceholder = 'Parameter description';
|
||||
$lang->struct->descPlaceholder = 'Parameter Description';
|
||||
$lang->struct->action = 'Action';
|
||||
$lang->struct->addSubField = 'Add Subfield';
|
||||
|
||||
@@ -94,36 +94,36 @@ $lang->struct->typeOptions = array(
|
||||
|
||||
/* fields of form */
|
||||
$lang->api->struct = 'Data Structure';
|
||||
$lang->api->structName = 'Structure Name';
|
||||
$lang->api->structName = 'Name';
|
||||
$lang->api->structType = 'Type';
|
||||
$lang->api->structAttr = 'Attribute';
|
||||
$lang->api->structAddedBy = 'Creator';
|
||||
$lang->api->structAddedDate = 'Created Time';
|
||||
$lang->api->name = 'Interface Library Name';
|
||||
$lang->api->baseUrl = 'Base Url';
|
||||
$lang->api->baseUrlDesc = 'Site or path. for example, api.zentao.com or /v1';
|
||||
$lang->api->structAddedBy = 'CreatedBy';
|
||||
$lang->api->structAddedDate = 'Created';
|
||||
$lang->api->name = 'API Library Name';
|
||||
$lang->api->baseUrl = 'Base URL';
|
||||
$lang->api->baseUrlDesc = 'Site or path, e.g., api.zentao.com or /v1.';
|
||||
$lang->api->desc = 'Description';
|
||||
$lang->api->control = 'Access Control';
|
||||
$lang->api->noLib = 'There is no interface library at present。';
|
||||
$lang->api->noApi = 'There is no interface for the time being。';
|
||||
$lang->api->noStruct = 'There is no structure for the time being。';
|
||||
$lang->api->lib = 'Interface Library';
|
||||
$lang->api->apiList = 'Interface List';
|
||||
$lang->api->formTitle = 'Interface Name';
|
||||
$lang->api->noLib = 'No API library yet.';
|
||||
$lang->api->noApi = 'No API yet.';
|
||||
$lang->api->noStruct = 'No API yet.';
|
||||
$lang->api->lib = 'API Library';
|
||||
$lang->api->apiList = 'API List';
|
||||
$lang->api->formTitle = 'API Name';
|
||||
$lang->api->path = 'Request Path';
|
||||
$lang->api->protocol = 'Request Protocol';
|
||||
$lang->api->method = 'Request Method';
|
||||
$lang->api->requestType = 'Request Type';
|
||||
$lang->api->status = 'Development Status';
|
||||
$lang->api->owner = 'Person In Charge';
|
||||
$lang->api->paramsExample = 'Response Example';
|
||||
$lang->api->protocol = 'Protocol';
|
||||
$lang->api->method = 'Method';
|
||||
$lang->api->requestType = 'Type';
|
||||
$lang->api->status = 'Status';
|
||||
$lang->api->owner = 'Owner';
|
||||
$lang->api->paramsExample = 'Request Example';
|
||||
$lang->api->header = 'Request Header';
|
||||
$lang->api->query = 'Request Parameters';
|
||||
$lang->api->query = 'Parameter';
|
||||
$lang->api->params = 'Request Body';
|
||||
$lang->api->response = 'Response';
|
||||
$lang->api->responseExample = 'Response Example';
|
||||
$lang->api->res = new stdClass();
|
||||
$lang->api->res->name = '名称';
|
||||
$lang->api->res->name = 'Name';
|
||||
$lang->api->res->desc = 'Description';
|
||||
$lang->api->res->type = 'Type';
|
||||
$lang->api->req = new stdClass();
|
||||
@@ -195,14 +195,14 @@ $lang->api->allParamsTypeOptions = array_merge($lang->api->paramsTypeOptions, $l
|
||||
$lang->api->requiredOptions = array(0 => 'No', 1 => 'Yes');
|
||||
|
||||
$lang->doclib = new stdclass();
|
||||
$lang->doclib->name = 'Interface Library Name';
|
||||
$lang->doclib->name = 'API Library Name';
|
||||
|
||||
$lang->apistruct = new stdClass();
|
||||
$lang->apistruct->name = 'Structure Name';
|
||||
$lang->apistruct->name = 'Name';
|
||||
|
||||
$lang->api_lib_release = new stdClass();
|
||||
$lang->api_lib_release->version = 'Version';
|
||||
|
||||
$lang->api->error = new stdclass();
|
||||
$lang->api->error->onlySelect = 'SQL interface only allow SELECT query.';
|
||||
$lang->api->error->disabled = 'For security reasons, this feature is disabled. You can go to the config directory and modify the configuration item %s to open this function.';
|
||||
$lang->api->error->onlySelect = 'SQL API only allows SELECT query.';
|
||||
$lang->api->error->disabled = 'For security reasons, this feature is disabled. Go to the config directory and modify the configuration item %s to enable it.';
|
||||
|
||||
@@ -9,6 +9,7 @@ $lang->project = new stdclass();
|
||||
$lang->scrum = new stdclass();
|
||||
$lang->waterfall = new stdclass();
|
||||
$lang->execution = new stdclass();
|
||||
$lang->kanban = new stdclass();
|
||||
$lang->story = new stdclass();
|
||||
$lang->release = new stdclass();
|
||||
$lang->branch = new stdclass();
|
||||
|
||||
+35
-34
@@ -47,6 +47,7 @@ $lang->designedByAIUX = "<a href='https://api.zentao.net/goto.php?item=aiux' cla
|
||||
$lang->reset = 'Reset';
|
||||
$lang->cancel = 'Cancel';
|
||||
$lang->refresh = 'Refresh';
|
||||
$lang->create = 'Create';
|
||||
$lang->edit = 'Edit';
|
||||
$lang->delete = 'Delete';
|
||||
$lang->close = 'Close';
|
||||
@@ -177,38 +178,38 @@ $lang->score->shortCommon = 'Score';
|
||||
$lang->testreport->shortCommon = 'Report';
|
||||
$lang->qa->shortCommon = 'QA';
|
||||
|
||||
$lang->dashboard = 'Dashboard';
|
||||
$lang->contribute = 'Contribute';
|
||||
$lang->dynamic = 'Dynamic';
|
||||
$lang->contact = 'Contacts';
|
||||
$lang->whitelist = 'Whitelist';
|
||||
$lang->roadmap = 'Roadmap';
|
||||
$lang->track = 'Track';
|
||||
$lang->settings = 'Settings';
|
||||
$lang->overview = 'Overview';
|
||||
$lang->module = 'Module';
|
||||
$lang->priv = 'Privilege';
|
||||
$lang->design = 'Design';
|
||||
$lang->other = 'Other';
|
||||
$lang->estimation = 'Estimation';
|
||||
$lang->issue = 'Issue';
|
||||
$lang->risk = 'Risk';
|
||||
$lang->measure = 'Report';
|
||||
$lang->treeView = 'Tree View';
|
||||
$lang->groupView = 'Group View';
|
||||
$lang->kanban = 'Kanban';
|
||||
$lang->burn = 'Burndown';
|
||||
$lang->view = 'View';
|
||||
$lang->intro = 'Introduction';
|
||||
$lang->indexPage = 'Index';
|
||||
$lang->model = 'Model';
|
||||
$lang->redev = 'Develop';
|
||||
$lang->browser = 'Browser';
|
||||
$lang->db = 'Database';
|
||||
$lang->editor = 'Editor';
|
||||
$lang->timezone = 'Timezone';
|
||||
$lang->security = 'Security';
|
||||
$lang->calendar = 'Calendar';
|
||||
$lang->dashboard = 'Dashboard';
|
||||
$lang->contribute = 'Contribute';
|
||||
$lang->dynamic = 'Dynamic';
|
||||
$lang->contact = 'Contacts';
|
||||
$lang->whitelist = 'Whitelist';
|
||||
$lang->roadmap = 'Roadmap';
|
||||
$lang->track = 'Track';
|
||||
$lang->settings = 'Settings';
|
||||
$lang->overview = 'Overview';
|
||||
$lang->module = 'Module';
|
||||
$lang->priv = 'Privilege';
|
||||
$lang->design = 'Design';
|
||||
$lang->other = 'Other';
|
||||
$lang->estimation = 'Estimation';
|
||||
$lang->issue = 'Issue';
|
||||
$lang->risk = 'Risk';
|
||||
$lang->measure = 'Report';
|
||||
$lang->treeView = 'Tree View';
|
||||
$lang->groupView = 'Group View';
|
||||
$lang->executionKanban = 'Kanban';
|
||||
$lang->burn = 'Burndown';
|
||||
$lang->view = 'View';
|
||||
$lang->intro = 'Introduction';
|
||||
$lang->indexPage = 'Index';
|
||||
$lang->model = 'Model';
|
||||
$lang->redev = 'Develop';
|
||||
$lang->browser = 'Browser';
|
||||
$lang->db = 'Database';
|
||||
$lang->editor = 'Editor';
|
||||
$lang->timezone = 'Timezone';
|
||||
$lang->security = 'Security';
|
||||
$lang->calendar = 'Calendar';
|
||||
|
||||
$lang->my->work = 'Work';
|
||||
|
||||
@@ -227,8 +228,8 @@ $lang->doc->api = 'API';
|
||||
$lang->doc->execution = $lang->execution->common;
|
||||
$lang->doc->custom = 'Custom';
|
||||
$lang->doc->wiki = 'WIKI';
|
||||
$lang->doc->apiDoc = 'Doc';
|
||||
$lang->doc->apiStruct = 'Struct';
|
||||
$lang->doc->apiDoc = 'API Docuemnt';
|
||||
$lang->doc->apiStruct = 'Data Structure';
|
||||
|
||||
$lang->product->list = $lang->productCommon . ' List';
|
||||
$lang->product->kanban = $lang->productCommon . ' Kanban';
|
||||
|
||||
@@ -246,7 +246,7 @@ if($config->systemMode == 'new') $lang->execution->homeMenu->executionkanban = a
|
||||
|
||||
$lang->execution->menu = new stdclass();
|
||||
$lang->execution->menu->task = array('link' => "{$lang->task->common}|execution|task|executionID=%s", 'subModule' => 'task,tree', 'alias' => 'importtask,importbug');
|
||||
$lang->execution->menu->kanban = array('link' => "$lang->kanban|execution|kanban|executionID=%s");
|
||||
$lang->execution->menu->kanban = array('link' => "$lang->executionKanban|execution|kanban|executionID=%s");
|
||||
$lang->execution->menu->burn = array('link' => "$lang->burn|execution|burn|executionID=%s");
|
||||
$lang->execution->menu->view = array('link' => "$lang->view|execution|grouptask|executionID=%s", 'alias' => 'grouptask,tree,taskeffort,gantt,calendar,relation,maintainrelation');
|
||||
$lang->execution->menu->story = array('link' => "$lang->SRCommon|execution|story|executionID=%s", 'subModule' => 'story', 'alias' => 'batchcreate,linkstory,storykanban');
|
||||
@@ -521,6 +521,7 @@ $lang->navGroup->build = 'project';
|
||||
$lang->navGroup->measrecord = 'project';
|
||||
$lang->navGroup->milestone = 'project';
|
||||
|
||||
$lang->navGroup->kanban = 'execution';
|
||||
$lang->navGroup->execution = 'execution';
|
||||
$lang->navGroup->task = 'execution';
|
||||
$lang->navGroup->build = 'execution';
|
||||
|
||||
@@ -47,6 +47,7 @@ $lang->designedByAIUX = "<a href='https://api.zentao.net/goto.php?item=aiux' cla
|
||||
$lang->reset = '重填';
|
||||
$lang->cancel = '取消';
|
||||
$lang->refresh = '刷新';
|
||||
$lang->create = '新建';
|
||||
$lang->edit = '编辑';
|
||||
$lang->delete = '删除';
|
||||
$lang->close = '关闭';
|
||||
@@ -177,38 +178,38 @@ $lang->score->shortCommon = '积分';
|
||||
$lang->testreport->shortCommon = '报告';
|
||||
$lang->qa->shortCommon = 'QA';
|
||||
|
||||
$lang->dashboard = '仪表盘';
|
||||
$lang->contribute = '贡献';
|
||||
$lang->dynamic = '动态';
|
||||
$lang->contact = '联系人';
|
||||
$lang->whitelist = '白名单';
|
||||
$lang->roadmap = '路线图';
|
||||
$lang->track = '矩阵';
|
||||
$lang->settings = '设置';
|
||||
$lang->overview = '概况';
|
||||
$lang->module = '模块';
|
||||
$lang->priv = '权限';
|
||||
$lang->design = '设计';
|
||||
$lang->other = '其他';
|
||||
$lang->estimation = '估算';
|
||||
$lang->issue = '问题';
|
||||
$lang->risk = '风险';
|
||||
$lang->measure = '度量';
|
||||
$lang->treeView = '树状图';
|
||||
$lang->groupView = '分组视图';
|
||||
$lang->kanban = '看板';
|
||||
$lang->burn = '燃尽图';
|
||||
$lang->view = '视图';
|
||||
$lang->intro = '介绍';
|
||||
$lang->indexPage = '首页';
|
||||
$lang->model = '模型';
|
||||
$lang->redev = '二次开发';
|
||||
$lang->browser = '浏览器';
|
||||
$lang->db = '数据库';
|
||||
$lang->editor = '编辑器';
|
||||
$lang->timezone = '时区';
|
||||
$lang->security = '安全';
|
||||
$lang->calendar = '日程';
|
||||
$lang->dashboard = '仪表盘';
|
||||
$lang->contribute = '贡献';
|
||||
$lang->dynamic = '动态';
|
||||
$lang->contact = '联系人';
|
||||
$lang->whitelist = '白名单';
|
||||
$lang->roadmap = '路线图';
|
||||
$lang->track = '矩阵';
|
||||
$lang->settings = '设置';
|
||||
$lang->overview = '概况';
|
||||
$lang->module = '模块';
|
||||
$lang->priv = '权限';
|
||||
$lang->design = '设计';
|
||||
$lang->other = '其他';
|
||||
$lang->estimation = '估算';
|
||||
$lang->issue = '问题';
|
||||
$lang->risk = '风险';
|
||||
$lang->measure = '度量';
|
||||
$lang->treeView = '树状图';
|
||||
$lang->groupView = '分组视图';
|
||||
$lang->executionKanban = '看板';
|
||||
$lang->burn = '燃尽图';
|
||||
$lang->view = '视图';
|
||||
$lang->intro = '介绍';
|
||||
$lang->indexPage = '首页';
|
||||
$lang->model = '模型';
|
||||
$lang->redev = '二次开发';
|
||||
$lang->browser = '浏览器';
|
||||
$lang->db = '数据库';
|
||||
$lang->editor = '编辑器';
|
||||
$lang->timezone = '时区';
|
||||
$lang->security = '安全';
|
||||
$lang->calendar = '日程';
|
||||
|
||||
$lang->my->work = '待处理';
|
||||
|
||||
|
||||
@@ -177,37 +177,37 @@ $lang->score->shortCommon = '積分';
|
||||
$lang->testreport->shortCommon = '報告';
|
||||
$lang->qa->shortCommon = 'QA';
|
||||
|
||||
$lang->dashboard = '儀表盤';
|
||||
$lang->contribute = '貢獻';
|
||||
$lang->dynamic = '動態';
|
||||
$lang->contact = '聯繫人';
|
||||
$lang->whitelist = '白名單';
|
||||
$lang->roadmap = '路線圖';
|
||||
$lang->track = '矩陣';
|
||||
$lang->settings = '設置';
|
||||
$lang->overview = '概況';
|
||||
$lang->module = '模組';
|
||||
$lang->priv = '權限';
|
||||
$lang->design = '設計';
|
||||
$lang->other = '其他';
|
||||
$lang->estimation = '估算';
|
||||
$lang->issue = '問題';
|
||||
$lang->risk = '風險';
|
||||
$lang->measure = '度量';
|
||||
$lang->treeView = '樹狀圖';
|
||||
$lang->groupView = '分組視圖';
|
||||
$lang->kanban = '看板';
|
||||
$lang->burn = '燃盡圖';
|
||||
$lang->view = '視圖';
|
||||
$lang->intro = '介紹';
|
||||
$lang->indexPage = '首頁';
|
||||
$lang->model = '模型';
|
||||
$lang->redev = '二次開發';
|
||||
$lang->browser = '瀏覽器';
|
||||
$lang->db = '資料庫';
|
||||
$lang->editor = '編輯器';
|
||||
$lang->timezone = '時區';
|
||||
$lang->security = '安全';
|
||||
$lang->dashboard = '儀表盤';
|
||||
$lang->contribute = '貢獻';
|
||||
$lang->dynamic = '動態';
|
||||
$lang->contact = '聯繫人';
|
||||
$lang->whitelist = '白名單';
|
||||
$lang->roadmap = '路線圖';
|
||||
$lang->track = '矩陣';
|
||||
$lang->settings = '設置';
|
||||
$lang->overview = '概況';
|
||||
$lang->module = '模組';
|
||||
$lang->priv = '權限';
|
||||
$lang->design = '設計';
|
||||
$lang->other = '其他';
|
||||
$lang->estimation = '估算';
|
||||
$lang->issue = '問題';
|
||||
$lang->risk = '風險';
|
||||
$lang->measure = '度量';
|
||||
$lang->treeView = '樹狀圖';
|
||||
$lang->groupView = '分組視圖';
|
||||
$lang->executionKanban = '看板';
|
||||
$lang->burn = '燃盡圖';
|
||||
$lang->view = '視圖';
|
||||
$lang->intro = '介紹';
|
||||
$lang->indexPage = '首頁';
|
||||
$lang->model = '模型';
|
||||
$lang->redev = '二次開發';
|
||||
$lang->browser = '瀏覽器';
|
||||
$lang->db = '資料庫';
|
||||
$lang->editor = '編輯器';
|
||||
$lang->timezone = '時區';
|
||||
$lang->security = '安全';
|
||||
$lang->calendar = '日程';
|
||||
|
||||
$lang->my->work = '待處理';
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
#kanbanList .kanban-header-col[data-type="doingProject"] + .kanban-header-col[data-type="doingExecution"] {padding: 38px 10px 0;}
|
||||
#kanbanList .kanban-header-col[data-type="doingProject"]:after {content: attr(data-span-text); display: block; position: absolute; z-index: 10; left: 0; right: -100%; right: calc(-100% - 2px); top: 0; line-height: 36px; text-align: center; font-weight: bold; border-bottom: 2px solid #fff; background-color: #ededed;}
|
||||
#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-lane-items {height: 100%; display: flex; flex-direction: column; justify-content: center;}
|
||||
#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item {background-color: transparent; border: none; padding: 0; text-align: center;}
|
||||
#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item {background-color: transparent; border: none; padding: 0; text-align: center; box-shadow: none!important;}
|
||||
#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item:hover {box-shadow: none;}
|
||||
#kanbanList .kanban-col[data-type="unclosedProduct"] .kanban-item > .title {white-space: normal;}
|
||||
#kanbanList .kanban-col[data-type="normalRelease"] .kanban-item > .title {display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center; height: 38px;}
|
||||
|
||||
@@ -8,11 +8,11 @@ $lang->datatable->reset = 'Reset';
|
||||
|
||||
$lang->datatable->custom = 'Customize Columns';
|
||||
$lang->datatable->customTip = 'Check columns to display and drag columns to order.';
|
||||
$lang->datatable->switchToTable = 'Switch to Common Table';
|
||||
$lang->datatable->switchToDatatable = 'Switch to Advanced Table';
|
||||
$lang->datatable->switchToTable = 'Common Table';
|
||||
$lang->datatable->switchToDatatable = 'Advanced Table';
|
||||
$lang->datatable->required = 'Required';
|
||||
$lang->datatable->confirmReset = 'Do you want to restore the default settings?';
|
||||
$lang->datatable->setGlobal = 'Set as Global';
|
||||
$lang->datatable->setGlobal = 'Set Global';
|
||||
$lang->datatable->resetGlobal = 'Global Reset';
|
||||
|
||||
$lang->datatable->branch = 'Branch';
|
||||
|
||||
@@ -29,12 +29,12 @@ $lang->doc->files = 'Files';
|
||||
$lang->doc->addedBy = 'Author';
|
||||
$lang->doc->addedDate = 'Added';
|
||||
$lang->doc->editedBy = 'UpdatedBy';
|
||||
$lang->doc->editedDate = 'UpdatedDate';
|
||||
$lang->doc->editedDate = 'Updated';
|
||||
$lang->doc->version = 'Version';
|
||||
$lang->doc->basicInfo = 'Basic Info';
|
||||
$lang->doc->basicInfo = 'Basic Information';
|
||||
$lang->doc->deleted = 'Deleted';
|
||||
$lang->doc->fileObject = 'Dependent Item';
|
||||
$lang->doc->whiteList = 'White List';
|
||||
$lang->doc->whiteList = 'Whitelist';
|
||||
$lang->doc->contentType = 'Format';
|
||||
$lang->doc->separator = "<i class='icon-angle-right'></i>";
|
||||
$lang->doc->fileTitle = 'File Name';
|
||||
@@ -51,7 +51,7 @@ $lang->doc->item = ' Items';
|
||||
$lang->doc->num = 'Documents';
|
||||
$lang->doc->searchResult = 'Search Result';
|
||||
$lang->doc->mailto = 'Mailto';
|
||||
$lang->doc->noModule = 'No document in this lib, please create it';
|
||||
$lang->doc->noModule = 'No document in this library. Create one.';
|
||||
$lang->doc->noChapter = 'No chapters or articles in this book. Please add chapters and articles.';
|
||||
$lang->doc->views = 'Views';
|
||||
$lang->doc->draft = 'Draft';
|
||||
@@ -73,7 +73,7 @@ $lang->doc->todayEdited = 'Updated Today';
|
||||
$lang->doc->pastEdited = 'Total Updated';
|
||||
$lang->doc->myDoc = 'My Documents';
|
||||
$lang->doc->myCollection = 'My Favorites';
|
||||
$lang->doc->tableContents = 'Catalog';
|
||||
$lang->doc->tableContents = 'Directory';
|
||||
|
||||
/* Methods list */
|
||||
$lang->doc->index = 'Document Home';
|
||||
@@ -91,12 +91,12 @@ $lang->doc->manageType = 'Manage Category';
|
||||
$lang->doc->editType = 'Edit';
|
||||
$lang->doc->deleteType = 'Delete';
|
||||
$lang->doc->addType = 'Add';
|
||||
$lang->doc->childType = 'Catalog';
|
||||
$lang->doc->catalogName = 'Catalog Name';
|
||||
$lang->doc->childType = 'Directory';
|
||||
$lang->doc->catalogName = 'Name';
|
||||
$lang->doc->collect = 'Add Favorite';
|
||||
$lang->doc->cancelCollection = 'Remove Favorite';
|
||||
$lang->doc->deleteFile = 'Delete File';
|
||||
$lang->doc->menuTitle = 'Menu';
|
||||
$lang->doc->menuTitle = 'Direcotory';
|
||||
|
||||
$lang->doc->collectAction = 'Add Favorite';
|
||||
|
||||
|
||||
@@ -1825,7 +1825,7 @@ class execution extends control
|
||||
* Kanban.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @param string $type
|
||||
* @param string $browseType story|bug|task|all
|
||||
* @param string $orderBy
|
||||
* @param string $groupBy
|
||||
* @access public
|
||||
@@ -1841,11 +1841,11 @@ class execution extends control
|
||||
/* Compatibility IE8. */
|
||||
if(strpos($this->server->http_user_agent, 'MSIE 8.0') !== false) header("X-UA-Compatible: IE=EmulateIE7");
|
||||
|
||||
$kanbanGroup = $this->loadModel('kanban')->getExecutionKanban($executionID, $type, $groupBy);
|
||||
$kanbanGroup = $this->loadModel('kanban')->getExecutionKanban($executionID, $browseType);
|
||||
if(empty($kanbanGroup))
|
||||
{
|
||||
$this->kanban->createLanes($executionID, $type, $groupBy);
|
||||
$kanbanGroup = $this->kanban->getExecutionKanban($executionID);
|
||||
$this->kanban->createLanes($executionID);
|
||||
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType);
|
||||
}
|
||||
|
||||
$this->execution->setMenu($executionID);
|
||||
|
||||
@@ -1,14 +1,30 @@
|
||||
#main > .container {max-width: 1960px!important; padding: 0 10px}
|
||||
|
||||
.kanban + .kanban {margin-top: 15px}
|
||||
.kanban-item {position: relative;}
|
||||
.kanban-item > .title { display: block;white-space: nowrap; overflow: hidden; text-overflow: ellipsis}
|
||||
.kanban-item > .infos {position: relative; margin-top: 5px}
|
||||
.kanban-item > .infos > .info + .info {margin-left: 10px}
|
||||
.kanban-item > .infos > .info + .info {margin-left: 8px}
|
||||
.kanban-item > .infos > .info-id,
|
||||
.kanban-item > .infos > .info-deadline,
|
||||
.kanban-item > .infos > .info-estimate {font-size: 12px; position: relative; top: 2px}
|
||||
.kanban-item > .infos > .label-pri {min-width: 16px; line-height: 14px; height: 16px; padding: 0}
|
||||
.kanban-item > .infos > .label-severity {transform: scale(.75)}
|
||||
.kanban-item > .infos > .avatar {position: absolute; right: 0; top: 0}
|
||||
.kanban-item > .actions {position: absolute; top: 4px; right: 4px; opacity: 0;}
|
||||
.kanban-item:hover > .actions {opacity: 1;}
|
||||
.kanban-item > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
|
||||
.kanban-item > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
|
||||
|
||||
.kanban-header-col > .actions {padding-top: 22px}
|
||||
.kanban-header-parent-col .kanban-header-col > .actions {padding-top: 6px; padding-right: 2px;}
|
||||
.kanban-header-col > .actions > a {display: block; float: left; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 4px; opacity: .7;}
|
||||
.kanban-header-col > .actions > a:hover {background-color: rgba(0,0,0,.075); opacity: 1;}
|
||||
|
||||
.kanban-lane-name > .actions {position: absolute; top: 0; left: 0; right: 0; opacity: 0;}
|
||||
.kanban-lane-name:hover > .actions {opacity: 1;}
|
||||
.kanban-lane-name > .actions > a {display: block; width: 20px; height: 20px; line-height: 20px; text-align: center; opacity: .7; color: #fff;}
|
||||
.kanban-lane-name > .actions > a:hover {background-color: rgba(0,0,0,.2); opacity: 1;}
|
||||
|
||||
.kanban-affixed .kanban-header-col > .title > .text,
|
||||
.kanban-affixed .kanban-header-col > .title > .count {color: #fff!important;}
|
||||
|
||||
+312
-368
@@ -1,371 +1,45 @@
|
||||
/**
|
||||
* Get story demo kanban data
|
||||
* 获取“软件需求”看板演示数据
|
||||
* @returns {Object} Kanban data
|
||||
* @todo 该方法作为演示,应该在最终版本中移除
|
||||
*/
|
||||
function getStoryKanbanDemoData()
|
||||
function changeView(view)
|
||||
{
|
||||
/* Define kanban columns 定义看板列 */
|
||||
var columns =
|
||||
[
|
||||
/* 看板列定义数据结构:
|
||||
id: 列 ID(确保在页面上唯一),
|
||||
type: 类型,
|
||||
name: 显示的名称,
|
||||
color: 列名称颜色,
|
||||
parentType: 所属的父级列类型
|
||||
asParent: 是否作为父级列
|
||||
cardType: 看板卡片类型,例如:'story'(需求)
|
||||
limit: 最大数目,该列上允许展示的卡片最大数目,如果为 0 表示无限制 */
|
||||
{id: 'story-blacklog', type: 'blacklog', name: 'Blacklog', color: '', limit: 0},
|
||||
{id: 'story-ready', type: 'ready', name: '准备好', color: '', limit: 4},
|
||||
{id: 'story-dev', type: 'dev', name: '开发', color: '', limit: 4, asParent: true},
|
||||
{id: 'story-dev-doing', type: 'dev-doing', name: '进行中', color: '#126eed', limit: 2, parentType: 'dev'},
|
||||
{id: 'story-dev-done', type: 'dev-done', name: '完成', color: '#2fab8e', limit: 2, parentType: 'dev'},
|
||||
{id: 'story-test', type: 'test', name: '测试', color: '', limit: 4, asParent: true},
|
||||
{id: 'story-test-doing', type: 'test-doing', name: '进行中', color: '#126eed', limit: 2, parentType: 'test'},
|
||||
{id: 'story-test-done', type: 'test-done', name: '完成', color: '#2fab8e', limit: 2, parentType: 'test'},
|
||||
{id: 'story-accepted', type: 'accepted', name: '已验收', color: '', limit: 0},
|
||||
{id: 'story-published', type: 'published', name: '已发布', color: '', limit: 0},
|
||||
];
|
||||
|
||||
/* Define cards in blacklog column 定义 Blacklog 列上的卡片 */
|
||||
var blacklogColCards =
|
||||
[
|
||||
/* 需求数据结构:
|
||||
id: ID,通常为需求 ID,
|
||||
title: 名称,
|
||||
order: 显示顺序,如果指定为 0,则按 ID 倒序排序,
|
||||
pri: 优先级,
|
||||
estimate: 预估时间,
|
||||
assignedTo: 指派给,
|
||||
deadline: 截止日期 */
|
||||
{id: 10010, title: '第一条 Blacklog', order: 1, pri: 1, estimate: 2, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10011, title: '文档模块的实现', order: 2, pri: 2, estimate: 0, assignedTo: 'sunhao'},
|
||||
{id: 10012, title: '实现软件需求看板视图中不同分组条件的看板展示方式', order: 3, pri: 3, estimate: 10, assignedTo: 'admin'},
|
||||
{id: 10013, title: 'Blacklog 3', order: 4, pri: 0, estimate: 0, assignedTo: 'sunhao'},
|
||||
{id: 10014, title: '实现泳道的更多操作菜单', order: 5, pri: 0, estimate: 0, assignedTo: 'sunhao'},
|
||||
];
|
||||
/* Define cards in ready column 定义 准备好 列上的卡片 */
|
||||
var readyColCards =
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColCards 定义 */
|
||||
{id: 10020, title: '在看板列中实现创建任务功能', order: 10, pri: 1, estimate: 2, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10021, title: '实现泳道的上下移动', order: 11, pri: 2, estimate: 0, assignedTo: 'sunhao'},
|
||||
{id: 10024, title: '实现泳道的更多操作菜单', order: 15, pri: 0, estimate: 0, assignedTo: 'sunhao'},
|
||||
];
|
||||
var devDoingColCards = /* Define cards in dev/doing column 定义 开发/进行中 列上的卡片 */
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColCards 定义 */
|
||||
{id: 10030, title: '实现看板的更多操作菜单', order: 10, pri: 3, estimate: 0, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10031, title: '实现卡片拖动效果功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
var devDoneColCards = /* Define cards in dev/done column 定义 开发/完成 列上的卡片 */
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColCards 定义 */
|
||||
{id: 10040, title: '实现看板列卡片排序功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
];
|
||||
var testDoingColCards = /* Define cards in test/doing column 定义 测试/进行中 列上的卡片 */
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColCards 定义 */
|
||||
{id: 10050, title: '实现看板列的更多操作菜单', order: 10, pri: 3, estimate: 0, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10051, title: '实现Bug卡片的更多操作功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
var testDoneColCards = /* Define cards in test/done column 定义 测试/完成 列上的卡片 */
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColCards 定义 */
|
||||
{id: 10060, title: '实现任务卡片的更多操作功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
];
|
||||
var acceptedColCards =
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColCards 定义 */
|
||||
{id: 10070, title: '实现执行看板的新建按钮组功能', order: 10, pri: 3, estimate: 12, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10071, title: '在看板列中实现提交Bug功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
|
||||
{id: 10072, title: '在看板列中实现需求的添加和关联功能', order: 10, pri: 3, estimate: 10, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10073, title: '实现任务看板视图中不同分组条件的看板展示方式', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
var publishedColCards =
|
||||
[
|
||||
/* 需求数据结构参见 blacklogColCards 定义 */
|
||||
{id: 10080, title: '实现任务看板视图中泳道分组下拉菜单功能', order: 10, pri: 3, estimate: 2, assignedTo: 'admin'},
|
||||
];
|
||||
|
||||
/* Define kanban cards in lane 定义看板泳道内每一列的卡片,属性名为看板列类型,属性值为卡片列表 */
|
||||
var cards =
|
||||
{
|
||||
/* 看板列类型: 卡片列表 */
|
||||
blacklog: blacklogColCards,
|
||||
ready: readyColCards,
|
||||
'dev-doing': devDoingColCards,
|
||||
'dev-done': devDoneColCards,
|
||||
'test-doing': testDoingColCards,
|
||||
'test-done': testDoneColCards,
|
||||
accepted: acceptedColCards,
|
||||
published: publishedColCards,
|
||||
};
|
||||
|
||||
/* Define kanban lanes 定义看板泳道 */
|
||||
var lanes =
|
||||
[
|
||||
/* 泳道数据结构:
|
||||
id: 泳道 ID,确保页面上唯一,可以为数字或字符串,
|
||||
name: 名称,
|
||||
cards: 定义每列上的卡片
|
||||
color: 泳道名称背景色
|
||||
defaultCardType: 看板泳道上的卡片默认类型,例如:'story'(需求) */
|
||||
{id: 'story', name: '软件需求', cards: cards, color: '#3dc6fc', defaultCardType: 'story'},
|
||||
]
|
||||
|
||||
/* Return kanban data 返回看板数据 */
|
||||
/* 看板数据结构:
|
||||
id: ID,确保页面上唯一,可以为数字或字符串,
|
||||
columns: 定义看板上的所有列,
|
||||
lanes: 定义看板上的所有泳道
|
||||
defaultCardType: 看板上的卡片默认类型,例如:'story'(需求) */
|
||||
return kanbanGroup.story;
|
||||
return {id: 'story', columns: columns, lanes: lanes, defaultCardType: 'story'};
|
||||
var link = createLink('execution', 'kanban', "executionID=" + executionID + '&type=' + view);
|
||||
location.href = link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bug demo kanban data
|
||||
* 获取“Bug”看板演示数据
|
||||
* @returns {Object} Kanban data
|
||||
* @todo 该方法作为演示,应该在最终版本中移除
|
||||
*/
|
||||
function getBugKanbanDemoData()
|
||||
{
|
||||
return kanbanGroup.bug;
|
||||
/* Define kanban columns 定义看板列 */
|
||||
var columns =
|
||||
[
|
||||
/* 看板列定义数据结构:
|
||||
id: 列 ID(确保在页面上唯一),
|
||||
type: 类型,
|
||||
name: 显示的名称,
|
||||
color: 列名称颜色,
|
||||
parentType: 所属的父级列类型
|
||||
asParent: 是否作为父级列
|
||||
cardType: 看板卡片类型,例如:'story'(需求)
|
||||
limit: 最大数目,该列上允许展示的卡片最大数目,如果为 0 表示无限制 */
|
||||
{id: 'bug-wait', type: 'wait', name: '待确认', color: '', limit: 0},
|
||||
{id: 'bug-confirmed', type: 'confirmed', name: '已确认', color: '', limit: 4},
|
||||
{id: 'bug-resolving', type: 'resolving', name: '解决中', color: '', limit: 4, asParent: true},
|
||||
{id: 'bug-resolving-doing', type: 'resolving-doing',name: '进行中', color: '#126eed', limit: 2, parentType: 'resolving'},
|
||||
{id: 'bug-resolving-done', type: 'resolving-done', name: '完成', color: '#2fab8e', limit: 2, parentType: 'resolving'},
|
||||
{id: 'bug-test', type: 'test', name: '测试', color: '', limit: 4, asParent: true},
|
||||
{id: 'bug-test-doing', type: 'test-doing', name: '测试中', color: '#126eed', limit: 2, parentType: 'test'},
|
||||
{id: 'bug-test-done', type: 'test-done', name: '测试完毕', color: '#2fab8e', limit: 2, parentType: 'test'},
|
||||
{id: 'bug-closed', type: 'closed', name: '已关闭', color: '', limit: 0},
|
||||
];
|
||||
|
||||
/* Define cards in wait column 定义 待确认 列上的卡片 */
|
||||
var waitColCards =
|
||||
[
|
||||
/* bug 数据结构:
|
||||
id: ID,通常为 Bug ID,
|
||||
title: 名称,
|
||||
order: 显示顺序,如果指定为 0,则按 ID 倒序排序,
|
||||
pri: 优先级,
|
||||
severity: 紧急程度,
|
||||
assignedTo: 指派给 */
|
||||
{id: 10010, title: '在看板列中实现创建任务功能', order: 10, pri: 1, severity: 2, assignedTo: 'admin'},
|
||||
{id: 10011, title: '实现泳道的上下移动', order: 11, pri: 2, severity: 1, assignedTo: 'sunhao'},
|
||||
{id: 10014, title: '实现泳道的更多操作菜单', order: 15, pri: 0, severity: 1, assignedTo: 'sunhao'},
|
||||
];
|
||||
/* Define cards in confirmed column 定义 已确认 列上的卡片 */
|
||||
var confirmedColCards =
|
||||
[
|
||||
/* Bug 数据结构参见 waitColCards 定义 */
|
||||
{id: 10020, title: '在看板列中实现创建任务功能', order: 10, pri: 1, severity: 2, assignedTo: 'admin'},
|
||||
{id: 10021, title: '实现泳道的上下移动', order: 11, pri: 2, severity: 1, assignedTo: 'sunhao'},
|
||||
{id: 10024, title: '实现泳道的更多操作菜单', order: 15, pri: 0, severity: 1, assignedTo: 'sunhao'},
|
||||
];
|
||||
var resolvingDoingColCards = /* Define cards in resolving/doing column 定义 开发/进行中 列上的卡片 */
|
||||
[
|
||||
/* Bug 数据结构参见 waitColCards 定义 */
|
||||
{id: 10030, title: '实现看板的更多操作菜单', order: 10, pri: 3, severity: 1, assignedTo: 'admin'},
|
||||
{id: 10031, title: '实现卡片拖动效果功能', order: 11, pri: 2, severity: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
var resolvingDoneColCards = /* Define cards in resolving/done column 定义 开发/完成 列上的卡片 */
|
||||
[
|
||||
/* Bug 数据结构参见 waitColCards 定义 */
|
||||
{id: 10040, title: '实现看板列卡片排序功能', order: 10, pri: 1, severity: 4, assignedTo: 'admin'},
|
||||
];
|
||||
var testDoingColCards = /* Define cards in test/doing column 定义 测试/进行中 列上的卡片 */
|
||||
[
|
||||
/* Bug 数据结构参见 waitColCards 定义 */
|
||||
{id: 10050, title: '实现看板列的更多操作菜单', order: 10, pri: 3, severity: 3, assignedTo: 'admin'},
|
||||
{id: 10051, title: '实现Bug卡片的更多操作功能', order: 11, pri: 2, severity: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
var testDoneColCards = /* Define cards in test/done column 定义 测试/完成 列上的卡片 */
|
||||
[
|
||||
/* Bug 数据结构参见 waitColCards 定义 */
|
||||
{id: 10060, title: '实现任务卡片的更多操作功能', order: 10, pri: 1, severity: 4, assignedTo: 'admin'},
|
||||
];
|
||||
var closedColCards =
|
||||
[
|
||||
/* Bug 数据结构参见 waitColCards 定义 */
|
||||
{id: 10070, title: '实现执行看板的新建按钮组功能', order: 10, pri: 3, severity: 3, assignedTo: 'admin'},
|
||||
{id: 10071, title: '在看板列中实现提交Bug功能', order: 11, pri: 2, severity: 2, assignedTo: 'sunhao'},
|
||||
{id: 10072, title: '在看板列中实现需求的添加和关联功能', order: 10, pri: 3, severity: 3, assignedTo: 'admin'},
|
||||
{id: 10073, title: '实现任务看板视图中不同分组条件的看板展示方式', order: 11, pri: 2, severity: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
|
||||
/* Define kanban cards in lane 定义看板泳道内每一列的卡片,属性名为看板列类型,属性值为卡片列表 */
|
||||
var cards =
|
||||
{
|
||||
/* 看板列类型: 卡片列表 */
|
||||
wait: waitColCards,
|
||||
confirmed: confirmedColCards,
|
||||
'resolving-doing': resolvingDoingColCards,
|
||||
'resolving-done': resolvingDoneColCards,
|
||||
'test-doing': testDoingColCards,
|
||||
'test-done': testDoneColCards,
|
||||
closed: closedColCards,
|
||||
};
|
||||
|
||||
/* Define kanban lanes 定义看板泳道 */
|
||||
var lanes =
|
||||
[
|
||||
/* 泳道数据结构:
|
||||
id: 泳道 ID,确保页面上唯一,可以为数字或字符串,
|
||||
name: 名称,
|
||||
cards: 定义每列上的卡片
|
||||
color: 泳道名称背景色
|
||||
defaultCardType: 看板泳道上的卡片默认类型,例如:'bug'(Bug) */
|
||||
{id: 'bug', name: 'Bug', cards: cards, color: '#9c30b0', defaultCardType: 'bug'},
|
||||
]
|
||||
|
||||
/* Return kanban data 返回看板数据 */
|
||||
/* 看板数据结构:
|
||||
id: ID,确保页面上唯一,可以为数字或字符串,
|
||||
columns: 定义看板上的所有列,
|
||||
lanes: 定义看板上的所有泳道
|
||||
defaultCardType: 看板上的卡片默认类型,例如:'bug'(Bug) */
|
||||
return {id: 'bug', columns: columns, lanes: lanes, defaultCardType: 'bug'};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get task demo kanban data
|
||||
* 获取“任务”看板演示数据
|
||||
* @returns {Object} Kanban data
|
||||
* @todo 该方法作为演示,应该在最终版本中移除
|
||||
*/
|
||||
function getTaskKanbanDemoData()
|
||||
{
|
||||
return kanbanGroup.task;
|
||||
/* Define kanban columns 定义看板列 */
|
||||
var columns =
|
||||
[
|
||||
/* 看板列定义数据结构:
|
||||
id: 列 ID(确保在页面上唯一),
|
||||
type: 类型,
|
||||
name: 显示的名称,
|
||||
color: 列名称颜色,
|
||||
parentType: 所属的父级列类型
|
||||
asParent: 是否作为父级列
|
||||
cardType: 看板卡片类型,例如:'task'(任务)
|
||||
limit: 最大数目,该列上允许展示的卡片最大数目,如果为 0 表示无限制 */
|
||||
{id: 'task-wait', type: 'wait', name: '未开始', color: '', limit: 0},
|
||||
{id: 'task-dev', type: 'dev', name: '开发', color: '', limit: 4, asParent: true},
|
||||
{id: 'task-dev-doing', type: 'dev-doing', name: '研发中', color: '#126eed', limit: 2, parentType: 'dev'},
|
||||
{id: 'task-dev-done', type: 'dev-done', name: '研发完成', color: '#2fab8e', limit: 2, parentType: 'dev'},
|
||||
{id: 'task-pause', type: 'pause', name: '已暂停', color: '', limit: 0},
|
||||
{id: 'task-cancel', type: 'cancel', name: '已取消', color: '', limit: 0},
|
||||
{id: 'task-closed', type: 'closed', name: '已关闭', color: '', limit: 0},
|
||||
];
|
||||
|
||||
/* Define cards in wait column 定义 未开始 列上的卡片 */
|
||||
var waitColCards =
|
||||
[
|
||||
/* 任务数据结构:
|
||||
id: ID,通常为任务 ID,
|
||||
name: 名称,
|
||||
order: 显示顺序,如果指定为 0,则按 ID 倒序排序,
|
||||
pri: 优先级,
|
||||
estimate: 预估时间,
|
||||
assignedTo: 指派给,
|
||||
deadline: 截止日期 */
|
||||
{id: 10020, name: '在看板列中实现创建任务功能', order: 10, pri: 1, estimate: 2, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10021, name: '实现泳道的上下移动', order: 11, pri: 2, estimate: 0, assignedTo: 'sunhao', deadline: '2021-10-22'},
|
||||
{id: 10024, name: '实现泳道的更多操作菜单', order: 15, pri: 0, estimate: 0, assignedTo: 'sunhao'},
|
||||
];
|
||||
var devDoingColCards = /* Define cards in dev/doing column 定义 开发/进行中 列上的卡片 */
|
||||
[
|
||||
/* 任务数据结构参见 waitColCards 定义 */
|
||||
{id: 10030, name: '实现看板的更多操作菜单', order: 10, pri: 3, estimate: 0, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10031, name: '实现卡片拖动效果功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
var devDoneColCards = /* Define cards in dev/done column 定义 开发/完成 列上的卡片 */
|
||||
[
|
||||
/* 任务数据结构参见 waitColCards 定义 */
|
||||
{id: 10040, name: '实现看板列卡片排序功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
];
|
||||
var pauseColCards = /* Define cards in pause column 定义 已暂停 列上的卡片 */
|
||||
[
|
||||
/* 任务数据结构参见 waitColCards 定义 */
|
||||
{id: 10050, name: '实现看板列的更多操作菜单', order: 10, pri: 3, estimate: 0, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10051, name: '实现Bug卡片的更多操作功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
var cancelColCards = /* Define cards in cancel column 定义 已取消 列上的卡片 */
|
||||
[
|
||||
/* 任务数据结构参见 waitColCards 定义 */
|
||||
{id: 10060, name: '实现任务卡片的更多操作功能', order: 10, pri: 1, estimate: 4, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
];
|
||||
var closedColCards =
|
||||
[
|
||||
/* 任务数据结构参见 waitColCards 定义 */
|
||||
{id: 10070, name: '实现执行看板的新建按钮组功能', order: 10, pri: 3, estimate: 12, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10071, name: '在看板列中实现提交Bug功能', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
|
||||
{id: 10072, name: '在看板列中实现任务的添加和关联功能', order: 10, pri: 3, estimate: 10, assignedTo: 'admin', deadline: '2021-10-30'},
|
||||
{id: 10073, name: '实现任务看板视图中不同分组条件的看板展示方式', order: 11, pri: 2, estimate: 2, assignedTo: 'sunhao'},
|
||||
];
|
||||
|
||||
/* Define kanban cards in lane 定义看板泳道内每一列的卡片,属性名为看板列类型,属性值为卡片列表 */
|
||||
var cards =
|
||||
{
|
||||
/* 看板列类型: 卡片列表 */
|
||||
wait: waitColCards,
|
||||
'dev-doing': devDoingColCards,
|
||||
'dev-done': devDoneColCards,
|
||||
pause: pauseColCards,
|
||||
cancel: cancelColCards,
|
||||
closed: closedColCards,
|
||||
};
|
||||
|
||||
/* Define kanban lanes 定义看板泳道 */
|
||||
var lanes =
|
||||
[
|
||||
/* 泳道数据结构:
|
||||
id: 泳道 ID,确保页面上唯一,可以为数字或字符串,
|
||||
name: 名称,
|
||||
cards: 定义每列上的卡片
|
||||
color: 泳道名称背景色
|
||||
defaultCardType: 看板泳道上的卡片默认类型,例如:'task'(任务) */
|
||||
{id: 'task', name: '任务', cards: cards, color: '#126eed', defaultCardType: 'task'},
|
||||
]
|
||||
|
||||
/* Return kanban data 返回看板数据 */
|
||||
/* 看板数据结构:
|
||||
id: ID,确保页面上唯一,可以为数字或字符串,
|
||||
columns: 定义看板上的所有列,
|
||||
lanes: 定义看板上的所有泳道
|
||||
defaultCardType: 看板上的卡片默认类型,例如:'task'(任务) */
|
||||
return {id: 'task', columns: columns, lanes: lanes, defaultCardType: 'task'};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render user avatar
|
||||
* @param {String|{account: string, avatar: string}} user User account or user object
|
||||
* @returns {string}
|
||||
*/
|
||||
function renderUserAvatar(user)
|
||||
function renderUserAvatar(user, objectType, objectID)
|
||||
{
|
||||
if(typeof user === 'string') user = {account: user};
|
||||
if(!user.avatar && window.userList && window.userList[user.account]) user = window.userList[user.account];
|
||||
return $('<div class="avatar has-text avatar-sm avatar-circle" />').avatar({user: user});
|
||||
if(objectType == 'task')
|
||||
{
|
||||
var link = createLink('task', 'assignto', 'executionID=' + executionID + '&id=' + objectID, '', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var link = createLink(objectType, 'assignto', 'id=' + objectID, '', true);
|
||||
}
|
||||
|
||||
return $('<a class="avatar has-text avatar-sm avatar-circle iframe" href="' + link + '"/>').avatar({user: user});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render deadline
|
||||
* @param {String|Date} deadline Deadline
|
||||
* @returns {JQuery}
|
||||
*/
|
||||
function renderDeadline(deadline)
|
||||
{
|
||||
var date = $.zui.createDate(deadline);
|
||||
var now = new Date();
|
||||
now.setHours(0);
|
||||
now.setMinutes(0);
|
||||
now.setSeconds(0);
|
||||
now.setMilliseconds(0);
|
||||
var isEarlyThanToday = date.getTime() < now.getTime();
|
||||
return $('<span class="info info-deadline"/>').text(deadline).addClass(isEarlyThanToday ? 'text-red' : 'text-muted');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,14 +71,25 @@ function renderStoryItem(item, $item, col)
|
||||
'<span class="info info-pri label-pri label-pri-' + item.pri + '" title="' + item.pri + '">' + item.pri + '</span>',
|
||||
item.estimate ? '<span class="info info-estimate text-muted">' + item.estimate + 'h</span>' : '',
|
||||
].join(''));
|
||||
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo));
|
||||
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo, 'story', item.id));
|
||||
|
||||
var $actions = $item.find('.actions');
|
||||
if(!$actions.length)
|
||||
{
|
||||
$actions = $([
|
||||
'<div class="actions">',
|
||||
'<a data-contextmenu="story" data-col="' + col.type + '">',
|
||||
'<i class="icon icon-ellipsis-v"></i>',
|
||||
'</a>',
|
||||
'</div>'
|
||||
].join('')).appendTo($item);
|
||||
}
|
||||
|
||||
$item.attr('data-type', 'story').addClass('kanban-item-story');
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render bug item 提供方法渲染看板中的 Bug 卡片
|
||||
* @param {Object} item Bug item object
|
||||
@@ -434,7 +119,20 @@ function renderBugItem(item, $item, col)
|
||||
'<span class="info info-severity label-severity" data-severity="' + item.severity + '" title="' + item.severity + '"></span>',
|
||||
'<span class="info info-pri label-pri label-pri-' + item.pri + '" title="' + item.pri + '">' + item.pri + '</span>',
|
||||
].join(''));
|
||||
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo));
|
||||
if(item.deadline) $infos.append(renderDeadline(item.deadline));
|
||||
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo, 'bug', item.id));
|
||||
|
||||
var $actions = $item.find('.actions');
|
||||
if(!$actions.length)
|
||||
{
|
||||
$actions = $([
|
||||
'<div class="actions">',
|
||||
'<a data-contextmenu="bug" data-col="' + col.type + '">',
|
||||
'<i class="icon icon-ellipsis-v"></i>',
|
||||
'</a>',
|
||||
'</div>'
|
||||
].join('')).appendTo($item);
|
||||
}
|
||||
|
||||
$item.attr('data-type', 'bug').addClass('kanban-item-bug');
|
||||
|
||||
@@ -470,7 +168,20 @@ function renderTaskItem(item, $item, col)
|
||||
'<span class="info i nfo-pri label-pri label-pri-' + item.pri + '" title="' + item.pri + '">' + item.pri + '</span>',
|
||||
item.estimate ? '<span class="info info-estimate text-muted">' + item.estimate + 'h</span>' : '',
|
||||
].join(''));
|
||||
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo));
|
||||
if(item.deadline) $infos.append(renderDeadline(item.deadline));
|
||||
if(item.assignedTo) $infos.append(renderUserAvatar(item.assignedTo, 'task', item.id));
|
||||
|
||||
var $actions = $item.find('.actions');
|
||||
if(!$actions.length)
|
||||
{
|
||||
$actions = $([
|
||||
'<div class="actions">',
|
||||
'<a data-contextmenu="task" data-col="' + col.type + '">',
|
||||
'<i class="icon icon-ellipsis-v"></i>',
|
||||
'</a>',
|
||||
'</div>'
|
||||
].join('')).appendTo($item);
|
||||
}
|
||||
|
||||
$item.attr('data-type', 'task').addClass('kanban-item-task');
|
||||
|
||||
@@ -478,7 +189,6 @@ function renderTaskItem(item, $item, col)
|
||||
}
|
||||
|
||||
/* Add column renderer/ 添加特定列类型或列条目类型渲染方法 */
|
||||
/* Add column renderer/ 添加特定列类型或列卡片类型渲染方法 */
|
||||
addColumnRenderer('story', renderStoryItem);
|
||||
addColumnRenderer('bug', renderBugItem);
|
||||
addColumnRenderer('task', renderTaskItem);
|
||||
@@ -492,10 +202,62 @@ addColumnRenderer('task', renderTaskItem);
|
||||
*/
|
||||
function renderColumnCount($count, count, col)
|
||||
{
|
||||
var text = count + '/' + (!col.limit ? '<i class="icon icon-infinite"></i>' : '');
|
||||
var text = count + '/' + (col.limit < 0 ? '<i class="icon icon-infinite"></i>' : col.limit);
|
||||
$count.html(text + '<i class="icon icon-arrow-up"></i>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render header column 渲染看板列头部
|
||||
* @param {JQuery} $col Header column element
|
||||
* @param {Object} col Header column object
|
||||
* @param {JQuery} $header Header element
|
||||
* @param {Object} kanban Kanban object
|
||||
*/
|
||||
function renderHeaderCol($col, col, $header, kanban)
|
||||
{
|
||||
if(col.asParent) $col = $col.children('.kanban-header-col');
|
||||
if(!$col.children('.actions').length)
|
||||
{
|
||||
var $actions = $('<div class="actions" />');
|
||||
if(col.type === 'backlog' || col.type === 'wait' || col.type == 'unconfirmed')
|
||||
{
|
||||
$actions.append([
|
||||
'<a data-contextmenu="columnCreate" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '" class="text-primary">',
|
||||
'<i class="icon icon-expand-alt"></i>',
|
||||
'</a>'
|
||||
].join(''));
|
||||
}
|
||||
$actions.append([
|
||||
'<a data-contextmenu="column" data-type="' + col.type + '" data-kanban="' + kanban.id + '" data-parent="' + (col.parentType || '') + '">',
|
||||
'<i class="icon icon-ellipsis-v"></i>',
|
||||
'</a>'
|
||||
].join(''));
|
||||
$actions.appendTo($col);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render lane name 渲染看板泳道名称
|
||||
* @param {JQuery} $name Name element
|
||||
* @param {Object} lane Lane object
|
||||
* @param {JQuery} $kanban $kanban element
|
||||
* @param {Object} columns Kanban columns
|
||||
* @param {Object} kanban Kanban object
|
||||
*/
|
||||
function renderLaneName($name, lane, $kanban, columns, kanban)
|
||||
{
|
||||
if(!$name.children('.actions').length)
|
||||
{
|
||||
$([
|
||||
'<div class="actions">',
|
||||
'<a data-contextmenu="lane" data-lane="' + lane.id + '" data-kanban="' + kanban.id + '">',
|
||||
'<i class="icon icon-ellipsis-v"></i>',
|
||||
'</a>',
|
||||
'</div>'
|
||||
].join('')).appendTo($name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updata kanban data
|
||||
* 更新看板上的数据
|
||||
@@ -657,6 +419,148 @@ function handleFinishDrop(event)
|
||||
$('#kanbans').find('.can-drop-here').removeClass('can-drop-here');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle sort cards in column 处理对列卡片进行排序
|
||||
*/
|
||||
function handleSortColCards()
|
||||
{
|
||||
/* TODO: handle sort cards from column contextmenu */
|
||||
}
|
||||
|
||||
/**
|
||||
* Create column menu 创建列操作菜单
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
function createColumnMenu(options)
|
||||
{
|
||||
var $col = options.$trigger.closest('.kanban-col');
|
||||
var col = $col.data('col');
|
||||
var kanbanID = options.kanban;
|
||||
|
||||
var items = [];
|
||||
if(priv.hasEditName) items.push({label: executionLang.editName, url: $.createLink('kanban', 'setColumn', 'col=' + col.columnID + '&executionID=' + executionID), className: 'iframe'})
|
||||
if(priv.hasSetWIP) items.push({label: executionLang.setWIP, url: $.createLink('kanban', 'setWIP', 'col=' + col.columnID + '&executionID=' + executionID), className: 'iframe'})
|
||||
items.push({label: executionLang.sortColumn, items: ['按ID倒序', '按ID顺序'], className: 'iframe', onClick: handleSortColCards})
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create column create button menu 创建列添加按钮操作菜单
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
function createColumnCreateMenu(options)
|
||||
{
|
||||
var $col = options.$trigger.closest('.kanban-col');
|
||||
var col = $col.data('col');
|
||||
var items = [];
|
||||
|
||||
if(col.laneType == 'story')
|
||||
{
|
||||
if(priv.canCreateStory) items.push({label: storyLang.create, url: $.createLink('story', 'create', 'productID=' + productID)});
|
||||
if(priv.canBatchCreateStory) items.push({label: storyLang.batchCreate, url: $.createLink('story', 'batchcreate', 'productID=' + productID)});
|
||||
if(priv.canLinkStory) items.push({label: executionLang.linkStory, url: $.createLink('execution', 'linkStory', 'executionID=' + executionID)});
|
||||
if(priv.canLinkStoryByPlane) items.push({label: executionLang.linkStoryByPlan, url: '#linkStoryByPlan', 'attrs' : {'data-toggle': 'modal'}});
|
||||
}
|
||||
else if(col.laneType == 'bug')
|
||||
{
|
||||
if(priv.canCreateBug) items.push({label: bugLang.create, url: $.createLink('bug', 'create', 'productID=0&moduleID=0&extra=executionID=' + executionID)});
|
||||
if(priv.canBatchCreateBug) items.push({label: bugLang.batchCreate, url: $.createLink('bug', 'batchcreate', 'productID=' + productID + '&moduleID=0&executionID=' + executionID)});
|
||||
}
|
||||
else
|
||||
{
|
||||
if(priv.canCreateTask) items.push({label: taskLang.create, url: $.createLink('task', 'create', 'executionID=' + executionID)});
|
||||
if(priv.canBatchCreateTask) items.push({label: taskLang.batchCreate, url: $.createLink('task', 'batchcreate', 'executionID=' + executionID)});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create lane menu 创建泳道操作菜单
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
function createLaneMenu(options)
|
||||
{
|
||||
var $lane = options.$trigger.closest('.kanban-lane');
|
||||
var $kanban = $lane.closest('.kanban');
|
||||
var lane = $lane.data('lane');
|
||||
var kanbanID = options.kanban;
|
||||
var items =
|
||||
[
|
||||
{label: '泳道设置', icon: 'edit', url: $.createLink('kanban', 'laneedit', 'lane=' + lane.id + '&kanban=' + kanbanID), className: 'iframe'},
|
||||
{label: '泳道上移', icon: 'arrow-up', url: $.createLink('kanban', 'lanemove', 'direction=up&lane=' + lane.id + '&kanban=' + kanbanID), className: 'iframe', disabled: !$kanban.prev('.kanban').length},
|
||||
{label: '泳道下移', icon: 'arrow-down', url: $.createLink('kanban', 'lanemove', 'direction=down&lane=' + lane.id + '&kanban=' + kanbanID), className: 'iframe', disabled: !$kanban.next('.kanban').length},
|
||||
];
|
||||
var bounds = options.$trigger[0].getBoundingClientRect();
|
||||
items.$options = {x: bounds.right, y: bounds.top};
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create story menu 创建需求卡片操作菜单
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
function createStoryMenu(options)
|
||||
{
|
||||
var $card = options.$trigger.closest('.kanban-item');
|
||||
var story = $card.data('item');
|
||||
var items =
|
||||
[
|
||||
{label: '编辑需求', icon: 'edit', url: $.createLink('story', 'edit', 'storyID=' + story.id), className: 'iframe'},
|
||||
{label: '变更需求', icon: 'change', url: $.createLink('story', 'change', 'storyID=' + story.id), className: 'iframe'},
|
||||
{label: '移除需求', icon: 'unlink', url: $.createLink('story', 'unlink', 'storyID=' + story.id), className: 'iframe'},
|
||||
{label: '分解任务', icon: 'plus', url: $.createLink('task', 'create', 'storyID=' + story.id), className: 'iframe'},
|
||||
{label: '批量分解', icon: 'pluses', url: $.createLink('task', 'batchCreate', 'storyID=' + story.id), className: 'iframe'},
|
||||
];
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create bug menu 创建 Bug 卡片操作菜单
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
function createBugMenu(options)
|
||||
{
|
||||
var $card = options.$trigger.closest('.kanban-item');
|
||||
var bug = $card.data('item');
|
||||
var items =
|
||||
[
|
||||
{label: '编辑Bug', icon: 'edit', url: $.createLink('bug', 'edit', 'bugID=' + bug.id), className: 'iframe'},
|
||||
{label: '确认Bug', icon: 'ok', url: $.createLink('bug', 'confirm', 'bugID=' + bug.id), className: 'iframe'},
|
||||
{label: '复制Bug', icon: 'copy', url: $.createLink('bug', 'copy', 'bugID=' + bug.id), className: 'iframe'},
|
||||
{label: '转软件需求', icon: 'lightbulb', url: $.createLink('story', 'create', 'bugID=' + bug.id), className: 'iframe'},
|
||||
];
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create task menu 创建任务卡片操作菜单
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
function createTaskMenu(options)
|
||||
{
|
||||
var $card = options.$trigger.closest('.kanban-item');
|
||||
var task = $card.data('item');
|
||||
var items =
|
||||
[
|
||||
{label: '编辑任务', icon: 'edit', url: $.createLink('task', 'edit', 'taskID=' + task.id), className: 'iframe'},
|
||||
{label: '拆分子任务', icon: 'plus', url: $.createLink('task', 'create', 'taskID=' + task.id), className: 'iframe'},
|
||||
{label: '复制任务', icon: 'copy', url: $.createLink('task', 'copy', 'taskID=' + task.id), className: 'iframe'},
|
||||
{label: '取消任务', icon: 'cancel', url: $.createLink('task', 'cancel', 'taskID=' + task.id), className: 'iframe'},
|
||||
];
|
||||
return items;
|
||||
}
|
||||
|
||||
/* Define menu creators */
|
||||
window.menuCreators =
|
||||
{
|
||||
column: createColumnMenu,
|
||||
columnCreate: createColumnCreateMenu,
|
||||
lane: createLaneMenu,
|
||||
story: createStoryMenu,
|
||||
bug: createBugMenu,
|
||||
task: createTaskMenu,
|
||||
};
|
||||
|
||||
/* Overload kanban default options */
|
||||
$.extend($.fn.kanban.Constructor.DEFAULTS,
|
||||
{
|
||||
@@ -683,26 +587,66 @@ $(function()
|
||||
showCount: true,
|
||||
showZeroCount: true,
|
||||
fluidBoardWidth: true,
|
||||
countRender: renderColumnCount,
|
||||
droppable:
|
||||
{
|
||||
target: findDropColumns,
|
||||
finish: handleFinishDrop,
|
||||
mouseButton: 'left'
|
||||
}
|
||||
},
|
||||
onRenderHeaderCol: renderHeaderCol,
|
||||
onRenderLaneName: renderLaneName,
|
||||
onRenderCount: renderColumnCount
|
||||
};
|
||||
|
||||
/* Hide group menu. */
|
||||
hideGroupMenu();
|
||||
|
||||
/* Create story kanban 创建需求看板 */
|
||||
createKanban('story', getStoryKanbanDemoData(), commonOptions);
|
||||
if(browseType == 'all' || browseType == 'story') createKanban('story', kanbanGroup.story, commonOptions);
|
||||
|
||||
/* Create bug kanban 创建 Bug 看板 */
|
||||
createKanban('bug', getBugKanbanDemoData(), commonOptions);
|
||||
if(browseType == 'all' || browseType == 'bug') createKanban('bug', kanbanGroup.bug, commonOptions);
|
||||
|
||||
/* Create task kanban 创建 任务 看板 */
|
||||
createKanban('task', getTaskKanbanDemoData(), commonOptions);
|
||||
if(browseType == 'all' || browseType == 'task') createKanban('task', kanbanGroup.task, commonOptions);
|
||||
|
||||
/* Init iframe modals */
|
||||
$(document).on('click', '.iframe', function(event)
|
||||
{
|
||||
$(this).modalTrigger({show: true, width: '500px'});
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
/* Init contextmenu */
|
||||
$('#kanbans').on('click', '[data-contextmenu]', function(event)
|
||||
{
|
||||
var $trigger = $(this);
|
||||
var menuType = $trigger.data('contextmenu');
|
||||
|
||||
var menuCreator = window.menuCreators[menuType];
|
||||
if(!menuCreator) return;
|
||||
|
||||
var options = $.extend({event, $trigger: $trigger}, $trigger.data());
|
||||
var items = menuCreator(options);
|
||||
if(!items || !items.length) return;
|
||||
|
||||
$.zui.ContextMenu.show(items, items.$options || {event: event});
|
||||
});
|
||||
|
||||
/* Hide contextmenu when page scroll */
|
||||
$(window).on('scroll', function()
|
||||
{
|
||||
$.zui.ContextMenu.hide();
|
||||
});
|
||||
|
||||
$('#toStoryButton').on('click', function()
|
||||
{
|
||||
var planID = $('#plan').val();
|
||||
if(planID)
|
||||
{
|
||||
parent.location.href = createLink('execution', 'importPlanStories', 'executionID=' + executionID + '&planID=' + planID);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -376,6 +376,11 @@ $lang->execution->kanbanHideCols = 'Geschlossene und abgebrochene Spalten in K
|
||||
$lang->execution->kanbanShowOption = 'Aufklappen';
|
||||
$lang->execution->kanbanColsColor = 'Spaltenfarben';
|
||||
|
||||
$lang->execution->kanbanViewList['all'] = 'All';
|
||||
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}";
|
||||
$lang->execution->kanbanViewList['bug'] = 'Bug';
|
||||
$lang->execution->kanbanViewList['task'] = 'Task';
|
||||
|
||||
$lang->kanbanSetting = new stdclass();
|
||||
$lang->kanbanSetting->noticeReset = 'Möchten Sie die Einstellungen des Kanbans zurücksetzen?';
|
||||
$lang->kanbanSetting->optionList['0'] = 'Verstecken';
|
||||
|
||||
@@ -101,6 +101,9 @@ $lang->execution->copyNoExecution = 'There are no ' . $lang->executionCommon . '
|
||||
$lang->execution->noTeam = 'No team members at the moment';
|
||||
$lang->execution->or = ' or ';
|
||||
$lang->execution->selectProject = 'Please select project';
|
||||
$lang->execution->editName = 'Edit Name';
|
||||
$lang->execution->setWIP = 'WIP Settings';
|
||||
$lang->execution->sortColumn = 'Kanban Card Sorting';
|
||||
|
||||
/* Fields of zt_team. */
|
||||
$lang->execution->root = 'Root';
|
||||
@@ -385,6 +388,11 @@ $lang->execution->kanbanHideCols = 'Closed & Cancelled Columns';
|
||||
$lang->execution->kanbanShowOption = 'Unfold';
|
||||
$lang->execution->kanbanColsColor = 'Customize Column Color';
|
||||
|
||||
$lang->execution->kanbanViewList['all'] = 'All';
|
||||
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}";
|
||||
$lang->execution->kanbanViewList['bug'] = 'Bug';
|
||||
$lang->execution->kanbanViewList['task'] = 'Task';
|
||||
|
||||
$lang->kanbanSetting = new stdclass();
|
||||
$lang->kanbanSetting->noticeReset = 'Do you want to reset Kanban?';
|
||||
$lang->kanbanSetting->optionList['0'] = 'Hide';
|
||||
|
||||
@@ -376,6 +376,11 @@ $lang->execution->kanbanHideCols = 'Colonnes masquées';
|
||||
$lang->execution->kanbanShowOption = 'Déplier';
|
||||
$lang->execution->kanbanColsColor = 'Personnalisation Couleurs';
|
||||
|
||||
$lang->execution->kanbanViewList['all'] = 'All';
|
||||
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}";
|
||||
$lang->execution->kanbanViewList['bug'] = 'Bug';
|
||||
$lang->execution->kanbanViewList['task'] = 'Task';
|
||||
|
||||
$lang->kanbanSetting = new stdclass();
|
||||
$lang->kanbanSetting->noticeReset = 'Voulez-vous réinitialiser le tableau Kanban ?';
|
||||
$lang->kanbanSetting->optionList['0'] = 'Masquer';
|
||||
|
||||
@@ -377,6 +377,11 @@ $lang->execution->kanbanHideCols = 'Cột Đã đóng & đã hủy';
|
||||
$lang->execution->kanbanShowOption = 'Mở ra';
|
||||
$lang->execution->kanbanColsColor = 'Tùy biến màu cột';
|
||||
|
||||
$lang->execution->kanbanViewList['all'] = 'All';
|
||||
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}";
|
||||
$lang->execution->kanbanViewList['bug'] = 'Bug';
|
||||
$lang->execution->kanbanViewList['task'] = 'Task';
|
||||
|
||||
$lang->kanbanSetting = new stdclass();
|
||||
$lang->kanbanSetting->noticeReset = 'Bạn có muốn thiết lập lại Kanban?';
|
||||
$lang->kanbanSetting->optionList['0'] = 'Ẩn';
|
||||
|
||||
@@ -101,6 +101,9 @@ $lang->execution->copyNoExecution = '没有可用的' . $lang->executionCommon .
|
||||
$lang->execution->noTeam = '暂时没有团队成员';
|
||||
$lang->execution->or = '或';
|
||||
$lang->execution->selectProject = '请选择项目';
|
||||
$lang->execution->editName = '编辑名称';
|
||||
$lang->execution->setWIP = '在制品数量设置(WIP)';
|
||||
$lang->execution->sortColumn = '看板列卡片排序';
|
||||
|
||||
/* Fields of zt_team. */
|
||||
$lang->execution->root = '源ID';
|
||||
@@ -386,6 +389,11 @@ $lang->execution->kanbanHideCols = '看板隐藏已关闭、已取消列';
|
||||
$lang->execution->kanbanShowOption = '显示折叠信息';
|
||||
$lang->execution->kanbanColsColor = '看板列自定义颜色';
|
||||
|
||||
$lang->execution->kanbanViewList['all'] = '综合看板';
|
||||
$lang->execution->kanbanViewList['story'] = "{$lang->SRCommon}看板";
|
||||
$lang->execution->kanbanViewList['bug'] = 'Bug看板';
|
||||
$lang->execution->kanbanViewList['task'] = '任务看板';
|
||||
|
||||
$lang->kanbanSetting = new stdclass();
|
||||
$lang->kanbanSetting->noticeReset = '是否恢复看板默认设置?';
|
||||
$lang->kanbanSetting->optionList['0'] = '隐藏';
|
||||
|
||||
@@ -81,6 +81,8 @@
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<?php else:?>
|
||||
<?php $canCreateTask = $canBatchCreateTask = $canCreateBug = $canBatchCreateBug = $canCreateStory = $canBatchCreateStory = $canLinkStory = $canLinkStoryByPlane = false;?>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -104,12 +106,37 @@
|
||||
<div class="modal-body">
|
||||
<div class='input-group'>
|
||||
<?php echo html::select('plan', $allPlans, '', "class='form-control chosen' id='plan'");?>
|
||||
<span class='input-group-btn'><?php echo html::commonButton($lang->execution->linkStory, "id='toTaskButton'", 'btn btn-primary');?></span>
|
||||
<span class='input-group-btn'><?php echo html::commonButton($lang->execution->linkStory, "id='toStoryButton'", 'btn btn-primary');?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php js::set('executionID', $executionID);?>
|
||||
<?php js::set('productID', $productID);?>
|
||||
<?php js::set('kanbanGroup', $kanbanGroup);?>
|
||||
<?php js::set('browseType', $browseType);?>
|
||||
<?php
|
||||
js::set('priv',
|
||||
array(
|
||||
'hasEditName' => common::hasPriv('kanban', 'setColumn'),
|
||||
'hasSetWIP' => common::hasPriv('kanban', 'setWIP'),
|
||||
'canCreateTask' => $canCreateTask,
|
||||
'canBatchCreateTask' => $canBatchCreateTask,
|
||||
'canCreateBug' => $canCreateBug,
|
||||
'canBatchCreateBug' => $canBatchCreateBug,
|
||||
'canCreateStory' => $canCreateStory,
|
||||
'canBatchCreateStory' => $canBatchCreateStory,
|
||||
'canLinkStory' => $canLinkStory,
|
||||
'canLinkStoryByPlane' => $canLinkStoryByPlane,
|
||||
)
|
||||
);
|
||||
?>
|
||||
<?php js::set('executionLang', $lang->execution);?>
|
||||
<?php js::set('storyLang', $lang->story);?>
|
||||
<?php js::set('taskLang', $lang->task);?>
|
||||
<?php js::set('bugLang', $lang->bug);?>
|
||||
<?php js::set('editName', $lang->execution->editName);?>
|
||||
<?php js::set('setWIP', $lang->execution->setWIP);?>
|
||||
<?php js::set('sortColumn', $lang->execution->sortColumn);?>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
|
||||
@@ -25,6 +25,7 @@ $lang->moduleOrder[40] = 'release';
|
||||
$lang->moduleOrder[45] = 'project';
|
||||
$lang->moduleOrder[50] = 'projectstory';
|
||||
$lang->moduleOrder[55] = 'execution';
|
||||
$lang->moduleOrder[56] = 'kanban';
|
||||
$lang->moduleOrder[60] = 'task';
|
||||
$lang->moduleOrder[65] = 'build';
|
||||
|
||||
@@ -547,6 +548,16 @@ $lang->release->methodOrder[60] = 'unlinkBug';
|
||||
$lang->release->methodOrder[65] = 'batchUnlinkBug';
|
||||
$lang->release->methodOrder[70] = 'changeStatus';
|
||||
|
||||
/* Kanban */
|
||||
$lang->resource->kanban = new stdclass();
|
||||
$lang->resource->kanban->setLane = 'setLane';
|
||||
$lang->resource->kanban->setColumn = 'setColumn';
|
||||
$lang->resource->kanban->setWIP = 'setWIP';
|
||||
|
||||
$lang->kanban->methodOrder[5] = 'setLane';
|
||||
$lang->kanban->methodOrder[10] = 'setColumn';
|
||||
$lang->kanban->methodOrder[15] = 'setWIP';
|
||||
|
||||
/* Execution. */
|
||||
$lang->resource->execution = new stdclass();
|
||||
$lang->resource->execution->view = 'view';
|
||||
|
||||
@@ -80,17 +80,21 @@ class kanban extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function setLaneColumn($columnID)
|
||||
public function setColumn($columnID)
|
||||
{
|
||||
$column = $this->kanban->getColumnById($columnID);
|
||||
|
||||
|
||||
if($_POST)
|
||||
{
|
||||
$params = fixer::input('post')->get();
|
||||
$this->kanban->updateLaneColumn($columnID, $params);
|
||||
/* Check lane column name is unique. */
|
||||
$exist = $this->kanban->getColumnByName($this->post->name, $column->lane);
|
||||
if($exist and $exist->id != $columnID)
|
||||
{
|
||||
return $this->sendError($this->lang->kanban->noColumnUniqueName);
|
||||
}
|
||||
|
||||
$changes = $this->kanban->updateLaneColumn($columnID, $column);
|
||||
if(dao::isError()) return $this->sendError(dao::getError());
|
||||
|
||||
$changes = common::createChanges($column, $params);
|
||||
if($changes)
|
||||
{
|
||||
$actionID = $this->loadModel('action')->create('kanbancolumn', $columnID, 'Edited');
|
||||
@@ -101,8 +105,51 @@ class kanban extends control
|
||||
}
|
||||
|
||||
$this->view->column = $column;
|
||||
$this->view->title = $column->name . $this->lang->colon . $this->lang->kanban->setLaneColumn;
|
||||
$this->view->title = $column->name . $this->lang->colon . $this->lang->kanban->setColumn;
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Update the cards sorting of the lane column.
|
||||
*
|
||||
* @param string $laneType story|bug|task
|
||||
* @param int $columnID
|
||||
* @param string $orderBy id_desc|id_asc|pri_desc|pri_asc|lastEditedDate_desc|lastEditedDate_asc|deadline_desc|deadline_asc|assignedTo_asc
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxCardsSort($laneType, $columnID, $orderBy = 'id_desc')
|
||||
{
|
||||
$oldCards = array();
|
||||
$column = $this->dao->select('parent,cards')->from(TABLE_KANBANCOLUMN)->where('id')->eq($columnID)->fetch();
|
||||
|
||||
/* Get the cards of the kanban column. */
|
||||
if($column->parent == -1)
|
||||
{
|
||||
$childColumns = $this->dao->select('id,cards')->from(TABLE_KANBANCOLUMN)->where('parent')->eq($columnID)->fetchAll();
|
||||
foreach($childColumns as $childColumn)
|
||||
{
|
||||
$oldCards[$childColumn->id] = $childColumn->cards;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oldCards[$columnID] = $column->cards;
|
||||
}
|
||||
|
||||
/* Update Kanban column card order. */
|
||||
$table = $this->config->objectTables[$laneType];
|
||||
foreach($oldCards as $colID => $cards)
|
||||
{
|
||||
if(empty($cards)) continue;
|
||||
$objects = $this->dao->select('id')->from($table)
|
||||
->where('id')->in($cards)
|
||||
->orderBy($orderBy)
|
||||
->fetchPairs('id');
|
||||
|
||||
$objectIdList = ',' . implode(',', $objects) . ',';
|
||||
$this->dao->update(TABLE_KANBANCOLUMN)->set('cards')->eq($objectIdList)->where('id')->eq($colID)->exec();
|
||||
}
|
||||
echo true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ td>ul {padding-left: 0;}
|
||||
li {display: block; float: left; padding: 5px; width: 28px; height: 28px;}
|
||||
li>a { position: relative; display: block; width: 100%; height: 100%; padding: 0; margin: 0; font-family: ZentaoIcon; font-size: 14px; font-style: normal; font-weight: 400; font-variant: normal; line-height: 1; text-align: center; text-transform: none; border: 1px solid transparent; border-radius: 50%; speak: none; -webkit-font-smoothing: antialiased; }
|
||||
li>a.active:before {font-size: 14px; content: "\e5ca"}
|
||||
|
||||
+18
-10
@@ -1,16 +1,24 @@
|
||||
<?php
|
||||
$lang->kanban = new stdClass();
|
||||
|
||||
$lang->kanban->WIP = 'WIP';
|
||||
$lang->kanban->setWIP = 'WIP Settings';
|
||||
$lang->kanban->WIPStatus = 'WIP Status';
|
||||
$lang->kanban->WIPStage = 'WIP Stage';
|
||||
$lang->kanban->WIPType = 'WIP Type';
|
||||
$lang->kanban->WIPCount = 'WIP Count';
|
||||
$lang->kanban->noLimit = 'No Limit ∞';
|
||||
$lang->kanban->setLane = 'Lane Settings';
|
||||
$lang->kanban->laneName = 'Lane Name';
|
||||
$lang->kanban->laneColor = 'Lane Color';
|
||||
$lang->kanban->WIP = 'WIP';
|
||||
$lang->kanban->setWIP = 'WIP Settings';
|
||||
$lang->kanban->WIPStatus = 'WIP Status';
|
||||
$lang->kanban->WIPStage = 'WIP Stage';
|
||||
$lang->kanban->WIPType = 'WIP Type';
|
||||
$lang->kanban->WIPCount = 'WIP Count';
|
||||
$lang->kanban->noLimit = 'No Limit ∞';
|
||||
$lang->kanban->setLane = 'Lane Settings';
|
||||
$lang->kanban->laneName = 'Lane Name';
|
||||
$lang->kanban->laneColor = 'Lane Color';
|
||||
$lang->kanban->setLaneColumn = 'Column Settings';
|
||||
$lang->kanban->columnName = 'Column Name';
|
||||
$lang->kanban->columnColor = 'Column Color';
|
||||
$lang->kanban->noColumnUniqueName = 'The Kanban column name already exists.';
|
||||
|
||||
$lang->kanban->error = new stdclass();
|
||||
$lang->kanban->error->parentLimitNote = 'The WIPs in the parent column cannot be < the sum of the WIPs in the child column.';
|
||||
$lang->kanban->error->childLimitNote = 'The sum of products in the child column cannot be > the number of products in the parent column.';
|
||||
|
||||
$this->lang->kanban->laneTypeList = array();
|
||||
$this->lang->kanban->laneTypeList['story'] = $lang->SRCommon;
|
||||
|
||||
+190
-110
@@ -32,86 +32,94 @@ class kanbanModel extends model
|
||||
->fetchAll('id');
|
||||
|
||||
if(empty($lanes)) return array();
|
||||
|
||||
foreach($lanes as $lane) $this->updateCards($lane);
|
||||
|
||||
$columns = $this->dao->select('*')->from(TABLE_KANBANCOLUMN)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('lane')->in(array_keys($lanes))
|
||||
->fetchGroup('lane', 'id');
|
||||
|
||||
/* Get parent column type pairs. */
|
||||
$parentTypes = $this->dao->select('id, type')->from(TABLE_KANBANCOLUMN)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('lane')->in(array_keys($lanes))
|
||||
->andWhere('parent')->eq(-1)
|
||||
->fetchPairs('id', 'type');
|
||||
|
||||
if($objectType == 'story' or $objectType == 'all') $stories = $this->loadModel('story')->getExecutionStories($executionID);
|
||||
if($objectType == 'bug' or $objectType == 'all') $bugs = $this->loadModel('bug')->getExecutionBugs($executionID);
|
||||
if($objectType == 'task' or $objectType == 'all') $tasks = $this->loadModel('execution')->getKanbanTasks($executionID, "id");
|
||||
/* Get group objects. */
|
||||
if($browseType == 'all' or $browseType == 'story') $objectGroup['story'] = $this->loadModel('story')->getExecutionStories($executionID);
|
||||
if($browseType == 'all' or $browseType == 'bug') $objectGroup['bug'] = $this->loadModel('bug')->getExecutionBugs($executionID);
|
||||
if($browseType == 'all' or $browseType == 'task') $objectGroup['task'] = $this->loadModel('execution')->getKanbanTasks($executionID, "id");
|
||||
|
||||
/* Init vars. */
|
||||
$kanban = array();
|
||||
/* Build kanban group data. */
|
||||
$kanbanGroup = array();
|
||||
foreach($lanes as $laneID => $lane)
|
||||
{
|
||||
$laneData = array();
|
||||
$columnData = array();
|
||||
$cards = array();
|
||||
$laneType = $lane->type;
|
||||
|
||||
$laneData['id'] = $lane->type;
|
||||
$laneData['id'] = $laneType;
|
||||
$laneData['name'] = $lane->name;
|
||||
$laneData['color'] = $lane->color;
|
||||
$laneData['defaultCardType'] = $lane->type;
|
||||
$laneData['order'] = $lane->order;
|
||||
$laneData['defaultCardType'] = $laneType;
|
||||
|
||||
foreach($columns[$laneID] as $colID => $col)
|
||||
foreach($columns[$laneID] as $columnID => $column)
|
||||
{
|
||||
$columnData[$col->id]['id'] = $lane->type . '-' . $col->type;
|
||||
$columnData[$col->id]['type'] = $col->type;
|
||||
$columnData[$col->id]['name'] = $col->name;
|
||||
$columnData[$col->id]['color'] = $col->color;
|
||||
$columnData[$col->id]['limit'] = $col->limit;
|
||||
$columnData[$col->id]['asParent'] = $col->parent == -1 ? true : false;
|
||||
$columnData[$column->id]['id'] = $laneType . '-' . $column->type;
|
||||
$columnData[$column->id]['columnID'] = $columnID;
|
||||
$columnData[$column->id]['type'] = $column->type;
|
||||
$columnData[$column->id]['name'] = $column->name;
|
||||
$columnData[$column->id]['color'] = $column->color;
|
||||
$columnData[$column->id]['limit'] = $column->limit;
|
||||
$columnData[$column->id]['laneType'] = $laneType;
|
||||
$columnData[$column->id]['asParent'] = $column->parent == -1 ? true : false;
|
||||
|
||||
if($col->parent > 0)
|
||||
if($column->parent > 0)
|
||||
{
|
||||
$columnData[$col->id]['parentType'] = zget($parentTypes, $col->parent, '');
|
||||
$columnData[$column->id]['parentType'] = zget($parentTypes, $column->parent, '');
|
||||
}
|
||||
|
||||
$cardIdList = array_filter(explode(',', $col->cards));
|
||||
$cardOrder = 1;
|
||||
$item = array();
|
||||
$cardIdList = array_filter(explode(',', $column->cards));
|
||||
foreach($cardIdList as $cardID)
|
||||
{
|
||||
if($lane->type == 'story') $object = zget($stories, $cardID, array());
|
||||
if($lane->type == 'task') $object = zget($tasks, $cardID, array());
|
||||
if($lane->type == 'bug') $object = zget($bugs, $cardID, array());
|
||||
$cardData = array();
|
||||
$objects = zget($objectGroup, $laneType, array());
|
||||
$object = zget($objects, $cardID, array());
|
||||
|
||||
$item['id'] = $object->id;
|
||||
$item['order'] = $cardOrder;
|
||||
$item['pri'] = $object->pri ? $object->pri : '';
|
||||
$item['estimate'] = $lane->type == 'bug' ? '' : $object->estimate;
|
||||
$item['assignedTo'] = $object->assignedTo;
|
||||
$item['deadline'] = $lane->type == 'task' ? $object->deadline : '';
|
||||
$cardData['id'] = $object->id;
|
||||
$cardData['order'] = $cardOrder;
|
||||
$cardData['pri'] = $object->pri ? $object->pri : '';
|
||||
$cardData['estimate'] = $laneType == 'bug' ? '' : $object->estimate;
|
||||
$cardData['assignedTo'] = $object->assignedTo;
|
||||
$cardData['deadline'] = $laneType == 'task' ? $object->deadline : '';
|
||||
$cardData['severity'] = $laneType == 'bug' ? $object->severity : '';
|
||||
|
||||
if($lane->type == 'task')
|
||||
if($laneType == 'task')
|
||||
{
|
||||
$item['name'] = $object->name;
|
||||
$cardData['name'] = $object->name;
|
||||
}
|
||||
else
|
||||
{
|
||||
$item['title'] = $object->title;
|
||||
$cardData['title'] = $object->title;
|
||||
}
|
||||
|
||||
$laneData['cards'][$col->type][] = $item;
|
||||
$laneData['cards'][$column->type][] = $cardData;
|
||||
$cardOrder ++;
|
||||
}
|
||||
if(!isset($laneData['cards'][$column->type])) $laneData['cards'][$column->type] = array();
|
||||
}
|
||||
|
||||
$kanban[$lane->type]['id'] = $lane->type;
|
||||
$kanban[$lane->type]['columns'] = array_values($columnData);
|
||||
$kanban[$lane->type]['lanes'][] = $laneData;
|
||||
$kanban[$lane->type]['defaultCardType'] = $lane->type;
|
||||
$kanbanGroup[$laneType]['id'] = $laneType;
|
||||
$kanbanGroup[$laneType]['columns'] = array_values($columnData);
|
||||
$kanbanGroup[$laneType]['lanes'][] = $laneData;
|
||||
$kanbanGroup[$laneType]['defaultCardType'] = $laneType;
|
||||
}
|
||||
|
||||
return $kanban;
|
||||
return $kanbanGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,93 +338,97 @@ class kanbanModel extends model
|
||||
/**
|
||||
* Update column cards.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @param string $objectType story|bug|task
|
||||
* @param object $columns
|
||||
* @param object $lane
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function updateCards($executionID, $objectType, $columns)
|
||||
public function updateCards($lane)
|
||||
{
|
||||
$objects = array();
|
||||
if($objectType == 'story') $objects = $this->loadModel('story')->getExecutionStories($executionID);
|
||||
if($objectType == 'bug') $objects = $this->loadModel('bug')->getExecutionBugs($executionID);
|
||||
if($objectType == 'task') $objects = $this->loadModel('execution')->getKanbanTasks($executionID);
|
||||
if($objectType == 'story')
|
||||
$laneType = $lane->type;
|
||||
$executionID = $lane->execution;
|
||||
$cardPairs = $this->dao->select('*')->from(TABLE_KANBANCOLUMN)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('lane')->eq($lane->id)
|
||||
->fetchPairs('type' ,'cards');
|
||||
|
||||
if($laneType == 'story')
|
||||
{
|
||||
$data = new stdClass();
|
||||
foreach($columns as $colID => $col)
|
||||
$stories = $this->loadModel('story')->getExecutionStories($executionID);
|
||||
foreach($stories as $storyID => $story)
|
||||
{
|
||||
foreach($objects as $storyID => $story)
|
||||
foreach($this->config->kanban->storyColumnStageList as $colType => $stage)
|
||||
{
|
||||
if($col->type == 'backlog' and $story->status == 'active' and $story->stage == 'projected') $data->cards .= $storyID . ',';
|
||||
|
||||
if($colType == 'closed' and $story->status == 'closed' and $story->stage == 'closed') $data->cards .= $storyID . ',';
|
||||
|
||||
if($story->stage == $colType and strpos(',backlog,closed,', $colType) === false and $story->status == 'active') $data->cards .= $storyID . ',';
|
||||
}
|
||||
if(!empty($data->cards)) $data->cards = ',' . $data->cards;
|
||||
}
|
||||
|
||||
$this->dao->insert(TABLE_KANBANCOLUMN)->data($data)->exec();
|
||||
if($colType == 'develop') $devColumnID = $this->dao->lastInsertId();
|
||||
if($colType == 'test') $testColumnID = $this->dao->lastInsertId();
|
||||
}
|
||||
elseif($type == 'bug')
|
||||
{
|
||||
foreach($this->lang->kanban->bugColumn as $colType => $name)
|
||||
{
|
||||
$data = new stdClass();
|
||||
$data->lane = $laneID;
|
||||
$data->name = $name;
|
||||
$data->type = $colType;
|
||||
$data->cards = '';
|
||||
if(strpos(',fixing,fixed,', $colType) !== false) $data->parent = $resolvingColumnID;
|
||||
if(strpos(',testing,tested,', $colType) !== false) $data->parent = $testColumnID;
|
||||
if(strpos(',resolving,fixing,test,testing,tested,', $colType) === false)
|
||||
{
|
||||
foreach($objects as $bugID => $bug)
|
||||
if(strpos(',ready,develop,test,', $colType) !== false) continue;
|
||||
if($colType == 'backlog' and $story->stage == $stage and strpos($cardPairs['ready'], ",$storyID,") === false and strpos($cardPairs['backlog'], ",$storyID,") === false)
|
||||
{
|
||||
if($colType == 'unconfirmed' and $bug->status == 'active' and $bug->confirmed == 0) $data->cards .= $bugID . ',';
|
||||
|
||||
if($colType == 'confirmed' and $bug->status == 'active' and $bug->confirmed == 1) $data->cards .= $bugID . ',';
|
||||
|
||||
if($colType == 'fixed' and $bug->status == 'resolved') $data->cards .= $bugID . ',';
|
||||
|
||||
if($colType == 'closed' and $bug->status == 'closed') $data->cards .= $bugID . ',';
|
||||
$cardPairs['backlog'] .= empty($cardPairs['backlog']) ? ',' . $storyID . ',' : $storyID . ',';
|
||||
}
|
||||
elseif($story->stage == $stage and strpos($cardPairs[$colType], ",$storyID,") === false)
|
||||
{
|
||||
$cardPairs[$colType] .= empty($cardPairs[$colType]) ? ',' . $storyID . ',' : $storyID . ',';
|
||||
}
|
||||
elseif($story->stage != $stage and strpos($cardPairs[$colType], ",$storyID,") !== false)
|
||||
{
|
||||
$cardPairs[$colType] = str_replace(",$storyID,", ',', $cardPairs[$colType]);
|
||||
}
|
||||
$this->dao->insert(TABLE_KANBANCOLUMN)->data($data)->exec();
|
||||
if($colType == 'resolving') $resolvingColumnID = $this->dao->lastInsertId();
|
||||
if($colType == 'test') $testColumnID = $this->dao->lastInsertId();
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif($type == 'task')
|
||||
elseif($laneType == 'bug')
|
||||
{
|
||||
foreach($this->lang->kanban->taskColumn as $colType => $name)
|
||||
$bugs = $this->loadModel('bug')->getExecutionBugs($executionID);
|
||||
foreach($bugs as $bugID => $bug)
|
||||
{
|
||||
$data = new stdClass();
|
||||
$data->lane = $laneID;
|
||||
$data->name = $name;
|
||||
$data->type = $colType;
|
||||
$data->cards = '';
|
||||
if(strpos(',developing,developed,', $colType) !== false) $data->parent = $devColumnID;
|
||||
if(strpos(',develop,', $colType) === false)
|
||||
foreach($this->config->kanban->bugColumnStatusList as $colType => $status)
|
||||
{
|
||||
foreach($objects as $taskID => $task)
|
||||
if(strpos(',resolving,fixing,test,testing,tested,', $colType) !== false) continue;
|
||||
if($colType == 'unconfirmed' and $bug->status == $status and $bug->confirmed == 0 and strpos($cardPairs['unconfirmed'], ",$bugID,") === false and strpos($cardPairs['fixing'], ",$bugID,") === false)
|
||||
{
|
||||
if($colType == 'developing' and $task->status == 'doing') $data->cards .= $taskID . ',';
|
||||
|
||||
if($colType == 'developed' and $task->status == 'done') $data->cards .= $taskID . ',';
|
||||
|
||||
if(strpos(',wait,pause,canceled,closed,', $colType) !== false and $task->status == $colType) $data->cards .= $taskID . ',';
|
||||
$cardPairs['unconfirmed'] .= empty($cardPairs['unconfirmed']) ? ',' . $bugID . ',' : $bugID . ',';
|
||||
}
|
||||
elseif($colType == 'confirmed' and $bug->status == $status and $bug->confirmed == 1 and strpos($cardPairs['confirmed'], ",$bugID,") === false and strpos($cardPairs['fixing'], ",$bugID,") === false)
|
||||
{
|
||||
$cardPairs['confirmed'] .= empty($cardPairs['confirmed']) ? ',' . $bugID . ',' : $bugID . ',';
|
||||
}
|
||||
elseif($colType == 'fixed' and $bug->status == $status and strpos($cardPairs['fixed'], ",$bugID,") === false and strpos($cardPairs['testing'], ",$bugID,") === false and strpos($cardPairs['tested'], ",$bugID,") === false)
|
||||
{
|
||||
$cardPairs['confirmed'] .= empty($cardPairs['confirmed']) ? ',' . $bugID . ',' : $bugID . ',';
|
||||
}
|
||||
elseif($bug->status == $status and strpos($cardPairs[$colType], ",$bugID,") === false)
|
||||
{
|
||||
$cardPairs[$colType] .= empty($cardPairs[$colType]) ? ',' . $bugID . ',' : $bugID . ',';
|
||||
}
|
||||
elseif($bug->status != $status and strpos($cardPairs[$colType], ",$bugID,") !== false)
|
||||
{
|
||||
$cardPairs[$colType] = str_replace(",$bugID,", ',', $cardPairs[$colType]);
|
||||
}
|
||||
|
||||
$this->dao->insert(TABLE_KANBANCOLUMN)->data($data)->exec();
|
||||
if($colType == 'develop') $devColumnID = $this->dao->lastInsertId();
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif($laneType == 'task')
|
||||
{
|
||||
$tasks = $this->loadModel('execution')->getKanbanTasks($executionID);
|
||||
foreach($tasks as $taskID => $task)
|
||||
{
|
||||
foreach($this->config->kanban->taskColumnStatusList as $colType => $status)
|
||||
{
|
||||
if($colType == 'develop') continue;
|
||||
if($task->status == $status and strpos($cardPairs[$colType], ",$taskID,") === false)
|
||||
{
|
||||
$cardPairs[$colType] .= empty($cardPairs[$colType]) ? ',' . $taskID . ',' : $taskID . ',';
|
||||
}
|
||||
elseif($task->status != $status and strpos($cardPairs[$colType], ",$taskID,") !== false)
|
||||
{
|
||||
$cardPairs[$colType] = str_replace(",$taskID,", ',', $cardPairs[$colType]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($cardPairs as $colType => $cards)
|
||||
{
|
||||
$this->dao->update(TABLE_KANBANCOLUMN)->set('cards')->eq($cards)->where('lane')->eq($lane->id)->andWhere('type')->eq($colType)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -439,6 +451,24 @@ class kanbanModel extends model
|
||||
return $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Column by column name.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $laneID
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function getColumnByName($name, $laneID)
|
||||
{
|
||||
return $this->dao->select('*')
|
||||
->from(TABLE_KANBANCOLUMN)
|
||||
->where('name')->eq($name)
|
||||
->andWhere('lane')->eq($laneID)
|
||||
->fetch();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get lane by id.
|
||||
*
|
||||
@@ -460,15 +490,57 @@ class kanbanModel extends model
|
||||
*/
|
||||
public function setWIP($columnID)
|
||||
{
|
||||
$column = $this->getColumnById($columnID);
|
||||
$data = fixer::input('post')
|
||||
$oldColumn = $this->getColumnById($columnID);
|
||||
$column = fixer::input('post')
|
||||
->cleanInt('limit')
|
||||
->remove('WIPCount,noLimit')
|
||||
->get();
|
||||
|
||||
$this->dao->update(TABLE_KANBANCOLUMN)->data($data)
|
||||
/* Check column limit. */
|
||||
$sumChildLimit = 0;
|
||||
if($oldColumn->parent == -1 and $column->limit != -1)
|
||||
{
|
||||
$childColumns = $this->dao->select('id,`limit`')->from(TABLE_KANBANCOLUMN)->where('parent')->eq($columnID)->fetchAll();
|
||||
foreach($childColumns as $childColumn)
|
||||
{
|
||||
if($childColumn->limit == -1)
|
||||
{
|
||||
dao::$errors['limit'] = $this->lang->kanban->error->parentLimitNote;
|
||||
return false;
|
||||
}
|
||||
|
||||
$sumChildLimit += $childColumn->limit;
|
||||
}
|
||||
|
||||
if($sumChildLimit > $column->limit)
|
||||
{
|
||||
dao::$errors['limit'] = $this->lang->kanban->error->parentLimitNote;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif($oldColumn->parent > 0)
|
||||
{
|
||||
$parentColumn = $this->getColumnByID($oldColumn->parent);
|
||||
if($parentColumn->limit != -1)
|
||||
{
|
||||
$siblingLimit = $this->dao->select('`limit`')->from(TABLE_KANBANCOLUMN)
|
||||
->where('`parent`')->eq($oldColumn->parent)
|
||||
->andWhere('id')->ne($columnID)
|
||||
->fetch('limit');
|
||||
|
||||
$sumChildLimit = $siblingLimit + $column->limit;
|
||||
|
||||
if($column->limit == -1 or $siblingLimit == -1 or $sumChildLimit > $parentColumn->limit)
|
||||
{
|
||||
dao::$errors['limit'] = $this->lang->kanban->error->childLimitNote;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->dao->update(TABLE_KANBANCOLUMN)->data($column)
|
||||
->autoCheck()
|
||||
->checkIF($data->limit != -1, 'limit', 'gt', 0)
|
||||
->checkIF($column->limit != -1, 'limit', 'gt', 0)
|
||||
->batchcheck($this->config->kanban->setwip->requiredFields, 'notempty')
|
||||
->where('id')->eq($columnID)
|
||||
->exec();
|
||||
@@ -499,16 +571,24 @@ class kanbanModel extends model
|
||||
/**
|
||||
* Update lane column.
|
||||
*
|
||||
* @param int $columnID
|
||||
* @param int $columnID
|
||||
* @param object $column
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function updateLaneColumn($columnID, $data)
|
||||
public function updateLaneColumn($columnID, $column)
|
||||
{
|
||||
$data = fixer::input('post')->get();
|
||||
|
||||
$this->dao->update(TABLE_KANBANCOLUMN)->data($data)
|
||||
->autoCheck()
|
||||
->batchcheck($this->config->kanban->setlaneColumn->requiredFields, 'notempty')
|
||||
->where('id')->eq($columnID)
|
||||
->exec();
|
||||
|
||||
if(dao::isError()) return;
|
||||
|
||||
$changes = common::createChanges($column, $data);
|
||||
return $changes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
* The set lane file of task module of ZenTaoPMS.
|
||||
* The set column file of task module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL (http://zpl.pub/page/zplv12.html)
|
||||
* @author Yuchun Li <liyuchun@easycorp.ltd>
|
||||
* @author Thanatos<xiawenlong@easycorp.ltd>
|
||||
* @package kanban
|
||||
* @version $Id: setlane.html.php 935 2021-10-26 16:24:24Z liyuchun@easycorp.ltd $
|
||||
* @version $Id: setcolumn.html.php 935 2021-10-26 16:24:24Z xiawenlong@easycorp.ltd $
|
||||
* @link https://www.zentao.net
|
||||
*/
|
||||
?>
|
||||
@@ -76,7 +76,7 @@ $lang->program->stakeholderTypeList['inside'] = 'Inside';
|
||||
$lang->program->stakeholderTypeList['outside'] = 'Outside';
|
||||
|
||||
$lang->program->noProgram = 'No program.';
|
||||
$lang->program->showClosed = 'Closed programs.';
|
||||
$lang->program->showClosed = 'Closed';
|
||||
$lang->program->tips = 'If a parent item set is selected, products under that parent item set can be associated. If no item set is selected, a product with the same name as the item is created by default and associated with that item.';
|
||||
$lang->program->confirmBatchUnlink = "Do you want to batch unlink these stakeholders?";
|
||||
$lang->program->beginLetterParent = "Parent begin date: %s, begin date should be > = parent begin date.";
|
||||
|
||||
+18
-2
@@ -132,11 +132,27 @@ class searchModel extends model
|
||||
$condition = '';
|
||||
if($operator == "include")
|
||||
{
|
||||
$condition = ' LIKE ' . $this->dbh->quote("%$value%");
|
||||
if($this->post->$fieldName == 'module')
|
||||
{
|
||||
$allModules = $this->loadModel('tree')->getAllChildId($value);
|
||||
if($allModules) $condition = helper::dbIN($allModules);
|
||||
}
|
||||
else
|
||||
{
|
||||
$condition = ' LIKE ' . $this->dbh->quote("%$value%");
|
||||
}
|
||||
}
|
||||
elseif($operator == "notinclude")
|
||||
{
|
||||
$condition = ' NOT LIKE ' . $this->dbh->quote("%$value%");
|
||||
if($this->post->$fieldName == 'module')
|
||||
{
|
||||
$allModules = $this->loadModel('tree')->getAllChildId($value);
|
||||
if($allModules) $condition = " NOT " . helper::dbIN($allModules);
|
||||
}
|
||||
else
|
||||
{
|
||||
$condition = ' NOT LIKE ' . $this->dbh->quote("%$value%");
|
||||
}
|
||||
}
|
||||
elseif($operator == 'belong')
|
||||
{
|
||||
|
||||
@@ -136,8 +136,15 @@ class story extends control
|
||||
|
||||
if($objectID != 0)
|
||||
{
|
||||
if($objectID != $this->session->project) $this->loadModel('action')->create('story', $storyID, 'linked2execution', '', $objectID);
|
||||
if($this->config->systemMode == 'new') $this->loadModel('action')->create('story', $storyID, 'linked2project', '', $this->session->project);
|
||||
$object = $this->dao->findById((int)$objectID)->from(TABLE_PROJECT)->fetch();
|
||||
if($object->type != 'project')
|
||||
{
|
||||
$this->loadModel('action')->create('story', $storyID, 'linked2execution', '', $objectID);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->loadModel('action')->create('story', $storyID, 'linked2project', '', $objectID);
|
||||
}
|
||||
}
|
||||
|
||||
if($todoID > 0)
|
||||
|
||||
@@ -184,7 +184,7 @@ class upgrade extends control
|
||||
$title = $this->lang->upgrade->toPMS15Guide;
|
||||
if(isset($this->config->maxVersion))
|
||||
{
|
||||
$this->lang->upgrade->to15Desc = str_replace('15', $this->lang->maxName, $this->lang->upgrade->to15Desc);
|
||||
$this->lang->upgrade->to15Desc = str_replace('15.0', $this->lang->maxName, $this->lang->upgrade->to15Desc);
|
||||
$title = $this->lang->upgrade->toMAXGuide;
|
||||
}
|
||||
elseif(isset($this->config->bizVersion))
|
||||
|
||||
Reference in New Issue
Block a user