From 8522e6fb5823c287978dd6f515d76a4069906823 Mon Sep 17 00:00:00 2001 From: zhaoke Date: Wed, 29 Mar 2023 16:26:59 +0800 Subject: [PATCH 01/14] * Add scene control and view file to testcase module. --- config/filter.php | 4 + config/zentaopms.php | 5 + module/action/config.php | 1 + module/action/lang/de.php | 4 + module/action/lang/en.php | 4 + module/action/lang/fr.php | 4 + module/action/lang/vi.php | 4 + module/action/lang/zh-cn.php | 6 + module/action/lang/zh-tw.php | 4 + module/action/model.php | 23 + module/action/view/trash.html.php | 103 +- module/common/view/mindmap.html.php | 6 + module/datatable/model.php | 13 +- module/my/model.php | 3 + module/search/model.php | 5 + module/testcase/config.php | 13 + module/testcase/control.php | 603 +++++++- module/testcase/css/browse.css | 14 + module/testcase/js/batchcreate.js | 89 ++ module/testcase/js/batchedit.js | 59 + module/testcase/js/browse.js | 579 ++++++++ module/testcase/js/create.js | 73 + module/testcase/js/createscene.js | 67 + module/testcase/js/edit.js | 73 + module/testcase/js/editscene.js | 66 + module/testcase/lang/de.php | 79 + module/testcase/lang/en.php | 79 + module/testcase/lang/fr.php | 79 + module/testcase/lang/vi.php | 79 + module/testcase/lang/zh-cn.php | 80 ++ module/testcase/lang/zh-tw.php | 80 ++ module/testcase/model.php | 1191 ++++++++++++++- module/testcase/view/batchcreate.html.php | 11 +- module/testcase/view/batchedit.html.php | 8 +- module/testcase/view/browse.html.php | 305 +++- module/testcase/view/caseheader.html.php | 14 + module/testcase/view/create.html.php | 16 +- module/testcase/view/createscene.html.php | 90 ++ module/testcase/view/datatable.fix.html.php | 134 ++ module/testcase/view/edit.html.php | 16 +- module/testcase/view/editscene.html.php | 99 ++ module/testsuite/control.php | 5 + module/testtask/control.php | 7 +- www/js/mindmap/css/zui.mindmap.css | 264 ++++ www/js/mindmap/js/hotkey.min.js | 12 + www/js/mindmap/js/zui.mindmap.js | 1438 +++++++++++++++++++ 46 files changed, 5766 insertions(+), 145 deletions(-) create mode 100644 module/common/view/mindmap.html.php create mode 100644 module/testcase/js/createscene.js create mode 100644 module/testcase/js/editscene.js create mode 100644 module/testcase/view/createscene.html.php create mode 100644 module/testcase/view/datatable.fix.html.php create mode 100644 module/testcase/view/editscene.html.php create mode 100644 www/js/mindmap/css/zui.mindmap.css create mode 100644 www/js/mindmap/js/hotkey.min.js create mode 100644 www/js/mindmap/js/zui.mindmap.js diff --git a/config/filter.php b/config/filter.php index ee20fa8909..6bb8e77083 100755 --- a/config/filter.php +++ b/config/filter.php @@ -349,6 +349,10 @@ $filter->testtask->cases->cookie['preTaskID'] = 'int'; $filter->testtask->cases->cookie['taskCaseModule'] = 'int'; $filter->testtask->default->cookie['lastProduct'] = 'int'; $filter->testtask->default->cookie['preProductID'] = 'int'; +$filter->testcase->browse->cookie['onlyScene'] = 'code'; + +if(empty($filter->project->testcase)) $filter->project->testcase = new stdclass(); +$filter->project->testcase->cookie= array('onlyScene' => 'code'); $filter->todo->export->cookie['checkedItem'] = 'reg::checked'; diff --git a/config/zentaopms.php b/config/zentaopms.php index 836c84acfe..b4a8dbcfee 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -355,6 +355,10 @@ define('TABLE_DASHBOARD', '`' . $config->db->prefix . 'dashboard`'); define('TABLE_DATASET', '`' . $config->db->prefix . 'dataset`'); define('TABLE_DATAVIEW', '`' . $config->db->prefix . 'dataview`'); define('TABLE_DIMENSION', '`' . $config->db->prefix . 'dimension`'); +define('TABLE_SCENE', '`' . $config->db->prefix . 'scene`'); +define('VIEW_SCENECASE', '`ztv_scenecase`'); + +define('CHANGEVALUE', 100000000); $config->objectTables['product'] = TABLE_PRODUCT; $config->objectTables['productplan'] = TABLE_PRODUCTPLAN; @@ -409,6 +413,7 @@ $config->objectTables['zahost'] = TABLE_ZAHOST; $config->objectTables['zanode'] = TABLE_ZAHOST; $config->objectTables['automation'] = TABLE_AUTOMATION; $config->objectTables['stepResult'] = TABLE_TESTRUN; +$config->objectTables['scene'] = TABLE_SCENE; $config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme', 'visions'); $config->disabledFeatures = ''; diff --git a/module/action/config.php b/module/action/config.php index d7e9abdb8f..9bebf940c3 100755 --- a/module/action/config.php +++ b/module/action/config.php @@ -51,6 +51,7 @@ $config->action->objectNameFields['repo'] = 'name'; $config->action->objectNameFields['dataview'] = 'name'; $config->action->objectNameFields['zahost'] = 'name'; $config->action->objectNameFields['zanode'] = 'name'; +$config->action->objectNameFields['scene'] = 'title'; $config->action->commonImgSize = 870; diff --git a/module/action/lang/de.php b/module/action/lang/de.php index 9c78f6e716..44932f522d 100644 --- a/module/action/lang/de.php +++ b/module/action/lang/de.php @@ -48,6 +48,9 @@ $lang->action->byQuery = 'Search'; $lang->action->undeleteAction = 'Reset Data'; $lang->action->hideOneAction = 'Hide Data'; +$lang->action->refusecase = 'Before restoring the use case, please restore the scene to which the use case belongs'; +$lang->action->refusescene = ' Before restoring the use case, please restore the parent scene of this scene '; + $lang->action->trashTips = 'Hinweis: Alle Löschungen in ZenTao sind logische Löschungen.'; $lang->action->textDiff = 'Text Format'; $lang->action->original = 'Original Format'; @@ -156,6 +159,7 @@ $lang->action->objectTypes['stage'] = 'Stage'; $lang->action->objectTypes['patch'] = 'Patch'; $lang->action->objectTypes['repo'] = 'Repo'; $lang->action->objectTypes['dataview'] = 'Data View'; +$lang->action->objectTypes['scene'] = 'Scene'; /* Used to describe operation history. */ $lang->action->desc = new stdclass(); diff --git a/module/action/lang/en.php b/module/action/lang/en.php index 11a1112438..c0810d403c 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -48,6 +48,9 @@ $lang->action->byQuery = 'Search'; $lang->action->undeleteAction = 'Reset Data'; $lang->action->hideOneAction = 'Hide Data'; +$lang->action->refusecase = 'Before restoring the use case, please restore the scene to which the use case belongs'; +$lang->action->refusescene = ' Before restoring the use case, please restore the parent scene of this scene '; + $lang->action->trashTips = 'Note: Delete in ZenTao is logic.'; $lang->action->textDiff = 'Text Format'; $lang->action->original = 'Original Format'; @@ -156,6 +159,7 @@ $lang->action->objectTypes['stage'] = 'Stage'; $lang->action->objectTypes['patch'] = 'Patch'; $lang->action->objectTypes['repo'] = 'Repo'; $lang->action->objectTypes['dataview'] = 'Data View'; +$lang->action->objectTypes['scene'] = 'Scene'; /* Used to describe operation history. */ $lang->action->desc = new stdclass(); diff --git a/module/action/lang/fr.php b/module/action/lang/fr.php index 9b83041321..d11a2655ac 100644 --- a/module/action/lang/fr.php +++ b/module/action/lang/fr.php @@ -48,6 +48,9 @@ $lang->action->byQuery = 'Search'; $lang->action->undeleteAction = 'Reset Data'; $lang->action->hideOneAction = 'Hide Data'; +$lang->action->refusecase = 'Before restoring the use case, please restore the scene to which the use case belongs'; +$lang->action->refusescene = ' Before restoring the use case, please restore the parent scene of this scene '; + $lang->action->trashTips = 'Note: Les suppressions dans ZenTao sont purement logiques.'; $lang->action->textDiff = 'Text Format'; $lang->action->original = 'Original Format'; @@ -156,6 +159,7 @@ $lang->action->objectTypes['stage'] = 'Stage'; $lang->action->objectTypes['patch'] = 'Patch'; $lang->action->objectTypes['repo'] = 'Repo'; $lang->action->objectTypes['dataview'] = 'Data View'; +$lang->action->objectTypes['scene'] = 'Scene'; /* Used to describe operation history. */ $lang->action->desc = new stdclass(); diff --git a/module/action/lang/vi.php b/module/action/lang/vi.php index 32c4719635..fcbf931b03 100644 --- a/module/action/lang/vi.php +++ b/module/action/lang/vi.php @@ -48,6 +48,9 @@ $lang->action->history->old = 'Giá trị cũ'; $lang->action->history->new = 'Giá trị mới'; $lang->action->history->diff = 'So sánh'; +$lang->action->refusecase = 'Before restoring the use case, please restore the scene to which the use case belongs'; +$lang->action->refusescene = ' Before restoring the use case, please restore the parent scene of this scene '; + $lang->action->dynamic = new stdclass(); $lang->action->dynamic->today = 'Hôm nay'; $lang->action->dynamic->yesterday = 'Hôm qua'; @@ -123,6 +126,7 @@ $lang->action->objectTypes['stage'] = 'Stage'; $lang->action->objectTypes['patch'] = 'Patch'; $lang->action->objectTypes['repo'] = 'Repo'; $lang->action->objectTypes['dataview'] = 'Data View'; +$lang->action->objectTypes['scene'] = 'Scene'; /* Used to describe operation history. */ $lang->action->desc = new stdclass(); diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index cda9c316ed..6fad56d7e4 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -48,6 +48,9 @@ $lang->action->byQuery = '搜索'; $lang->action->undeleteAction = '还原数据'; $lang->action->hideOneAction = '隐藏数据'; +$lang->action->refusecase = '还原用例之前,请先还原该用例所属场景'; +$lang->action->refusescene = '还原场景之前,请先还原该场景的父场景'; + $lang->action->trashTips = '提示:为了保证系统的完整性,禅道系统的删除都是标记删除。'; $lang->action->textDiff = '文本格式'; $lang->action->original = '原始格式'; @@ -156,6 +159,8 @@ $lang->action->objectTypes['stage'] = '阶段'; $lang->action->objectTypes['patch'] = '补丁'; $lang->action->objectTypes['repo'] = '代码库'; $lang->action->objectTypes['dataview'] = '中间表'; +$lang->action->objectTypes['scene'] = '场景'; + /* 用来描述操作历史记录。*/ $lang->action->desc = new stdclass(); @@ -793,6 +798,7 @@ $lang->action->search->objectTypeList['testsuite'] = '套件'; $lang->action->search->objectTypeList['caselib'] = '公共库'; $lang->action->search->objectTypeList['testreport'] = '报告'; + /* 用来在动态显示中显示动作 */ $lang->action->search->label[''] = ''; $lang->action->search->label['created'] = $lang->action->label->created; diff --git a/module/action/lang/zh-tw.php b/module/action/lang/zh-tw.php index c73fff5912..717dc3fd9e 100755 --- a/module/action/lang/zh-tw.php +++ b/module/action/lang/zh-tw.php @@ -46,6 +46,9 @@ $lang->action->comment = '備註'; $lang->action->undeleteAction = '還原數據'; $lang->action->hideOneAction = '隱藏數據'; +$lang->action->refusecase = 'Before restoring the use case, please restore the scene to which the use case belongs'; +$lang->action->refusescene = ' Before restoring the use case, please restore the parent scene of this scene '; + $lang->action->trashTips = '提示:為了保證系統的完整性,禪道系統的刪除都是標記刪除。'; $lang->action->textDiff = '文本格式'; $lang->action->original = '原始格式'; @@ -136,6 +139,7 @@ $lang->action->objectTypes['kanbancolumn'] = '看板列'; $lang->action->objectTypes['kanbancard'] = '看板卡片'; $lang->action->objectTypes['repo'] = '代码库'; $lang->action->objectTypes['dataview'] = '数据表'; +$lang->action->objectTypes['scene'] = 'Scene'; /* 用來描述操作歷史記錄。*/ $lang->action->desc = new stdclass(); diff --git a/module/action/model.php b/module/action/model.php index d75c6a0b65..ed7fdd065d 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -1886,6 +1886,29 @@ class actionModel extends model if($release->shadow) $this->dao->update(TABLE_BUILD)->set('deleted')->eq(0)->where('id')->eq($release->shadow)->exec(); } + if($action->objectType == 'case') + { + $case = $this->dao->select('*')->from(TABLE_CASE)->where('id')->eq($action->objectID)->fetch(); + if($case->scene) + { + $scene = $this->dao->select('*')->from(VIEW_SCENECASE)->where('id')->eq($case->scene)->fetch(); + if($scene->deleted) + { + return print(js::error($this->lang->action->refusecase)); + } + } + } + + if($action->objectType == 'scene') + { + $scene = $this->dao->select('*')->from("zt_scene")->where('id')->eq($action->objectID)->fetch(); + if($scene->parent) + { + $scenerow = $this->dao->select('*')->from(VIEW_SCENECASE)->where('id')->eq($scene->parent)->fetch(); + if($scenerow->deleted) return print(js::error($this->lang->action->refusescene)); + } + } + /* Update deleted field in object table. */ $table = $this->config->objectTables[$action->objectType]; $this->dao->update($table)->set('deleted')->eq(0)->where('id')->eq($action->objectID)->exec(); diff --git a/module/action/view/trash.html.php b/module/action/view/trash.html.php index 80ae3a9bff..909571d76e 100755 --- a/module/action/view/trash.html.php +++ b/module/action/view/trash.html.php @@ -101,51 +101,66 @@ objectType == 'case' ? 'testcase' : $action->objectType;?> action->objectTypes, $action->objectType, '');?> - objectID;?> - - objectType == 'user' ? "account={$action->objectName}" : "id={$action->objectID}"; - $methodName = 'view'; - if($module == 'caselib') - { - $methodName = 'view'; - $module = 'caselib'; - } - if($module == 'basicmeas') - { - $module = 'measurement'; - $methodName = 'setSQL'; - $params = "id={$action->objectID}"; - } - if($action->objectType == 'api') - { - $params = "libID=0&moduelID=0&apiID={$action->objectID}"; - $methodName = 'index'; - } - if(strpos('traincourse,traincontents', $module) !== false) - { - $methodName = $module == 'traincourse' ? 'viewcourse' : 'viewchapter'; - $module = 'traincourse'; - } - if(isset($config->action->customFlows[$action->objectType])) - { - $flow = $config->action->customFlows[$action->objectType]; - $module = $flow->module; - } - if(strpos($this->config->action->noLinkModules, ",{$module},") !== false) - { - echo $action->objectName; - } - else - { - $tab = ''; - $canView = common::hasPriv($module, $methodName); - if($action->objectType == 'meeting') $tab = $action->project ? "data-app='project'" : "data-app='my'"; - if($module == 'requirement') $module = 'story'; - echo $canView ? html::a($this->createLink($module, $methodName, $params), $action->objectName, '_self', "title='{$action->objectName}' $tab") : "$action->objectName"; - } + objectType == 'scene'){ ?> + objectID,1); + //$showid = preg_replace('/^0+/', '', $showid); ?> - + objectID;?> + + objectID;?> + + + objectType == 'scene'){ ?> + + objectName;?> + + + + objectType == 'user' ? "account={$action->objectName}" : "id={$action->objectID}"; + $methodName = 'view'; + if($module == 'caselib') + { + $methodName = 'view'; + $module = 'caselib'; + } + if($module == 'basicmeas') + { + $module = 'measurement'; + $methodName = 'setSQL'; + $params = "id={$action->objectID}"; + } + if($action->objectType == 'api') + { + $params = "libID=0&moduelID=0&apiID={$action->objectID}"; + $methodName = 'index'; + } + if(strpos('traincourse,traincontents', $module) !== false) + { + $methodName = $module == 'traincourse' ? 'viewcourse' : 'viewchapter'; + $module = 'traincourse'; + } + if(isset($config->action->customFlows[$action->objectType])) + { + $flow = $config->action->customFlows[$action->objectType]; + $module = $flow->module; + } + if(strpos($this->config->action->noLinkModules, ",{$module},") !== false) + { + echo $action->objectName; + } + else + { + $tab = ''; + $canView = common::hasPriv($module, $methodName); + if($action->objectType == 'meeting') $tab = $action->project ? "data-app='project'" : "data-app='my'"; + if($module == 'requirement') $module = 'story'; + echo $canView ? html::a($this->createLink($module, $methodName, $params), $action->objectName, '_self', "title='{$action->objectName}' $tab") : "$action->objectName"; + } + ?> + + project]->name;?> diff --git a/module/common/view/mindmap.html.php b/module/common/view/mindmap.html.php new file mode 100644 index 0000000000..9df19feacd --- /dev/null +++ b/module/common/view/mindmap.html.php @@ -0,0 +1,6 @@ +getExtViewFile(__FILE__)){include $extView; return helper::cd();}?> + diff --git a/module/datatable/model.php b/module/datatable/model.php index 377c925f99..c00c6cb654 100644 --- a/module/datatable/model.php +++ b/module/datatable/model.php @@ -75,7 +75,18 @@ class datatableModel extends model $module = zget($this->config->datatable->moduleAlias, "$module-$method", $module); if(!isset($this->config->$module)) $this->loadModel($module); - if(isset($this->config->datatable->$datatableId->$key)) $setting = json_decode($this->config->datatable->$datatableId->$key); + $setting = ""; + if(isset($this->config->datatable->$datatableId->$key)) + { + if($datatableId == 'testcaseBrowse' && $key == 'tablecols' && $this->cookie->onlyScene) + { + $setting = json_decode('[{"id":"id","order":1,"show":true,"width":"70px","fixed":"left"},{"id":"title","order":2,"show":true,"width":"auto","fixed":"left"},{"id":"openedBy","order":8,"show":true,"width":"80px","fixed":"no"},{"id":"openedDate","order":9,"show":true,"width":"90px","fixed":"no"},{"id":"lastEditedBy","order":16,"show":true,"width":"80px","fixed":"no"},{"id":"lastEditedDate","order":17,"show":true,"width":"90px","fixed":"no"},{"id":"actions","order":23,"show":true,"width":"150px","fixed":"right"}]'); + } + else + { + $setting = json_decode($this->config->datatable->$datatableId->$key); + } + } $fieldList = $this->getFieldList($module); if(empty($setting)) diff --git a/module/my/model.php b/module/my/model.php index 92f9060ccc..4e18b1ef6d 100644 --- a/module/my/model.php +++ b/module/my/model.php @@ -416,12 +416,15 @@ class myModel extends model ->orderBy('order_asc') ->fetchPairs(); + $scene = $this->loadModel('testcase')->getSceneMenu(0, 0); + $queryName = $type == 'contribute' ? 'contributeTestcase' : 'workTestcase'; $this->app->loadConfig('testcase'); $this->config->testcase->search['module'] = $queryName; $this->config->testcase->search['queryID'] = $queryID; $this->config->testcase->search['actionURL'] = $actionURL; $this->config->testcase->search['params']['product']['values'] = array('' => '') + $products; + $this->config->testcase->search['params']['scene']['values'] = array('' => '') + $scene; $this->config->testcase->search['params']['lib']['values'] = array('' => '') + $this->loadModel('caselib')->getLibraries(); unset($this->config->testcase->search['fields']['module']); diff --git a/module/search/model.php b/module/search/model.php index 695a4ab088..5375b77f24 100644 --- a/module/search/model.php +++ b/module/search/model.php @@ -189,6 +189,11 @@ class searchModel extends model $allDepts = $this->loadModel('dept')->getAllChildId($value); $condition = helper::dbIN($allDepts); } + elseif($this->post->$fieldName == 'scene') + { + $allScenes = $value === '0' ? array() : ($value === '' ? array(0) : $this->loadModel('testcase')->getAllChildId($value)); + if(count($allScenes)) $condition = helper::dbIN($allScenes); + } else { $condition = ' = ' . $this->dbh->quote($value) . ' '; diff --git a/module/testcase/config.php b/module/testcase/config.php index 283c36ac00..226489ecde 100644 --- a/module/testcase/config.php +++ b/module/testcase/config.php @@ -235,3 +235,16 @@ $config->testcase->datatable->fieldList['actions']['fixed'] = 'right'; $config->testcase->datatable->fieldList['actions']['width'] = '180'; $config->testcase->datatable->fieldList['actions']['required'] = 'yes'; $config->testcase->datatable->fieldList['actions']['sort'] = 'no'; + + +$config->testcase->search['module'] = 'testcase'; +$config->testcase->search['fields']['scene'] = $lang->testcase->iScene; +$config->testcase->search['params']['scene'] = array('operator' => 'belong', 'control' => 'select', 'values' => ''); + +$config->testcase->createscene = new stdclass(); +$config->testcase->createscene->requiredFields = 'title'; + +$config->testcase->customBatchCreateFields = 'module,scene,stage,story,pri,precondition,keywords,review'; +$config->testcase->customBatchEditFields = 'module,scene,story,stage,precondition,status,pri,keywords'; +$config->testcase->custom->batchCreateFields = 'module,scene,story,%s'; +$config->testcase->custom->batchEditFields = 'branch,module,scene,stage,status,pri,story'; diff --git a/module/testcase/control.php b/module/testcase/control.php index a7334900f1..aa6e9dc377 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -123,6 +123,7 @@ class testcase extends control $hasProduct = $this->dao->findById($projectID)->from(TABLE_PROJECT)->fetch('hasProduct'); if(!$hasProduct) unset($this->config->testcase->search['fields']['product']); + $branch = intval($branch) > 0 ? $branch : 'all'; $this->loadModel('project')->setMenu($projectID); } else @@ -136,6 +137,8 @@ class testcase extends control $this->session->set('moduleID', $moduleID); $this->session->set('browseType', $browseType); $this->session->set('orderBy', $orderBy); + $this->session->set('testcaseOrderBy', '`sort` asc', $this->app->tab); + $this->session->set('testcaseOrderBy', '`sort` asc'); /* Load lang. */ $this->app->loadLang('testtask'); @@ -144,21 +147,60 @@ class testcase extends control $this->app->loadClass('pager', $static = true); $pager = new pager($recTotal, $recPerPage, $pageID); $sort = common::appendOrder($orderBy); + $cases = $this->testcase->getTestCases($productID, $branch, $browseType, $browseType == 'bysearch' ? $queryID : $suiteID, $moduleID, $caseType, $sort, null); - /* Get test cases. */ - $cases = $this->testcase->getTestCases($productID, $branch, $browseType, $browseType == 'bysearch' ? $queryID : $suiteID, $moduleID, $caseType, $sort, $pager); - if(empty($cases) and $pageID > 1) + $caseIdList = array(); + if(!empty($cases)) { - $pager = pager::init(0, $recPerPage, 1); - $cases = $this->testcase->getTestCases($productID, $branch, $browseType, $browseType == 'bysearch' ? $queryID : $suiteID, $moduleID, $caseType, $sort, $pager); + foreach($cases as $case) $caseIdList[] = $case->id; + } + /* Get top level cases and scenes.*/ + $productParam = $productID; + if(intval($productID) <= 0) + { + $productParam = array_keys($this->products); + if(count($productParam) > 1) unset($productParam[0]); } - /* save session .*/ - $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', false); + $queryCondition = ''; + $topObjects = $this->testcase->getList($productParam,$branch, $moduleID, $caseIdList, $pager, 'top', array(), $browseType); + + /* Get children cases and scenes.*/ + $scenes = $this->testcase->getList($productParam, $branch,$moduleID, $caseIdList, null, 'child', array_keys($topObjects), $browseType, $queryCondition); + if(empty($topObjects) and $pageID > 1) + { + $pager = pager::init(0, $recPerPage, 1); + $topObjects = $this->testcase->getList($productParam,$branch, $moduleID, $caseIdList, $pager,'top',array(),$browseType); + $scenes = $this->testcase->getList($productParam,$branch, $moduleID, $caseIdList, null, 'child', array_keys($topObjects),$browseType, $queryCondition); + } + + /* Save SQL for execution. */ + $this->loadModel('common')->saveQueryCondition($queryCondition, 'testcase', false); + + /* Get summary. */ + $indCount = 0; + $topCount = 0; + $casesCount = 0; + // print_r($scenes);die; + foreach($scenes as $scene) + { + if($scene->isCase == '2' and $scene->parent == 0) $topCount ++; + if($scene->isCase == '1' and $scene->parent == 0) $indCount ++; + if($scene->isCase == '1') $casesCount ++; + } + + if($this->cookie->onlyScene) + { + $summary = sprintf($this->lang->testcase->summaryScene, $topCount); + } + else + { + $summary = sprintf($this->lang->testcase->summary, $topCount, $indCount); + } /* Process case for check story changed. */ - $cases = $this->loadModel('story')->checkNeedConfirm($cases); - $cases = $this->testcase->appendData($cases); + $scenes = $this->loadModel('story')->checkNeedConfirm($scenes); + $scenes = $this->testcase->appendData($scenes); /* Build the search form. */ $currentModule = $this->app->tab == 'project' ? 'project' : 'testcase'; @@ -211,11 +253,13 @@ class testcase extends control $this->view->product = $product; $this->view->productName = $this->products[$productID]; $this->view->modules = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, $branch == 'all' ? '0' : $branch); + $this->view->iscenes = $this->testcase->getSceneMenu($productID, $moduleID, $viewType = 'case', $startSceneID = 0, 0); $this->view->moduleTree = $moduleTree; $this->view->moduleName = $moduleID ? $tree->name : $this->lang->tree->all; $this->view->moduleID = $moduleID; $this->view->projectType = !empty($projectID) ? $this->dao->select('model')->from(TABLE_PROJECT)->where('id')->eq($projectID)->fetch('model') : ''; - $this->view->summary = $this->testcase->summary($cases); + // $this->view->summary = $this->testcase->summary($cases); + $this->view->summary = $summary; $this->view->pager = $pager; $this->view->users = $this->user->getPairs('noletter'); $this->view->orderBy = $orderBy; @@ -233,6 +277,8 @@ class testcase extends control $this->view->showBranch = $showBranch; $this->view->libraries = $this->loadModel('caselib')->getLibraries(); $this->view->automation = $this->loadModel('zanode')->getAutomationByProduct($productID); + $this->view->scenes = $scenes; + $this->view->casesCount = $casesCount; $this->display(); } @@ -399,6 +445,7 @@ class testcase extends control $response['result'] = 'success'; setcookie('lastCaseModule', (int)$this->post->module, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, false); + setcookie('lastCaseScene', (int)$this->post->scene, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, false); $caseResult = $this->testcase->create($bugID); if(!$caseResult or dao::isError()) { @@ -444,6 +491,7 @@ class testcase extends control $type = 'feature'; $stage = ''; $pri = 3; + $scene = 0; $caseTitle = ''; $precondition = ''; $keywords = ''; @@ -459,6 +507,7 @@ class testcase extends control $type = $testcase->type ? $testcase->type : 'feature'; $stage = $testcase->stage; $pri = $testcase->pri; + $scene = $testcase->scene; $storyID = $testcase->story; $caseTitle = $testcase->title; $precondition = $testcase->precondition; @@ -538,6 +587,8 @@ class testcase extends control } $currentModuleID = $moduleID ? (int)$moduleID : (int)$this->cookie->lastCaseModule; + $currentSceneID = (int)$this->cookie->lastCaseScene; + if($testcaseID > 0) $currentSceneID = $scene; /* Get the status of stories are not closed. */ $modules = array(); if($currentModuleID) @@ -572,6 +623,7 @@ class testcase extends control $this->view->productName = $this->products[$productID]; $this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch); $this->view->currentModuleID = $currentModuleID; + $this->view->currentSceneID = $currentSceneID; $this->view->gobackLink = (isset($output['from']) and $output['from'] == 'global') ? $this->createLink('testcase', 'browse', "productID=$productID") : ''; $this->view->stories = $stories; $this->view->caseTitle = $caseTitle; @@ -589,6 +641,7 @@ class testcase extends control $this->view->product = $product; $this->view->branches = $branches; $this->view->auto = $auto; + $this->view->sceneOptionMenu = $this->testcase->getSceneMenu($productID, $moduleID, $viewType = 'case', $startSceneID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch); $this->display(); } @@ -692,6 +745,8 @@ class testcase extends control $moduleOptionMenu = $this->tree->getOptionMenu($productID, 'case', 0, $branch === 'all' ? 0 : $branch); $moduleOptionMenu['ditto'] = $this->lang->testcase->ditto; + $sceneOptionMenu = $this->testcase->getSceneMenu($productID, $moduleID, $viewType = 'case', $startSceneID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch); + $sceneOptionMenu['ditto'] = $this->lang->testcase->ditto; $this->view->customFields = $customFields; $this->view->showFields = $showFields; @@ -707,6 +762,8 @@ class testcase extends control $this->view->productName = $this->products[$productID]; $this->view->moduleOptionMenu = $moduleOptionMenu; $this->view->currentModuleID = $currentModuleID; + $this->view->sceneOptionMenu = $sceneOptionMenu; + $this->view->currentSceneID = 0; $this->view->branch = $branch; $this->view->branches = $branches; $this->view->needReview = $this->testcase->forceNotReview() == true ? 0 : 1; @@ -1014,6 +1071,10 @@ class testcase extends control $this->view->moduleOptionMenu = $moduleOptionMenu; $this->view->stories = $stories; } + + $sceneOptionMenu = $this->testcase->getSceneMenu($productID, $case->module, $viewType = 'case', $startSceneID = 0, 0 ); + if(!isset($sceneOptionMenu[$case->scene])) $sceneOptionMenu += $this->testcase->getScenesName($case->scene); + $forceNotReview = $this->testcase->forceNotReview(); if($forceNotReview) unset($this->lang->testcase->statusList['wait']); @@ -1025,6 +1086,8 @@ class testcase extends control $this->view->isLibCase = $isLibCase; $this->view->forceNotReview = $forceNotReview; $this->view->testtasks = $testtasks; + $this->view->sceneOptionMenu = $sceneOptionMenu; + $this->view->currentSceneID = $case->scene; $this->display(); } @@ -1194,7 +1257,14 @@ class testcase extends control } } + $scenePairs = array(0 => '/'); + foreach($cases as $case) + { + $scenePairs[$case->id] = $this->testcase->getSceneMenu($productID, $case->module, $viewType = 'case', $startSceneID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch);; + } + /* Assign. */ + $this->view->scenePairs = $scenePairs; $this->view->position[] = $this->lang->testcase->common; $this->view->position[] = $this->lang->testcase->batchEdit; $this->view->caseIDList = $caseIDList; @@ -1316,7 +1386,18 @@ class testcase extends control if(!$this->post->caseIDList) return print(js::locate($this->session->caseList)); $caseIDList = array_unique($this->post->caseIDList); - foreach($caseIDList as $caseID) $this->testcase->delete(TABLE_CASE, $caseID); + foreach($caseIDList as $caseID) + { + $vs = $this->dao->findById((int)$caseID)->from(VIEW_SCENECASE)->fetch(); + if($vs->isCase == 2) + { + $this->testcase->delete(TABLE_SCENE, $caseID-CHANGEVALUE); + } + else + { + $this->testcase->delete(TABLE_CASE, $caseID); + } + } echo js::locate($this->session->caseList); } @@ -1363,11 +1444,25 @@ class testcase extends control unset($_POST['caseIDList']); $allChanges = $this->testcase->batchChangeModule($caseIDList, $moduleID); if(dao::isError()) return print(js::error(dao::getError())); - foreach($allChanges as $caseID => $changes) + + if (!empty($allChanges[1])) { - $this->loadModel('action'); - $actionID = $this->action->create('case', $caseID, 'Edited'); - $this->action->logHistory($actionID, $changes); + foreach($allChanges[1] as $caseID => $changes) + { + $this->loadModel('action'); + $actionID = $this->action->create('case', $caseID, 'Edited'); + $this->action->logHistory($actionID, $changes); + } + } + + if (!empty($allChanges[0])) + { + foreach($allChanges[0] as $sceneID => $changes) + { + $this->loadModel('action'); + $actionID = $this->action->create('scene', $sceneID, 'Edited'); + $this->action->logHistory($actionID, $changes); + } } } @@ -2457,4 +2552,482 @@ class testcase extends control { echo $this->dao->select('count(id) as count')->from(TABLE_CASE)->where('status')->eq('wait')->fetch('count'); } + + /** + * Create Scene. + * + * @param int $productID + * @param int $branch + * @param int $moduleID + * @param string $from + * @param string $param + * @param int $storyID + * @param string $extras + * @access public + * @return void + */ + public function createScene($productID, $branch = '', $moduleID = 0, $from = '', $param = 0, $storyID = 0, $extras = '') + { + if(!empty($_POST)) + { + $response['result'] = 'success'; + setcookie('lastCaseScene', (int)$this->post->parent, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, false); + + $sceneResult = $this->testcase->createScene(); + if(!$sceneResult or dao::isError()) + { + $response['result'] = 'fail'; + $response['message'] = dao::getError(); + return $this->send($response); + } + + $sceneID = $sceneResult['id']; + $this->loadModel('action'); + $this->action->create('scene', $sceneID, 'Opened'); + + $useSession = $this->app->tab != 'qa' and $this->session->caseList and strpos($this->session->caseList, 'dynamic') === false; + $response['locate'] = $useSession ? $this->session->caseList : $this->createLink('testcase', 'browse', "root={$this->post->product}&branch=$branch&type=byModule¶m=" . $this->post->module); + $response['message'] = $this->lang->saveSuccess; + + return $this->send($response); + } + + /* Set menu. */ + if(empty($this->products)) $this->locate($this->createLink('product', 'create')); + $this->qa->setMenu($this->products, $productID, $branch); + + /* Set branch. */ + $product = $this->product->getById($productID); + if(!isset($this->products[$productID])) $this->products[$productID] = $product->name; + if($this->app->tab == 'execution' or $this->app->tab == 'project') + { + $objectID = $this->app->tab == 'project' ? $this->session->project : $executionID; + $productBranches = (isset($product->type) and $product->type != 'normal') ? $this->execution->getBranchByProduct($productID, $objectID, 'noclosed|withMain') : array(); + $branches = isset($productBranches[$productID]) ? $productBranches[$productID] : array(); + $branch = key($branches); + } + else + { + $branches = (isset($product->type) and $product->type != 'normal') ? $this->loadModel('branch')->getPairs($productID, 'active') : array(); + } + + $currentModuleID = $moduleID ? (int)$moduleID : (int)$this->cookie->lastCaseModule; + $currentParentID = (int)$this->cookie->lastCaseScene; + + $modules = array(); + if($currentModuleID) + { + $productModules = $this->tree->getOptionMenu($productID, 'story'); + $storyModuleID = array_key_exists($currentModuleID, $productModules) ? $currentModuleID : 0; + $modules = $this->loadModel('tree')->getStoryModule($storyModuleID); + $modules = $this->tree->getAllChildID($modules); + } + + $this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->testcase->newScene; + $this->view->productID = $productID; + $this->view->currentModuleID = $currentModuleID; + $this->view->currentParentID = $currentParentID; + $this->view->gobackLink = (isset($output['from']) and $output['from'] == 'global') ? $this->createLink('testcase', 'browse', "productID=$productID") : ''; + $this->view->showFields = $this->config->testcase->custom->createFields; + $this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch); + $this->view->branch = $branch; + $this->view->product = $product; + $this->view->branches = $branches; + $this->view->sceneTitle = ''; + $this->view->sceneOptionMenu = $this->testcase->getSceneMenu($productID, $moduleID, $viewType = 'case', $startSceneID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch); + + $this->display(); + } + + public function ajaxGetModuleScenes($productID, $branch = 0, $moduleID = 0, $stype = 1, $storyID = 0, $onlyOption = 'false', $status = '', $limit = 0, $type = 'full', $hasParent = 1, $number = '', $currentScene = 0) + { + $optionMenu = $this->testcase->getSceneMenu($productID, $moduleID, 'case', 0, $branch, $currentScene); + $output = ($stype == 1) ? html::select("parent", $optionMenu, "", "class='form-control'") : $output = html::select("scene".$number, $optionMenu, array('' => ''), "class='form-control'"); + + die($output); + } + + public function ajaxGetOptionMenu($rootID, $viewType = 'story', $branch = 0, $rootModuleID = 0, $returnType = 'html', $fieldID = '', $needManage = false, $extra = '', $currentModuleID = 0) + { + $this->loadModel('tree'); + + if($viewType == 'task') + { + $optionMenu = $this->tree->getTaskOptionMenu($rootID, 0, 0, $extra); + } + else + { + $optionMenu = $this->tree->getOptionMenu($rootID, $viewType, $rootModuleID, $branch); + } + + if($returnType == 'html') + { + if($viewType == 'line') + { + $lineID = $this->dao->select('id')->from(TABLE_MODULE)->where('type')->eq('line')->andWhere('deleted')->eq(0)->orderBy('id_desc')->limit(1)->fetch('id'); + $output = html::select("line", $optionMenu, $lineID, "class='form-control'"); + $output .= ""; + $output .= html::a($this->createLink('tree', 'browse', "rootID=$rootID&view=$viewType¤tModuleID=0&branch=$branch", '', true), $viewType == 'line' ? $this->lang->tree->manageLine : $this->lang->tree->manage, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'"); + $output .= ''; + } + else + { + $changeFunc = ''; + if($viewType == 'bug' or $viewType == 'case') $changeFunc = "onchange='loadModuleRelatedNew()'"; + if($viewType == 'task') $changeFunc = ""; + $field = $fieldID ? "modules[$fieldID]" : 'module'; + + $currentModule = $this->tree->getById($currentModuleID); + $currentModuleID = (isset($currentModule->branch) and $currentModule->branch == 0) ? $currentModuleID : 0; + + $output = html::select("$field", $optionMenu, $currentModuleID, "class='form-control' $changeFunc"); + if(count($optionMenu) == 1 and $needManage) + { + $output .= ""; + $output .= html::a($this->createLink('tree', 'browse', "rootID=$rootID&view=$viewType¤tModuleID=0&branch=$branch", '', true), $this->lang->tree->manage, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'"); + $output .= '  '; + $output .= html::a("javascript:void(0)", $this->lang->refreshIcon, '', "class='refresh' onclick='loadProductModulesNew($rootID)'"); + $output .= ''; + } + } + die($output); + } + + if($returnType == 'mhtml') + { + $changeFunc = ''; + if($viewType == 'bug' or $viewType == 'case') $changeFunc = "onchange='loadModuleRelatedNew()'"; + if($viewType == 'task') $changeFunc = ""; + $field = $fieldID ? "modules[$fieldID]" : 'module'; + $output = html::select("$field", $optionMenu, '', "class='input' $changeFunc"); + die($output); + } + + if($returnType == 'json') die(json_encode($optionMenu)); + } + + public function ajaxGetScenesForBC($productID, $branch = 0, $moduleID = 0, $stype = 1, $storyID = 0, $onlyOption = 'false', $status = '', $limit = 0, $type = 'full', $hasParent = 1, $number = '', $currentScene = 0) + { + $optionMenu = $this->testcase->getSceneMenu($productID, $moduleID, 'case', 0, $branch, $currentScene); + $optionMenu['ditto'] = $this->lang->testcase->ditto; + + if($stype == 1) + { + $output = html::select("parent", $optionMenu, "", "class='form-control'"); + } + else + { + $output = html::select("scene" . $number, $optionMenu, array('' => ''), "class='form-control'"); + } + + die($output); + } + + public function batchChangeScene($sceneId) + { + if($this->post->caseIDList) + { + $caseIDList = $this->post->caseIDList; + $caseIDList = array_unique($caseIDList); + unset($_POST['caseIDList']); + $allChanges = $this->testcase->batchChangeScene($caseIDList, $sceneId); + if(dao::isError()) return print(js::error(dao::getError())); + + if(!empty($allChanges[1])) + { + foreach($allChanges[1] as $caseID => $changes) + { + $this->loadModel('action'); + $actionID = $this->action->create('case', $caseID, 'Edited'); + } + } + + if(!empty($allChanges[0])) + { + foreach($allChanges[0] as $sceneID => $changes) + { + $this->loadModel('action'); + $actionID = $this->action->create('scene', $sceneID - CHANGEVALUE, 'Edited'); + } + } + } + + echo js::locate($this->session->caseList, 'parent'); + } + + public function changeScene() + { + $now = helper::now(); + $currentID = $this->post->sourceId; + $targetId = $this->post->targetId; + + $targetScene = $this->dao->findById((int)$targetId)->from(VIEW_SCENECASE)->fetch(); + $currentScene = $this->dao->findById((int)$currentID)->from(VIEW_SCENECASE)->fetch(); + + $product = $targetScene->product; + $module = $targetScene->module; + if($targetScene->isCase == 2) + { + if($currentScene->isCase == 2) + { + $childPath = $targetScene->path . "$currentID,"; + $grade = $targetScene->grade + 1; + + $this->dao->update(TABLE_SCENE)->set('parent')->eq($targetId) + ->set('path')->eq($childPath) + ->set('grade')->eq($grade) + ->set('product')->eq($product) + ->set('module')->eq($module) + ->set('lastEditedBy')->eq($this->app->user->account) + ->set('lastEditedDate')->eq($now) + ->where('id')->eq($currentID-CHANGEVALUE) + ->exec(); + + $children = $this->dao->select('*')->from(VIEW_SCENECASE) + ->where('deleted')->eq(0) + ->andWhere('path')->like("%,$currentID,%") + ->orderBy('grade') + ->fetchAll('id'); + + foreach($children as $id => $childScene) + { + if($id && $id != $currentID) + { + if($childScene->parent) + { + $parentScene = $this->dao->findById((int)$childScene->parent-CHANGEVALUE)->from(TABLE_SCENE)->fetch(); + if($childScene->isCase == 2) + { + if($childScene->grade > $currentScene->grade) + { + $childPath = $parentScene->path . "$id,"; + $grade = $parentScene->grade + 1; + + $this->dao->update(TABLE_SCENE)->set('path')->eq($childPath) + ->set('grade')->eq($grade) + ->set('product')->eq($parentScene->product) + ->set('module')->eq($parentScene->module) + ->set('lastEditedBy')->eq($this->app->user->account) + ->set('lastEditedDate')->eq($now) + ->where('id')->eq($id - CHANGEVALUE) + ->exec(); + } + } + else + { + $this->dao->update(TABLE_CASE) + ->set('product')->eq($parentScene->product) + ->set('module')->eq($parentScene->module) + ->set('lastEditedBy')->eq($this->app->user->account) + ->set('lastEditedDate')->eq($now) + ->where('id')->eq($id) + ->exec(); + } + } + } + } + } + else + { + $this->dao->update(TABLE_CASE)->set('scene')->eq($targetId) + ->set('product')->eq($product) + ->set('module')->eq($module) + ->set('lastEditedBy')->eq($this->app->user->account) + ->set('lastEditedDate')->eq($now) + ->where('id')->eq($currentID) + ->exec(); + } + } + } + + public function deleteScene($sceneID, $confirm = 'no') + { + $scene = $this->dao->select('*')->from(VIEW_SCENECASE)->where('id')->eq($sceneID)->andWhere('isCase')->eq(2)->fetch(); + + if($confirm == 'no') return print(js::confirm(sprintf($this->lang->testcase->confirmDeleteScene,addslashes($scene->title)), $this->createLink('testcase', 'deleteScene', "sceneID=$sceneID&confirm=yes"))); + + $childrenCount = $this->dao->select('count(*) as count')->from(VIEW_SCENECASE)->where('parent')->eq($sceneID)->andWhere('deleted')->eq(0)->fetch('count'); + if($childrenCount) + { + if($confirm != "wait") return print(js::confirm(sprintf($this->lang->testcase->hasChildren), $this->createLink('testcase', 'deleteScene', "sceneID=$sceneID&confirm=wait"))); + $all = $this->dao->select('id,isCase')->from(VIEW_SCENECASE)->where('path')->like($scene->path . '%')->andWhere('deleted')->eq(0)->fetchAll(); + + foreach($all as $v) + { + if($v->isCase == 2) + { + $this->testcase->delete(TABLE_SCENE, $v->id - CHANGEVALUE); + } + else + { + $this->testcase->delete(TABLE_CASE, $v->id); + } + } + } + else + { + + $this->testcase->delete(TABLE_SCENE, $sceneID-CHANGEVALUE); + } + + echo js::reload('parent'); + } + + public function editScene($sceneID, $executionID = 0) + { + $this->loadModel('story'); + + /* Update scene. */ + if(!empty($_POST)) + { + $changes = array(); + $files = array(); + + $sceneResult = $this->testcase->updateScene($sceneID); + if(!$sceneResult or dao::isError()) + { + $response['result'] = 'fail'; + $response['message'] = dao::getError(); + return $this->send($response); + } + + $this->loadModel('action'); + $sceneID = $sceneResult['id']; + + $this->action->create('scene', $sceneID - CHANGEVALUE, 'Edited'); + $this->executeHooks($sceneID); + + if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'success', 'data' => $sceneID)); + + $scene = $this->dao->findById((int)$sceneID - CHANGEVALUE)->from(TABLE_SCENE)->fetch(); + $product = $scene->product; + $this->executeHooks($product); + + setcookie('caseModule', $scene->module, 0, $this->config->webRoot, '', $this->config->cookieSecure, false); + + if(!$scene->module) return print(js::locate($this->createLink('testcase', 'browse', "productID=$product"), 'parent')); + + return print(js::locate($this->createLink('testcase', 'browse', "root=$product&branch=0&type=byModule¶m={$scene->module}"), 'parent')); + } + + /* Get scene. */ + $scene = $this->dao->select('*')->from(VIEW_SCENECASE)->where('id')->eq($sceneID)->andWhere('isCase')->eq(2)->fetch(); + + if(!$scene) return print(js::error($this->lang->notFound) . js::locate('back')); + + $productID = $scene->product; + $product = $this->product->getById($productID); + if(!isset($this->products[$productID])) $this->products[$productID] = $product->name; + + $title = $this->products[$productID] . $this->lang->colon . $this->lang->testcase->editScene; + $position[] = html::a($this->createLink('testcase', 'browse', "productID=$productID"), $this->products[$productID]); + + /* Set menu. */ + if($this->app->tab == 'project') $this->loadModel('project')->setMenu(null); + if($this->app->tab == 'qa') $this->testcase->setMenu($this->products, $productID, $scene->branch); + + /* Display status of branch. */ + $branches = $this->loadModel('branch')->getList($productID, isset($objectID) ? $objectID : 0, 'all'); + $branchTagOption = array(); + foreach($branches as $branchInfo) + { + $branchTagOption[$branchInfo->id] = $branchInfo->name . ($branchInfo->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : ''); + } + + if(!isset($branchTagOption[$scene->branch])) + { + $caseBranch = $this->branch->getById($scene->branch, $scene->product, ''); + $branchTagOption[$scene->branch] = $scene->branch == BRANCH_MAIN ? $caseBranch : ($caseBranch->name . ($caseBranch->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '')); + } + + $moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, $scene->branch); + + if(!isset($moduleOptionMenu[$scene->module])) $moduleOptionMenu += $this->tree->getModulesName($scene->module); + + if($scene->module) + { + $sceneOptionMenu = $this->testcase->getSceneMenu($productID, $scene->module, $viewType = 'case', $startSceneID = 0, $scene->branch, $sceneID); + } + else + { + $sceneOptionMenu = $this->testcase->getSceneMenu($productID, 0, $viewType = 'case', $startSceneID = 0, $scene->branch, $sceneID); + } + + if(!isset($sceneOptionMenu[$scene->parent])) $sceneOptionMenu += $this->testcase->getScenesName($scene->parent); + + /* Get product and branches. */ + if($this->app->tab == 'execution' or $this->app->tab == 'project') + { + $objectID = $this->app->tab == 'project' ? $scene->project : $executionID; + } + + $this->view->productID = $productID; + $this->view->product = $product; + $this->view->products = $this->products; + $this->view->branchTagOption = $branchTagOption; + $this->view->productName = $this->products[$productID]; + $this->view->moduleOptionMenu = $moduleOptionMenu; + $this->view->sceneOptionMenu = $sceneOptionMenu; + $this->view->stories = $this->story->getProductStoryPairs($productID, $scene->branch, 0, 'all','id_desc', 0, 'full', 'story', false); + + $forceNotReview = $this->testcase->forceNotReview(); + if($forceNotReview) unset($this->lang->testcase->statusList['wait']); + + $this->view->title = $title; + $this->view->branch = 0; + $this->view->currentModuleID = $scene->module; + $this->view->currentParentID = $scene->parent; + $this->view->users = $this->user->getPairs('noletter'); + + $this->view->actions = $this->loadModel('action')->getList('case', $sceneID); + $this->view->gobackLink = (isset($output['from']) and $output['from'] == 'global') ? $this->createLink('testcase', 'browse', "productID=$productID") : ''; + $this->view->forceNotReview = $forceNotReview; + $this->view->scene = $scene; + $this->view->executionID = $executionID; + + $this->display(); + } + + /** + * Update order. + * + * @access public + * @return void + */ + public function updateOrder() + { + $idList = explode(',', trim($this->post->scenes, ',')); + $orderBy = $this->post->orderBy; + if(strpos($orderBy, 'sort') === false) return false; + + /* Get list of original scenes and cases, and sort with orderBy param. */ + $scenesMap = $this->dao->select('id,sort,isCase')->from(VIEW_SCENECASE)->where('id')->in($idList)->orderBy($orderBy)->fetchAll(id); + + foreach($scenesMap as $scene) + { + /* Compare with sorted list from front-end. */ + $newID = array_shift($idList); + if($scene->id == $newID) continue; + + /* Change sort value of scene. */ + if ($scenesMap[$newID]->isCase == 2) + { + $this->dao->update(TABLE_SCENE) + ->set('sort')->eq($scene->sort) + ->set('lastEditedBy')->eq($this->app->user->account) + ->set('lastEditedDate')->eq(helper::now()) + ->where('id')->eq($newID - CHANGEVALUE) + ->exec(); + continue; + } + + /* Change sort value of case. */ + $this->dao->update(TABLE_CASE) + ->set('sort')->eq($scene->sort) + ->set('lastEditedBy')->eq($this->app->user->account) + ->set('lastEditedDate')->eq(helper::now()) + ->where('id')->eq($newID) + ->exec(); + } + } } diff --git a/module/testcase/css/browse.css b/module/testcase/css/browse.css index d6add1f85f..9be97aa3b6 100644 --- a/module/testcase/css/browse.css +++ b/module/testcase/css/browse.css @@ -32,3 +32,17 @@ tbody > tr > td .icon-share {font-size: 9px;} .btn-toolbar>.btn, .btn-toolbar>.btn-group, .btn-toolbar>.dropdown {margin-right: 5px;} #mainMenu > .btn-toolbar > .checkbox-primary{float: left;} + +.table-nest-icon { cursor: pointer; } + +.tr-moving { opacity: 0.3; } + +.tr-acceptable { border: 2px solid rgba(255,0,0,0.5); } + +.none-select { -webkit-user-select: none; -moz-user-select: none; -moz-user-select: none; user-select: none; } + +.table-nest-toggle { vertical-align: middle; } + +.table-nest-title .icon-test { pointer-events: none; } + +.icon-test:after { border: 0px solid!important; } diff --git a/module/testcase/js/batchcreate.js b/module/testcase/js/batchcreate.js index 7480f4f066..d5c6f32f92 100644 --- a/module/testcase/js/batchcreate.js +++ b/module/testcase/js/batchcreate.js @@ -40,3 +40,92 @@ $(document).ready(function() return false; }); }); + +function findRealRowIndex(num) +{ + var $sel = $('#module' + num); + var $tr = $sel.closest("tr"); + return $tr.get(0).rowIndex-1; +} + +function findModuleID(moduleID, num) +{ + if(moduleID != "ditto") + return moduleID; + + var rIndex = findRealRowIndex(num); + + var trList = $("#tableBody").find("tbody").find("tr"); + for(var i=rIndex-1; i>=0; i--) + { + var currentID = $(trList[i]).find("td:eq(2)").find("select").val(); + if(currentID != "ditto") + { + moduleID = currentID; + break; + } + } + + return moduleID; +} + +function canSceneDitto(num) +{ + var rIndex = findRealRowIndex(num); + if(rIndex == 0) return false; + + var trList = $("#tableBody").find("tbody").find("tr"); + + var rowModule = $(trList[rIndex]).find("td:eq(2)").find("select").val(); + var preModule = $(trList[rIndex-1]).find("td:eq(2)").find("select").val(); + + return rowModule == "ditto" || rowModule == preModule; +} + +function onModuleChanged(productID, moduleID, num) +{ + loadStories(productID, moduleID, num); + loadScenes(productID, moduleID, num); +} + +function loadScenes(productID, moduleID, num) +{ + moduleID = findModuleID(moduleID, num); + + var branchIDName = (config.currentMethod == 'batchcreate' || config.currentMethod == 'showimport') ? '#branch' : '#branches'; + var branchID = $(branchIDName + num).val(); + var sceneLink = createLink('testcase', 'ajaxGetScenesForBC', 'productID=' + productID + '&branch=' + branchID + '&moduleID=' + moduleID + '&stype=2&storyID=0&onlyOption=false&status=noclosed&limit=50&type=full&hasParent=1&number=' + num); + $.get(sceneLink, function(scenes) + { + if(!scenes) scenes = ''; + if(config.currentMethod == 'batchcreate') + { + for(var i = num; i <= rowIndex ; i ++) + { + //if(i != num && $('#scene' + i).val() != 'ditto') break; + + var nowScenes = scenes.replaceAll('scene' + num, 'scene' + i); + $('#scene' + i).replaceWith(nowScenes); + if(canSceneDitto(i) == false) $('#scene' + i).find("option:last").remove(); + $('#scene' + i + "_chosen").remove(); + $('#scene' + i).next('.picker').remove(); + $('#scene' + i).attr('name', 'scene[' + i + ']'); + $('#scene' + i).picker(); + + if (i != num) + { + var myPicker = $('#scene' + i).data('zui.picker'); + myPicker && myPicker.setValue("ditto"); + } + } + } + else + { + $('#scene' + num).replaceWith(scenes); + $('#scene' + num + "_chosen").remove(); + $('#scene' + num).next('.picker').remove(); + $('#scene' + num).attr('name', 'scene[' + num + ']'); + $('#scene' + num).picker(); + } + }); +} diff --git a/module/testcase/js/batchedit.js b/module/testcase/js/batchedit.js index cb9a264b43..7a37699ddf 100644 --- a/module/testcase/js/batchedit.js +++ b/module/testcase/js/batchedit.js @@ -159,3 +159,62 @@ $(document).on('change', 'select', function() $(this).trigger("change"); } }); + +function loadStories2(productID, moduleID, num) +{ + var branchIDName = (config.currentMethod == 'batchcreate' || config.currentMethod == 'showimport') ? '#branch' : '#branches'; + var branchID = $(branchIDName + num).val(); + var storyLink = createLink('story', 'ajaxGetProductStories', 'productID=' + productID + '&branch=' + branchID + '&moduleID=' + moduleID + '&storyID=0&onlyOption=false&status=noclosed&limit=50&type=full&hasParent=1&executionID=0&number=' + num); + $.get(storyLink, function(stories) + { + if(!stories) stories = ''; + if(config.currentMethod == 'batchcreate') + { + for(var i = num; i < 10 ; i ++) + { + if(i != num && $('#module' + i).val() != 'ditto') break; + var nowStories = stories.replaceAll('story' + num, 'story' + i); + $('#story' + i).replaceWith(nowStories); + $('#story' + i + "_chosen").remove(); + $('#story' + i).next('.picker').remove(); + $('#story' + i).attr('name', 'story[' + i + ']'); + $('#story' + i).chosen(); + } + } + else + { + $('#story' + num).replaceWith(stories); + $('#story' + num + "_chosen").remove(); + $('#story' + num).next('.picker').remove(); + $('#story' + num).attr('name', 'story[' + num + ']'); + $('#story' + num).chosen(); + } + }); + + branch = 0; + link = createLink('testcase', 'ajaxGetModuleScenes', 'productID=' + productID + '&branch=' + branch + '&moduleID=' + moduleID + '&stype=2&storyID=0&onlyOption=false&status=noclosed&limit=50&type=full&hasParent=1&number=' + num); + $.get(link, function(scenes){ + if(!scenes) scenes = ''; + if(config.currentMethod == 'batchcreate') + { + for(var i = num; i < 10 ; i ++) + { + if(i != num && $('#module' + i).val() != 'ditto') break; + var nowScenes = scenes.replaceAll('scene' + num, 'scene' + i); + $('#scene' + i).replaceWith(nowScenes); + $('#scene' + i + "_chosen").remove(); + $('#scene' + i).next('.picker').remove(); + $('#scene' + i).attr('name', 'scene[' + i + ']'); + $('#scene' + i).chosen(); + } + } + else + { + $('#scene' + num).replaceWith(scenes); + $('#scene' + num + "_chosen").remove(); + $('#scene' + num).next('.picker').remove(); + $('#scene' + num).attr('name', 'scene[' + num + ']'); + $('#scene' + num).chosen(); + } + }); +} diff --git a/module/testcase/js/browse.js b/module/testcase/js/browse.js index 4ca57abf90..88d96cf9ca 100644 --- a/module/testcase/js/browse.js +++ b/module/testcase/js/browse.js @@ -54,3 +54,582 @@ function triggerHidden() if(runCase == true) window.location.reload(); }); } + +//sort js +var DtSort = {}; + +DtSort.defaultConfig = { + idAttrName: 'data-id', + parentAttrName: 'data-parent', + dataNestedAttrName: 'data-nested', + nestPathAttrName: 'data-nest-path', + dataTypeAttrName: 'data-itype', + moveBuffer: 2, + canAccept: function(source, target,sameLevel, sourceMgr, targetMgr){return target.dataNested == "true";}, + canMove: function(source, sourceMgr){return source.dataNested != "true";}, + finish: function(source, target, sameLevel , sourceMgr, targetMgr){}, + movingClass: 'tr-moving', + acceptableClass: 'tr-acceptable', + indicatorYOffset: 0.5 +}; + +DtSort.Table = function(options) +{ + this.options = $.extend({},DtSort.defaultConfig, options); + + this.indicator = undefined; + + this.sourceRow = undefined; + this.targetRow = undefined; + + this.mousePress = false; + + if(this.options.container == undefined) + return; + + this.$container = $(this.options.container); + + this.overRow = undefined; + this.downPosition = undefined; + this.rowList = []; + + this.init(); + + + //解决表格展开折叠按钮单击导致checkbox自动选中功能,临时先放在这里 + this.$container.find("tr").on("click",'.table-nest-toggle', function(e) + { + e.stopPropagation(); + var table = $('#caseForm').data('zui.table'); + var $row = $(e.currentTarget).closest("tr"); + var dataId = $row.attr("data-id"); + table.toggleNestedRows(dataId,undefined,true); + }) +} + +DtSort.Table.prototype.init = function() +{ + var that = this; + + $(document).on("mousedown", this.options.container, function(e){ + that.mousePress = true; + that.mouseDown(DtSort.tools.mousePos(e)); + }); + + $(document).on("mousemove", this.options.container, function(e){ + that.mouseMove(DtSort.tools.mousePos(e)); + }); + + $(document).on("mouseup", function(e){ + that.mouseUp(DtSort.tools.mousePos(e)); + that.mousePress = false; + }); + + $(window).resize(function(){ + that.measure(); + }) + + that.measure(); +} + +DtSort.Table.prototype.measure = function() +{ + this.rowList = []; + + var $rows = this.$container.find("tr"); + for(var i=0;i<$rows.length;i++) + { + $row = $($rows[i]); + + var rowIndex = i; + var id = $row.attr(this.options.idAttrName); + var parent = $row.attr(this.options.parentAttrName); + var dataNested = $row.attr(this.options.dataNestedAttrName); + var nestPath = $row.attr(this.options.nestPathAttrName); + var dataType = $row.attr(this.options.dataTypeAttrName); + + var pos = DtSort.tools.elementPos($rows[i]); + var size = DtSort.tools.elementSize($rows[i]); + + if($row.hasClass("table-nest-hide") && i>0) + { + size = {w:this.rowList[0].boundary.w, h:this.rowList[0].boundary.h}; + } + + if(dataNested == "true" && nestPath == undefined) + nestPath = "," + id + ","; + + this.rowList.push({ + id: id, + index: i, + parent: parent, + dataNested: dataNested, + nestPath: nestPath, + dateType: dataType, + boundary: {x:pos.x, y:pos.y, w:size.w, h:size.h}, + $dom: $row + }); + } +} + +DtSort.Table.prototype.pick = function(pos) +{ + if(this.rowList.length == 0) return undefined; + + var hitIndex = -1; + + var x = this.rowList[0].boundary.x; + var y = this.rowList[0].boundary.y; + + for(var i=0; i"); + $root.height(row.boundary.h); + $root.width(row.boundary.w); + $root.append($table); + $root.css("opacity",0.75); + $root.css("z-index",99999); + + $cloneRow.css({"background":"#FFFFFF"}); + $root.addClass("none-select"); + $table.css("cursor","move"); + + //var yOffset = this.options.indicatorYOffset * row.boundary.h; + //$root.css({position: "absolute", top: rowPos.y + yOffset + "px", left: rowPos.x + "px"}); + + this.indicator = {$dom:$root}; + + $("body").append($root); +} + +DtSort.Table.prototype.removeSourceIndicator = function() +{ + this.indicator && this.indicator.$dom.remove(); + this.indicator = undefined; +} + +DtSort.RowBase = function(table){ this.table = table;} + +DtSort.RowBase.prototype.setCanMoveIndicator = function(){ this.table.$container.css("cursor","move");} + +DtSort.RowBase.prototype.removeCanMoveIndicator = function(){ this.table.$container.css("cursor","pointer");} + +DtSort.NestRow = function(table, rows) +{ + DtSort.RowBase.call(this,table); + this.rows = rows; +}; + +DtSort.NestRow.prototype = Object.create(DtSort.RowBase.prototype); + +DtSort.NestRow.prototype.getIndex = function(){ return this.rows[0].index;} + +DtSort.NestRow.prototype.getRow = function(){ return this.rows[0];} + +DtSort.NestRow.prototype.include = function(row) +{ + for(var one of this.rows) + { + if(one.id == row.id) return true; + } + + return false; +} + +DtSort.NestRow.prototype.setMovingCss = function() +{ + for(var one of this.rows) + { + one.$dom.addClass(this.table.options.movingClass); + } +} + +DtSort.NestRow.prototype.removeMovingCss = function() +{ + for(var one of this.rows) + { + one.$dom.removeClass(this.table.options.movingClass); + } +} + +DtSort.NestRow.prototype.setAcceptCss = function(){ this.rows[0].$dom.addClass(this.table.options.acceptableClass);} + +DtSort.NestRow.prototype.removeAcceptCss = function(){ this.rows[0].$dom.removeClass(this.table.options.acceptableClass);} + +DtSort.NestRow.prototype.toString = function(){ return langRowIndex + ' ' + this.getIndex() + ' [' + langNestTotal + ': '+ this.rows.length +']'} + +DtSort.NormalRow = function(table, row) +{ + DtSort.RowBase.call(this,table); + this.row = row; +}; + +DtSort.NormalRow.prototype = Object.create(DtSort.RowBase.prototype); + +DtSort.NormalRow.prototype.getIndex = function() +{ + return this.row.index; +} + +DtSort.NormalRow.prototype.getRow = function(){ return this.row;} +DtSort.NormalRow.prototype.include = function(row){ return this.row.id == row.id;} +DtSort.NormalRow.prototype.setMovingCss = function(){ this.row.$dom.addClass(this.table.options.movingClass);} +DtSort.NormalRow.prototype.removeMovingCss = function(){ this.row.$dom.removeClass(this.table.options.movingClass);} +DtSort.NormalRow.prototype.setAcceptCss = function(){ this.row.$dom.addClass(this.table.options.acceptableClass);} +DtSort.NormalRow.prototype.removeAcceptCss = function(){ this.row.$dom.removeClass(this.table.options.acceptableClass);} +DtSort.NormalRow.prototype.toString = function(){ return langRowIndex + ' ' + this.getIndex() + ' [' + langNormal + ']';} + +DtSort.tools = { + elementSize: function(el) + { + let w = el.clientWidth || el.offsetWidth; + let h = el.clientHeight || el.offsetHeight; + + return { w: w, h: h } + }, + elementPos: function(el) + { + if (el.parentNode === null || el.style.display == 'none') + { + return false; + } + var parent = null; + var pos = []; + var box; + if (el.getBoundingClientRect) + { + // IE + box = el.getBoundingClientRect(); + var scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop); + var scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft); + return { + x: box.left + scrollLeft, + y: box.top + scrollTop + }; + } + else if (document.getBoxObjectFor) + { + box = document.getBoxObjectFor(el); + var borderLeft = (el.style.borderLeftWidth) ? parseInt(el.style.borderLeftWidth) : 0; + var borderTop = (el.style.borderTopWidth) ? parseInt(el.style.borderTopWidth) : 0; + pos = [box.x - borderLeft, box.y - borderTop]; + } + else + { + // safari & opera + pos = [el.offsetLeft, el.offsetTop]; + parent = el.offsetParent; + if (parent != el) + { + while (parent) + { + pos[0] += parent.offsetLeft; + pos[1] += parent.offsetTop; + parent = parent.offsetParent; + } + } + if (ua.indexOf('opera') != -1 || (ua.indexOf('safari') != -1 && el.style.position == 'absolute')) + { + pos[0] -= document.body.offsetLeft; + pos[1] -= document.body.offsetTop; + } + } + if (el.parentNode) + { + parent = el.parentNode; + } else { + parent = null; + } + while (parent && parent.tagName != 'BODY' && parent.tagName != 'HTML') + { + // account for any scrolled ancestors + pos[0] -= parent.scrollLeft; + pos[1] -= parent.scrollTop; + if (parent.parentNode) + { + parent = parent.parentNode; + } + else + { + parent = null; + } + } + return { + x: pos[0], + y: pos[1] + }; + }, + mousePos: function(event) + { + var e = event || window.event; + var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollY = document.documentElement.scrollTop || document.body.scrollTop; + var x = e.pageX || e.clientX + scrollX; + var y = e.pageY || e.clientY + scrollY; + + return { x: x, y: y }; + }, + distance(p1, p2) + { + var dx = p1.x - p2.x; + var dy = p1.y - p2.y; + + return Math.pow((dx * dx + dy * dy), 0.5); + } +}; + +DtSort.sort = function(options) +{ + var dtSort = new DtSort.Table(options); + return dtSort; +}; + + +//tree js +$(function() +{ + $('#caseTableList').on('click', '.c-id a,.c-name a,.c-actions a', function(e) + { + e.stopPropagation(); + }); + + $('#caseTableList').on('click', '.row-case', function(e) + { + var $row = $(this); + $row.toggleClass('checked'); + + updateChildrenCheckboxes($row); + updatePrarentCheckbox($row); + }); + + function updateChildrenCheckboxes($row) + { + var rowID = $row.data('id'); + var isChecked = $row.hasClass('checked'); + + if ($row.hasClass('has-nest-child')) + { + $('#caseTableList tr[data-nest-parent="'+rowID+'"]').each(function(){ + $(this).toggleClass('checked', isChecked); + $(this).find('input:checkbox').prop('checked', isChecked); + + updateChildrenCheckboxes($(this)); + }); + } + } + + /* Update parent checkbox */ + function updatePrarentCheckbox($row) + { + var rowID = $row.data('id'); + var parentID = $row.attr('data-nest-parent'); + var $parent = $('#caseTableList>tr[data-id="' + parentID + '"]'); + if(parentID && parentID !== '0') + { + var isAllChecked = true; + $('#caseTableList tr[data-nest-parent="'+parentID+'"]').each(function(){ + if (!$(this).hasClass('checked')) + { + isAllChecked = false; + } + }); + $parent.toggleClass('checked', isAllChecked); + $parent.find('input:checkbox').prop('checked', isAllChecked); + updatePrarentCheckbox($parent); + } + } + + //only scene + $('input[name^="onlyScene"]').click(function(){ + var onlyScene = $(this).is(':checked') ? 1 : 0; + $.cookie('onlyScene', onlyScene, {expires:config.cookieLife, path:config.webRoot}); + window.location.reload(); + }); + + $("input[name^=caseIDList]").change(function(){ + var trList = $("#caseTableList").find("tr"); + var selectedCaseNum = 0; + for(var i=0; i 0) + { + group.show(); + } + else + { + group.hide(); + } + }); +}); diff --git a/module/testcase/js/create.js b/module/testcase/js/create.js index 4042988341..481f5fb464 100644 --- a/module/testcase/js/create.js +++ b/module/testcase/js/create.js @@ -175,3 +175,76 @@ $(function() return false; }); }); + + +function loadAllNew(productID) +{ + loadProductBranchesNew(productID); +} + +function loadProductBranchesNew(productID) +{ + $('#branch').remove(); + + var param = page == 'create' ? 'active' : 'all'; + var oldBranch = page == 'edit' ? caseBranch : 0; + var param = "productID=" + productID + "&oldBranch=" + oldBranch + "¶m=" + param; + if(typeof(tab) != 'undefined' && (tab == 'execution' || tab == 'project')) param += "&projectID=" + objectID; + $.get(createLink('branch', 'ajaxGetBranches', param), function(data) + { + if(data) + { + $('#product').closest('.input-group').append(data); + $('#branch').css('width', config.currentMethod == 'create' ? '120px' : '95px'); + } + + loadProductModulesNew(productID); + setStories(); + }) +} + +function loadProductModulesNew(productID, branch) +{ + if(typeof(branch) == 'undefined') branch = $('#branch').val(); + if(!branch) branch = 0; + var currentModuleID = config.currentMethod == 'edit' ? $('#module').val() : 0; + link = createLink('testcase', 'ajaxGetOptionMenu', 'productID=' + productID + '&viewtype=case&branch=' + branch + '&rootModuleID=0&returnType=html&fieldID=&needManage=true&extra=¤tModuleID=' + currentModuleID); + $('#moduleIdBox').load(link, function() + { + var $inputGroup = $(this); + $inputGroup.find('select').chosen() + if(typeof(caseModule) == 'string') $('#moduleIdBox').prepend("" + caseModule + ""); + $inputGroup.fixInputGroup(); + }); + setScenes(); + setStories(); +} + +function setScenes() +{ + moduleID = $('#module').val(); + productID = $('#product').val(); + branch = $('#branch').val(); + if(typeof(branch) == 'undefined') branch = 0; + link = createLink('testcase', 'ajaxGetModuleScenes', 'productID=' + productID + '&branch=' + branch + '&moduleID=' + moduleID + '&stype=2&storyID=0&onlyOption=false&status=noclosed&limit=50&type=full&hasParent=1'); + + $('#sceneIdBox').load(link, function() + { + $(this).find('select').chosen() + }); +} + +function loadBranchNew() +{ + var branch = $('#branch').val(); + if(typeof(branch) == 'undefined') branch = 0; + loadProductModulesNew($('#product').val(), branch); + + setStories(); +} + +function loadModuleRelatedNew() +{ + setScenes(); + setStories(); +} diff --git a/module/testcase/js/createscene.js b/module/testcase/js/createscene.js new file mode 100644 index 0000000000..4fc67f0365 --- /dev/null +++ b/module/testcase/js/createscene.js @@ -0,0 +1,67 @@ +function loadAllNew(productID) +{ + loadProductBranchesNew(productID); +} + +function loadProductBranchesNew(productID) +{ + $('#branch').remove(); + + console.log(1111, caseBranch) + var param = page == 'create' ? 'active' : 'all'; + var oldBranch = page == 'edit' ? caseBranch : 0; + var param = "productID=" + productID + "&oldBranch=" + oldBranch + "¶m=" + param; + if(typeof(tab) != 'undefined' && (tab == 'execution' || tab == 'project')) param += "&projectID=" + objectID; + $.get(createLink('branch', 'ajaxGetBranches', param), function(data) + { + if(data) + { + $('#product').closest('.input-group').append(data); + $('#branch').css('width', config.currentMethod == 'create' ? '120px' : '95px'); + } + + loadProductModulesNew(productID); + }) +} + +function loadProductModulesNew(productID, branch) +{ + if(typeof(branch) == 'undefined') branch = $('#branch').val(); + if(!branch) branch = 0; + var currentModuleID = config.currentMethod == 'edit' ? $('#module').val() : 0; + link = createLink('testcase', 'ajaxGetOptionMenu', 'productID=' + productID + '&viewtype=case&branch=' + branch + '&rootModuleID=0&returnType=html&fieldID=&needManage=true&extra=¤tModuleID=' + currentModuleID); + $('#moduleIdBox').load(link, function() + { + var $inputGroup = $(this); + $inputGroup.find('select').chosen() + if(typeof(caseModule) == 'string') $('#moduleIdBox').prepend("" + caseModule + ""); + $inputGroup.fixInputGroup(); + }); + setScenes(); +} + +function setScenes() +{ + moduleID = $('#module').val(); + productID = $('#product').val(); + branch = $('#branch').val(); + if(typeof(branch) == 'undefined') branch = 0; + link = createLink('testcase', 'ajaxGetModuleScenes', 'productID=' + productID + '&branch=' + branch + '&moduleID=' + moduleID + '&stype=2&storyID=0&onlyOption=false&status=noclosed&limit=50&type=full&hasParent=1'); + + $('#sceneIdBox').load(link, function() + { + $(this).find('select').chosen() + }); +} + +function loadBranchNew() +{ + var branch = $('#branch').val(); + if(typeof(branch) == 'undefined') branch = 0; + loadProductModulesNew($('#product').val(), branch); +} + +function loadModuleRelatedNew() +{ + setScenes(); +} diff --git a/module/testcase/js/edit.js b/module/testcase/js/edit.js index 0812712236..8cce84d8fb 100644 --- a/module/testcase/js/edit.js +++ b/module/testcase/js/edit.js @@ -90,3 +90,76 @@ function loadBranch(oldBranch) setStories(); } } + +function loadAllNew(productID) +{ + loadProductBranchesNew(productID); +} + +function loadProductBranchesNew(productID) +{ + $('#branch').remove(); + + var param = page == 'create' ? 'active' : 'all'; + var oldBranch = page == 'edit' ? caseBranch : 0; + var param = "productID=" + productID + "&oldBranch=" + oldBranch + "¶m=" + param; + if(typeof(tab) != 'undefined' && (tab == 'execution' || tab == 'project')) param += "&projectID=" + objectID; + $.get(createLink('branch', 'ajaxGetBranches', param), function(data) + { + if(data) + { + $('#product').closest('.input-group').append(data); + $('#branch').css('width', config.currentMethod == 'create' ? '120px' : '95px'); + } + + loadProductModulesNew(productID); + setStories(); + }) +} + +function loadProductModulesNew(productID, branch) +{ + if(typeof(branch) == 'undefined') branch = $('#branch').val(); + if(!branch) branch = 0; + var currentModuleID = config.currentMethod == 'edit' ? $('#module').val() : 0; + link = createLink('testcase', 'ajaxGetOptionMenu', 'productID=' + productID + '&viewtype=case&branch=' + branch + '&rootModuleID=0&returnType=html&fieldID=&needManage=true&extra=¤tModuleID=' + currentModuleID); + $('#moduleIdBox').load(link, function() + { + var $inputGroup = $(this); + $inputGroup.find('select').chosen() + if(typeof(caseModule) == 'string') $('#moduleIdBox').prepend("" + caseModule + ""); + $inputGroup.fixInputGroup(); + }); + setScenes(); + setStories(); +} + +function setScenes() +{ + moduleID = $('#module').val(); + productID = $('#product').val(); + branch = $('#branch').val(); + if(typeof(branch) == 'undefined') branch = 0; + link = createLink('testcase', 'ajaxGetModuleScenes', 'productID=' + productID + '&branch=' + branch + '&moduleID=' + moduleID + '&stype=2&storyID=0&onlyOption=false&status=noclosed&limit=50&type=full&hasParent=1'); + + $('#sceneIdBox').load(link, function() + { + $(this).find('select').chosen() + }); +} + + +function loadBranchNew() +{ + var branch = $('#branch').val(); + if(typeof(branch) == 'undefined') branch = 0; + loadProductModulesNew($('#product').val(), branch); + + setStories(); +} + +function loadModuleRelatedNew() +{ + setScenes(); + setStories(); +} diff --git a/module/testcase/js/editscene.js b/module/testcase/js/editscene.js new file mode 100644 index 0000000000..9bfb5f9cf0 --- /dev/null +++ b/module/testcase/js/editscene.js @@ -0,0 +1,66 @@ +function loadAllNew(productID) +{ + loadProductBranchesNew(productID); +} + +function loadProductBranchesNew(productID) +{ + $('#branch').remove(); + + var param = page == 'create' ? 'active' : 'all'; + var oldBranch = page == 'edit' ? caseBranch : 0; + var param = "productID=" + productID + "&oldBranch=" + oldBranch + "¶m=" + param; + if(typeof(tab) != 'undefined' && (tab == 'execution' || tab == 'project')) param += "&projectID=" + objectID; + $.get(createLink('branch', 'ajaxGetBranches', param), function(data) + { + if(data) + { + $('#product').closest('.input-group').append(data); + $('#branch').css('width', config.currentMethod == 'create' ? '120px' : '95px'); + } + + loadProductModulesNew(productID); + }) +} + +function loadProductModulesNew(productID, branch) +{ + if(typeof(branch) == 'undefined') branch = $('#branch').val(); + if(!branch) branch = 0; + var currentModuleID = config.currentMethod == 'edit' ? $('#module').val() : 0; + link = createLink('testcase', 'ajaxGetOptionMenu', 'productID=' + productID + '&viewtype=case&branch=' + branch + '&rootModuleID=0&returnType=html&fieldID=&needManage=true&extra=¤tModuleID=' + currentModuleID); + $('#moduleIdBox').load(link, function() + { + var $inputGroup = $(this); + $inputGroup.find('select').chosen() + if(typeof(caseModule) == 'string') $('#moduleIdBox').prepend("" + caseModule + ""); + $inputGroup.fixInputGroup(); + }); + setScenes(); +} + +function setScenes() +{ + moduleID = $('#module').val(); + productID = $('#product').val(); + branch = $('#branch').val(); + if(typeof(branch) == 'undefined') branch = 0; + link = createLink('testcase', 'ajaxGetModuleScenes', 'productID=' + productID + '&branch=' + branch + '&moduleID=' + moduleID + '&stype=1&storyID=0&onlyOption=false&status=noclosed&limit=50&type=full&hasParent=1&number=¤tScene=' + sceneId); + + $('#sceneIdBox').load(link, function() + { + $(this).find('select').chosen() + }); +} + +function loadBranchNew() +{ + var branch = $('#branch').val(); + if(typeof(branch) == 'undefined') branch = 0; + loadProductModulesNew($('#product').val(), branch); +} + +function loadModuleRelatedNew() +{ + setScenes(); +} diff --git a/module/testcase/lang/de.php b/module/testcase/lang/de.php index a301beaba7..0ed86183c8 100644 --- a/module/testcase/lang/de.php +++ b/module/testcase/lang/de.php @@ -266,3 +266,82 @@ $lang->testcase->featureBar['browse']['group'] = 'Group View'; $lang->testcase->featureBar['browse']['zerocase'] = 'Zero Case Story'; $lang->testcase->featureBar['browse']['suite'] = 'Suite'; $lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; + + +$lang->testcase->importXmind = "Import XMIND"; +$lang->testcase->exportXmind = "Export XMIND"; +$lang->testcase->getXmindImport = "Get Mindmap"; +$lang->testcase->showXMindImport = "Display Mindmap"; + + +$lang->testcase->xmindImport = "Imort XMIND"; +$lang->testcase->xmindExport = "Export XMIND"; +$lang->testcase->xmindImportEdit = "XMIND Edit"; +$lang->testcase->errorFileNotEmpty = 'The uploaded file cannot be empty'; +$lang->testcase->errorXmindUpload = 'Upload failed'; +$lang->testcase->errorFileFormat = 'File format error'; +$lang->testcase->moduleSelector = 'Module Selection'; +$lang->testcase->errorImportBadProduct = 'Product does not exist, import error'; +$lang->testcase->errorSceneNotExist = 'Scene [%d] not exists'; + +$lang->testcase->save = 'Save'; +$lang->testcase->close = 'Close'; + +$lang->testcase->xmindImportSetting = 'Import Characteristic Character Settings'; +$lang->testcase->xmindExportSetting = 'Export Characteristic Character Settings'; + +$lang->testcase->settingModule = 'Module'; +$lang->testcase->settingScene = 'Scene'; +$lang->testcase->settingCase = 'Testcase'; +$lang->testcase->settingPri = 'Priority'; +$lang->testcase->settingGroup = 'Step Group'; + +$lang->testcase->caseNotExist = 'The test case in the imported file was not recognized and the import failed'; +$lang->testcase->saveFail = 'Save failed'; +$lang->testcase->set2Scene = 'Set as Scene'; +$lang->testcase->set2Testcase = 'Set as Testcase'; +$lang->testcase->clearSetting = 'Clear Settings'; +$lang->testcase->setModule = 'Set scene module'; +$lang->testcase->pickModule = 'Please select a module'; +$lang->testcase->clearBefore = 'Clear previous scenes'; +$lang->testcase->clearAfter = 'Clear the following scenes'; +$lang->testcase->clearCurrent = 'Clear the current scene'; +$lang->testcase->removeGroup = 'Remove Group'; +$lang->testcase->set2Group = 'Set as Group'; + +$lang->testcase->exportTemplet = 'Export Template'; + +$lang->testcase->createScene = "Add Scene"; +$lang->testcase->changeScene = "Drag to change the scene which it belongs"; +$lang->testcase->batchChangeScene = "Batch change scene"; +$lang->testcase->updateOrder = "Drag Sort"; +$lang->testcase->errDiffProduct = "Different product"; + +$lang->testcase->newScene = "Add Scene"; +$lang->testcase->sceneTitle = 'Scene Title'; +$lang->testcase->parentScene = "Parent Scene"; +$lang->testcase->scene = "Scene"; +$lang->testcase->summary = 'Total %d Top Scene,%d Independent test case.'; +$lang->testcase->summaryScene = 'Total %d Top Scene.'; +$lang->testcase->deleteScene = 'Delete Scene'; +$lang->testcase->editScene = 'Edit Scene'; +$lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; +$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; +$lang->testcase->sceneb = "Scene"; +$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; +$lang->testcase->onlyScene = 'Only Scene'; +$lang->testcase->iScene = 'Scene'; +$lang->testcase->generalTitle = 'Title'; +$lang->testcase->noScene = 'No Scene'; +$lang->testcase->rowIndex = 'Row Index'; +$lang->testcase->nestTotal = 'nest total'; +$lang->testcase->normal = 'normal'; + +/* Translation for drag modal message box. */ +$lang->testcase->dragModalTitle = 'Drag and drop operation selection'; +$lang->testcase->dragModalMessage = '

There are two possible situations for the current operation:

1) Adjust the sequence.
2) Change its scenario, meanwhile its module will be changed accordingly.

Please select the operation you want to perform.

'; +$lang->testcase->dragModalChangeScene = 'Change its scene'; +$lang->testcase->dragModalChangeOrder = 'Reorder'; + +$lang->scene = new stdclass(); +$lang->scene->title = 'Scene Title'; \ No newline at end of file diff --git a/module/testcase/lang/en.php b/module/testcase/lang/en.php index fbb69e4df6..fc9c6b5f81 100644 --- a/module/testcase/lang/en.php +++ b/module/testcase/lang/en.php @@ -266,3 +266,82 @@ $lang->testcase->featureBar['browse']['group'] = 'Group View'; $lang->testcase->featureBar['browse']['zerocase'] = 'Zero Case Story'; $lang->testcase->featureBar['browse']['suite'] = 'Suite'; $lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; + + +$lang->testcase->importXmind = "Import XMIND"; +$lang->testcase->exportXmind = "Export XMIND"; +$lang->testcase->getXmindImport = "Get Mindmap"; +$lang->testcase->showXMindImport = "Display Mindmap"; + + +$lang->testcase->xmindImport = "Imort XMIND"; +$lang->testcase->xmindExport = "Export XMIND"; +$lang->testcase->xmindImportEdit = "XMIND Edit"; +$lang->testcase->errorFileNotEmpty = 'The uploaded file cannot be empty'; +$lang->testcase->errorXmindUpload = 'Upload failed'; +$lang->testcase->errorFileFormat = 'File format error'; +$lang->testcase->moduleSelector = 'Module Selection'; +$lang->testcase->errorImportBadProduct = 'Product does not exist, import error'; +$lang->testcase->errorSceneNotExist = 'Scene [%d] not exists'; + +$lang->testcase->save = 'Save'; +$lang->testcase->close = 'Close'; + +$lang->testcase->xmindImportSetting = 'Import Characteristic Character Settings'; +$lang->testcase->xmindExportSetting = 'Export Characteristic Character Settings'; + +$lang->testcase->settingModule = 'Module'; +$lang->testcase->settingScene = 'Scene'; +$lang->testcase->settingCase = 'Testcase'; +$lang->testcase->settingPri = 'Priority'; +$lang->testcase->settingGroup = 'Step Group'; + +$lang->testcase->caseNotExist = 'The test case in the imported file was not recognized and the import failed'; +$lang->testcase->saveFail = 'Save failed'; +$lang->testcase->set2Scene = 'Set as Scene'; +$lang->testcase->set2Testcase = 'Set as Testcase'; +$lang->testcase->clearSetting = 'Clear Settings'; +$lang->testcase->setModule = 'Set scene module'; +$lang->testcase->pickModule = 'Please select a module'; +$lang->testcase->clearBefore = 'Clear previous scenes'; +$lang->testcase->clearAfter = 'Clear the following scenes'; +$lang->testcase->clearCurrent = 'Clear the current scene'; +$lang->testcase->removeGroup = 'Remove Group'; +$lang->testcase->set2Group = 'Set as Group'; + +$lang->testcase->exportTemplet = 'Export Template'; + +$lang->testcase->createScene = "Add Scene"; +$lang->testcase->changeScene = "Drag to change the scene which it belongs"; +$lang->testcase->batchChangeScene = "Batch change scene"; +$lang->testcase->updateOrder = "Drag Sort"; +$lang->testcase->errDiffProduct = "Different product"; + +$lang->testcase->newScene = "Add Scene"; +$lang->testcase->sceneTitle = 'Scene Title'; +$lang->testcase->parentScene = "Parent Scene"; +$lang->testcase->scene = "Scene"; +$lang->testcase->summary = 'Total %d Top Scene,%d Independent test case.'; +$lang->testcase->summaryScene = 'Total %d Top Scene.'; +$lang->testcase->deleteScene = 'Delete Scene'; +$lang->testcase->editScene = 'Edit Scene'; +$lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; +$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; +$lang->testcase->sceneb = "Scene"; +$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; +$lang->testcase->onlyScene = 'Only Scene'; +$lang->testcase->iScene = 'Scene'; +$lang->testcase->generalTitle = 'Title'; +$lang->testcase->noScene = 'No Scene'; +$lang->testcase->rowIndex = 'Row Index'; +$lang->testcase->nestTotal = 'nest total'; +$lang->testcase->normal = 'normal'; + +/* Translation for drag modal message box. */ +$lang->testcase->dragModalTitle = 'Drag and drop operation selection'; +$lang->testcase->dragModalMessage = '

There are two possible situations for the current operation:

1) Adjust the sequence.
2) Change its scenario, meanwhile its module will be changed accordingly.

Please select the operation you want to perform.

'; +$lang->testcase->dragModalChangeScene = 'Change its scene'; +$lang->testcase->dragModalChangeOrder = 'Reorder'; + +$lang->scene = new stdclass(); +$lang->scene->title = 'Scene Title'; \ No newline at end of file diff --git a/module/testcase/lang/fr.php b/module/testcase/lang/fr.php index d4af3d8735..7d88be8890 100644 --- a/module/testcase/lang/fr.php +++ b/module/testcase/lang/fr.php @@ -266,3 +266,82 @@ $lang->testcase->featureBar['browse']['group'] = 'Group View'; $lang->testcase->featureBar['browse']['zerocase'] = 'Zero Case Story'; $lang->testcase->featureBar['browse']['suite'] = 'Suite'; $lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; + + +$lang->testcase->importXmind = "Import XMIND"; +$lang->testcase->exportXmind = "Export XMIND"; +$lang->testcase->getXmindImport = "Get Mindmap"; +$lang->testcase->showXMindImport = "Display Mindmap"; + + +$lang->testcase->xmindImport = "Imort XMIND"; +$lang->testcase->xmindExport = "Export XMIND"; +$lang->testcase->xmindImportEdit = "XMIND Edit"; +$lang->testcase->errorFileNotEmpty = 'The uploaded file cannot be empty'; +$lang->testcase->errorXmindUpload = 'Upload failed'; +$lang->testcase->errorFileFormat = 'File format error'; +$lang->testcase->moduleSelector = 'Module Selection'; +$lang->testcase->errorImportBadProduct = 'Product does not exist, import error'; +$lang->testcase->errorSceneNotExist = 'Scene [%d] not exists'; + +$lang->testcase->save = 'Save'; +$lang->testcase->close = 'Close'; + +$lang->testcase->xmindImportSetting = 'Import Characteristic Character Settings'; +$lang->testcase->xmindExportSetting = 'Export Characteristic Character Settings'; + +$lang->testcase->settingModule = 'Module'; +$lang->testcase->settingScene = 'Scene'; +$lang->testcase->settingCase = 'Testcase'; +$lang->testcase->settingPri = 'Priority'; +$lang->testcase->settingGroup = 'Step Group'; + +$lang->testcase->caseNotExist = 'The test case in the imported file was not recognized and the import failed'; +$lang->testcase->saveFail = 'Save failed'; +$lang->testcase->set2Scene = 'Set as Scene'; +$lang->testcase->set2Testcase = 'Set as Testcase'; +$lang->testcase->clearSetting = 'Clear Settings'; +$lang->testcase->setModule = 'Set scene module'; +$lang->testcase->pickModule = 'Please select a module'; +$lang->testcase->clearBefore = 'Clear previous scenes'; +$lang->testcase->clearAfter = 'Clear the following scenes'; +$lang->testcase->clearCurrent = 'Clear the current scene'; +$lang->testcase->removeGroup = 'Remove Group'; +$lang->testcase->set2Group = 'Set as Group'; + +$lang->testcase->exportTemplet = 'Export Template'; + +$lang->testcase->createScene = "Add Scene"; +$lang->testcase->changeScene = "Drag to change the scene which it belongs"; +$lang->testcase->batchChangeScene = "Batch change scene"; +$lang->testcase->updateOrder = "Drag Sort"; +$lang->testcase->errDiffProduct = "Different product"; + +$lang->testcase->newScene = "Add Scene"; +$lang->testcase->sceneTitle = 'Scene Title'; +$lang->testcase->parentScene = "Parent Scene"; +$lang->testcase->scene = "Scene"; +$lang->testcase->summary = 'Total %d Top Scene,%d Independent test case.'; +$lang->testcase->summaryScene = 'Total %d Top Scene.'; +$lang->testcase->deleteScene = 'Delete Scene'; +$lang->testcase->editScene = 'Edit Scene'; +$lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; +$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; +$lang->testcase->sceneb = "Scene"; +$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; +$lang->testcase->onlyScene = 'Only Scene'; +$lang->testcase->iScene = 'Scene'; +$lang->testcase->generalTitle = 'Title'; +$lang->testcase->noScene = 'No Scene'; +$lang->testcase->rowIndex = 'Row Index'; +$lang->testcase->nestTotal = 'nest total'; +$lang->testcase->normal = 'normal'; + +/* Translation for drag modal message box. */ +$lang->testcase->dragModalTitle = 'Drag and drop operation selection'; +$lang->testcase->dragModalMessage = '

There are two possible situations for the current operation:

1) Adjust the sequence.
2) Change its scenario, meanwhile its module will be changed accordingly.

Please select the operation you want to perform.

'; +$lang->testcase->dragModalChangeScene = 'Change its scene'; +$lang->testcase->dragModalChangeOrder = 'Reorder'; + +$lang->scene = new stdclass(); +$lang->scene->title = 'Scene Title'; \ No newline at end of file diff --git a/module/testcase/lang/vi.php b/module/testcase/lang/vi.php index 85cd7786e6..6161998ca6 100644 --- a/module/testcase/lang/vi.php +++ b/module/testcase/lang/vi.php @@ -236,3 +236,82 @@ $lang->testcase->featureBar['browse']['zerocase'] = 'Zero Case Story'; $lang->testcase->featureBar['browse']['browseunits'] = 'Unit Test'; $lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; $lang->testcase->featureBar['groupcase'] = $lang->testcase->featureBar['browse']; + + +$lang->testcase->importXmind = "Import XMIND"; +$lang->testcase->exportXmind = "Export XMIND"; +$lang->testcase->getXmindImport = "Get Mindmap"; +$lang->testcase->showXMindImport = "Display Mindmap"; + + +$lang->testcase->xmindImport = "Imort XMIND"; +$lang->testcase->xmindExport = "Export XMIND"; +$lang->testcase->xmindImportEdit = "XMIND Edit"; +$lang->testcase->errorFileNotEmpty = 'The uploaded file cannot be empty'; +$lang->testcase->errorXmindUpload = 'Upload failed'; +$lang->testcase->errorFileFormat = 'File format error'; +$lang->testcase->moduleSelector = 'Module Selection'; +$lang->testcase->errorImportBadProduct = 'Product does not exist, import error'; +$lang->testcase->errorSceneNotExist = 'Scene [%d] not exists'; + +$lang->testcase->save = 'Save'; +$lang->testcase->close = 'Close'; + +$lang->testcase->xmindImportSetting = 'Import Characteristic Character Settings'; +$lang->testcase->xmindExportSetting = 'Export Characteristic Character Settings'; + +$lang->testcase->settingModule = 'Module'; +$lang->testcase->settingScene = 'Scene'; +$lang->testcase->settingCase = 'Testcase'; +$lang->testcase->settingPri = 'Priority'; +$lang->testcase->settingGroup = 'Step Group'; + +$lang->testcase->caseNotExist = 'The test case in the imported file was not recognized and the import failed'; +$lang->testcase->saveFail = 'Save failed'; +$lang->testcase->set2Scene = 'Set as Scene'; +$lang->testcase->set2Testcase = 'Set as Testcase'; +$lang->testcase->clearSetting = 'Clear Settings'; +$lang->testcase->setModule = 'Set scene module'; +$lang->testcase->pickModule = 'Please select a module'; +$lang->testcase->clearBefore = 'Clear previous scenes'; +$lang->testcase->clearAfter = 'Clear the following scenes'; +$lang->testcase->clearCurrent = 'Clear the current scene'; +$lang->testcase->removeGroup = 'Remove Group'; +$lang->testcase->set2Group = 'Set as Group'; + +$lang->testcase->exportTemplet = 'Export Template'; + +$lang->testcase->createScene = "Add Scene"; +$lang->testcase->changeScene = "Drag to change the scene which it belongs"; +$lang->testcase->batchChangeScene = "Batch change scene"; +$lang->testcase->updateOrder = "Drag Sort"; +$lang->testcase->errDiffProduct = "Different product"; + +$lang->testcase->newScene = "Add Scene"; +$lang->testcase->sceneTitle = 'Scene Title'; +$lang->testcase->parentScene = "Parent Scene"; +$lang->testcase->scene = "Scene"; +$lang->testcase->summary = 'Total %d Top Scene,%d Independent test case.'; +$lang->testcase->summaryScene = 'Total %d Top Scene.'; +$lang->testcase->deleteScene = 'Delete Scene'; +$lang->testcase->editScene = 'Edit Scene'; +$lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; +$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; +$lang->testcase->sceneb = "Scene"; +$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; +$lang->testcase->onlyScene = 'Only Scene'; +$lang->testcase->iScene = 'Scene'; +$lang->testcase->generalTitle = 'Title'; +$lang->testcase->noScene = 'No Scene'; +$lang->testcase->rowIndex = 'Row Index'; +$lang->testcase->nestTotal = 'nest total'; +$lang->testcase->normal = 'normal'; + +/* Translation for drag modal message box. */ +$lang->testcase->dragModalTitle = 'Drag and drop operation selection'; +$lang->testcase->dragModalMessage = '

There are two possible situations for the current operation:

1) Adjust the sequence.
2) Change its scenario, meanwhile its module will be changed accordingly.

Please select the operation you want to perform.

'; +$lang->testcase->dragModalChangeScene = 'Change its scene'; +$lang->testcase->dragModalChangeOrder = 'Reorder'; + +$lang->scene = new stdclass(); +$lang->scene->title = 'Scene Title'; \ No newline at end of file diff --git a/module/testcase/lang/zh-cn.php b/module/testcase/lang/zh-cn.php index 7d4f6386d9..72972304cd 100644 --- a/module/testcase/lang/zh-cn.php +++ b/module/testcase/lang/zh-cn.php @@ -266,3 +266,83 @@ $lang->testcase->featureBar['browse']['group'] = '分组查看'; $lang->testcase->featureBar['browse']['zerocase'] = "零用例{$lang->SRCommon}"; $lang->testcase->featureBar['browse']['suite'] = '套件'; $lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; + + +$lang->testcase->importXmind = "导入XMIND"; +$lang->testcase->exportXmind = "导出XMIND"; +$lang->testcase->getXmindImport = "获取导图"; +$lang->testcase->showXMindImport = "显示导图"; + + +$lang->testcase->xmindImport = "导入XMIND"; +$lang->testcase->xmindExport = "导出XMIND"; +$lang->testcase->xmindImportEdit = "XMIND 编辑"; +$lang->testcase->errorFileNotEmpty = '上传文件不能为空'; +$lang->testcase->errorXmindUpload = '上传失败'; +$lang->testcase->errorFileFormat = '文件格式错误'; +$lang->testcase->moduleSelector = '模块选择'; +$lang->testcase->errorImportBadProduct = '产品不存在,导入错误'; +$lang->testcase->errorSceneNotExist = '场景[%d]不存在'; + +$lang->testcase->save = '保存'; +$lang->testcase->close = '关闭'; + +$lang->testcase->xmindImportSetting = '导入特征字符设置'; +$lang->testcase->xmindExportSetting = '导出特征字符设置'; + +$lang->testcase->settingModule = '模       块'; +$lang->testcase->settingScene = '场    景'; +$lang->testcase->settingCase = '测试用例'; +$lang->testcase->settingPri = '优先级 '; +$lang->testcase->settingGroup = '步骤分组'; + +$lang->testcase->caseNotExist = '未识别导入数据中的用例,导入失败'; +$lang->testcase->saveFail = '保存失败'; +$lang->testcase->set2Scene = '设为场景'; +$lang->testcase->set2Testcase = '设为测试用例'; +$lang->testcase->clearSetting = '清除设置'; +$lang->testcase->setModule = '设置场景模块'; +$lang->testcase->pickModule = '请选择模块'; +$lang->testcase->clearBefore = '清除前面场景'; +$lang->testcase->clearAfter = '清除后面场景'; +$lang->testcase->clearCurrent = '清除当前场景'; +$lang->testcase->removeGroup = '移除分组'; +$lang->testcase->set2Group = '设为分组'; + +$lang->testcase->exportTemplet = '导出模板'; + +$lang->testcase->createScene = "建场景"; +$lang->testcase->changeScene = "拖动改变所属场景"; +$lang->testcase->batchChangeScene = "批量改变所属场景"; +$lang->testcase->updateOrder = "拖动排序"; +$lang->testcase->differentProduct = "所属产品不同"; + +$lang->testcase->newScene = "建场景"; +$lang->testcase->sceneTitle = '场景标题'; +$lang->testcase->parentScene = "父场景"; +$lang->testcase->scene = "所属场景"; +$lang->testcase->summary = '本页共 %d 个顶级场景,%d 个独立用例。'; +$lang->testcase->summaryScene = '本页共 %d 个顶级场景。'; +$lang->testcase->deleteScene = '删除场景'; +$lang->testcase->editScene = '编辑场景'; +$lang->testcase->hasChildren = '该场景有子场景或测试用例存在,要全部删除吗?'; +$lang->testcase->confirmDeleteScene = '您确定要删除场景:\“%s\”吗?'; +$lang->testcase->sceneb = "场景"; +$lang->testcase->onlyScene = '仅场景'; +$lang->testcase->iScene = '所属场景'; +$lang->testcase->generalTitle = '标题'; +$lang->testcase->noScene = '暂时没有场景'; +$lang->testcase->rowIndex = '行索引'; +$lang->testcase->nestTotal = '嵌套总数'; +$lang->testcase->normal = '正常'; + +/* Translation for drag modal message box. */ +$lang->testcase->dragModalTitle = '拖拽操作选择'; +$lang->testcase->dragModalMessage = '

当前操作有两种可能的情况:

1) 调整排序
2) 更改所属场景,所属模块同时变更为目标场景的模块

请选择您要执行的操作

'; +$lang->testcase->dragModalChangeScene = '更改所属场景'; +$lang->testcase->dragModalChangeOrder = '调整排序'; + +$lang->testcase->confirmBatchDeleteSceneCase = '您确认要批量删除这些场景或测试用例吗?'; + +$lang->scene = new stdclass(); +$lang->scene->title = '场景标题'; \ No newline at end of file diff --git a/module/testcase/lang/zh-tw.php b/module/testcase/lang/zh-tw.php index 52b32cb3c4..973a0a7d35 100644 --- a/module/testcase/lang/zh-tw.php +++ b/module/testcase/lang/zh-tw.php @@ -246,3 +246,83 @@ $lang->testcase->featureBar['browse']['suite'] = '套件'; $lang->testcase->featureBar['browse']['zerocase'] = "零用例{$lang->SRCommon}"; $lang->testcase->featureBar['browse']['browseunits'] = '單元測試'; $lang->testcase->featureBar['groupcase'] = $lang->testcase->featureBar['browse']; + + +$lang->testcase->importXmind = "导入XMIND"; +$lang->testcase->exportXmind = "导出XMIND"; +$lang->testcase->getXmindImport = "获取导图"; +$lang->testcase->showXMindImport = "显示导图"; + + +$lang->testcase->xmindImport = "导入XMIND"; +$lang->testcase->xmindExport = "导出XMIND"; +$lang->testcase->xmindImportEdit = "XMIND 编辑"; +$lang->testcase->errorFileNotEmpty = '上传文件不能为空'; +$lang->testcase->errorXmindUpload = '上传失败'; +$lang->testcase->errorFileFormat = '文件格式错误'; +$lang->testcase->moduleSelector = '模块选择'; +$lang->testcase->errorImportBadProduct = '产品不存在,导入错误'; +$lang->testcase->errorSceneNotExist = '场景[%d]不存在'; + +$lang->testcase->save = '保存'; +$lang->testcase->close = '关闭'; + +$lang->testcase->xmindImportSetting = '导入特征字符设置'; +$lang->testcase->xmindExportSetting = '导出特征字符设置'; + +$lang->testcase->settingModule = '模       块'; +$lang->testcase->settingScene = '场    景'; +$lang->testcase->settingCase = '测试用例'; +$lang->testcase->settingPri = '优先级 '; +$lang->testcase->settingGroup = '步骤分组'; + +$lang->testcase->caseNotExist = '未识别导入数据中的用例,导入失败'; +$lang->testcase->saveFail = '保存失败'; +$lang->testcase->set2Scene = '设为场景'; +$lang->testcase->set2Testcase = '设为测试用例'; +$lang->testcase->clearSetting = '清除设置'; +$lang->testcase->setModule = '设置场景模块'; +$lang->testcase->pickModule = '请选择模块'; +$lang->testcase->clearBefore = '清除前面场景'; +$lang->testcase->clearAfter = '清除后面场景'; +$lang->testcase->clearCurrent = '清除当前场景'; +$lang->testcase->removeGroup = '移除分组'; +$lang->testcase->set2Group = '设为分组'; + +$lang->testcase->exportTemplet = '导出模板'; + +$lang->testcase->createScene = "建场景"; +$lang->testcase->changeScene = "拖动改变所属场景"; +$lang->testcase->batchChangeScene = "批量改变所属场景"; +$lang->testcase->updateOrder = "拖动排序"; +$lang->testcase->differentProduct = "所属产品不同"; + +$lang->testcase->newScene = "建场景"; +$lang->testcase->sceneTitle = '场景标题'; +$lang->testcase->parentScene = "父场景"; +$lang->testcase->scene = "所属场景"; +$lang->testcase->summary = '本页共 %d 个顶级场景,%d 个独立用例。'; +$lang->testcase->summaryScene = '本页共 %d 个顶级场景。'; +$lang->testcase->deleteScene = '删除场景'; +$lang->testcase->editScene = '编辑场景'; +$lang->testcase->hasChildren = '该场景有子场景或测试用例存在,要全部删除吗?'; +$lang->testcase->confirmDeleteScene = '您确定要删除场景:\“%s\”吗?'; +$lang->testcase->sceneb = "场景"; +$lang->testcase->onlyScene = '仅场景'; +$lang->testcase->iScene = '所属场景'; +$lang->testcase->generalTitle = '标题'; +$lang->testcase->noScene = '暂时没有场景'; +$lang->testcase->rowIndex = '行索引'; +$lang->testcase->nestTotal = '嵌套总数'; +$lang->testcase->normal = '正常'; + +/* Translation for drag modal message box. */ +$lang->testcase->dragModalTitle = '拖拽操作选择'; +$lang->testcase->dragModalMessage = '

当前操作有两种可能的情况:

1) 调整排序
2) 更改所属场景,所属模块同时变更为目标场景的模块

请选择您要执行的操作

'; +$lang->testcase->dragModalChangeScene = '更改所属场景'; +$lang->testcase->dragModalChangeOrder = '调整排序'; + +$lang->testcase->confirmBatchDeleteSceneCase = '您确认要批量删除这些场景或测试用例吗?'; + +$lang->scene = new stdclass(); +$lang->scene->title = '场景标题'; \ No newline at end of file diff --git a/module/testcase/model.php b/module/testcase/model.php index 1b3046340e..1376b87a85 100644 --- a/module/testcase/model.php +++ b/module/testcase/model.php @@ -143,6 +143,7 @@ class testcaseModel extends model } $module = 0; + $scene = 0; $story = 0; $type = ''; $pri = 3; @@ -155,10 +156,12 @@ class testcaseModel extends model } $module = $cases->module[$i] == 'ditto' ? $module : $cases->module[$i]; + $scene = $cases->scene[$i] == 'ditto' ? $scene : $cases->scene[$i]; $story = $cases->story[$i] == 'ditto' ? $story : $cases->story[$i]; $type = $cases->type[$i] == 'ditto' ? $type : $cases->type[$i]; $pri = $cases->pri[$i] == 'ditto' ? $pri : $cases->pri[$i]; $cases->module[$i] = (int)$module; + $cases->scene[$i] = (int)$scene; $cases->story[$i] = !empty($storyID) ? $storyID : (int)$story; $cases->type[$i] = $type; $cases->pri[$i] = $pri; @@ -178,6 +181,7 @@ class testcaseModel extends model if($this->app->tab == 'project') $data[$i]->project = $this->session->project; $data[$i]->branch = isset($cases->branch[$i]) ? $cases->branch[$i] : '0'; $data[$i]->module = $cases->module[$i]; + $data[$i]->scene = $cases->scene[$i]; $data[$i]->type = $cases->type[$i]; $data[$i]->pri = $cases->pri[$i]; $data[$i]->stage = empty($cases->stage[$i]) ? '' : implode(',', $cases->stage[$i]); @@ -648,11 +652,12 @@ class testcaseModel extends model if($this->app->tab == 'project') $caseQuery = str_replace('`product`', 't2.`product`', $caseQuery); /* Search criteria under compatible project. */ - $sql = $this->dao->select('*')->from(TABLE_CASE)->alias('t1'); + $sql = $this->dao->select('*')->from(VIEW_SCENECASE)->alias('t1'); if($this->app->tab == 'project') $sql->leftJoin(TABLE_PROJECTCASE)->alias('t2')->on('t1.id=t2.case'); $cases = $sql ->where($caseQuery) - ->beginIF($this->app->tab == 'project')->andWhere('t2.project')->eq($this->session->project)->fi() + ->andWhere('t1.isCase')->eq(1) + ->beginIF($this->app->tab == 'project' and $this->config->systemMode == 'new')->andWhere('t2.project')->eq($this->session->project)->fi() ->beginIF($this->app->tab == 'project' and !empty($productID) and $queryProductID != 'all')->andWhere('t2.product')->eq($productID)->fi() ->beginIF($this->app->tab != 'project' and !empty($productID) and $queryProductID != 'all')->andWhere('t1.product')->eq($productID)->fi() ->beginIF($auto != 'unit')->andWhere('t1.auto')->ne('unit')->fi() @@ -1133,6 +1138,7 @@ class testcaseModel extends model { if($data->pris[$caseID] == 'ditto') $data->pris[$caseID] = isset($prev['pri']) ? $prev['pri'] : 3; if($data->modules[$caseID] == 'ditto') $data->modules[$caseID] = isset($prev['module']) ? $prev['module'] : 0; + if($data->scene[$caseID] == 'ditto') $data->scene[$caseID] = isset($prev['scene']) ? $prev['scene'] : 0; if($data->types[$caseID] == 'ditto') $data->types[$caseID] = isset($prev['type']) ? $prev['type'] : ''; if($data->story[$caseID] == '') $data->story[$caseID] = 0; if($data->story[$caseID] == 'ditto') $data->story[$caseID] = isset($prev['story']) ? $prev['story'] : 0; @@ -1142,6 +1148,7 @@ class testcaseModel extends model $prev['type'] = $data->types[$caseID]; $prev['story'] = $data->story[$caseID]; $prev['module'] = $data->modules[$caseID]; + $prev['scene'] = $data->scene[$caseID]; if(isset($data->branches)) $prev['branch'] = $data->branches[$caseID]; } @@ -1155,6 +1162,7 @@ class testcaseModel extends model $case->lastEditedDate = $now; $case->pri = $data->pris[$caseID]; $case->module = $data->modules[$caseID]; + $case->scene = $data->scene[$caseID]; $case->status = $data->statuses[$caseID]; $case->story = $data->story[$caseID]; $case->color = $data->color[$caseID]; @@ -1280,19 +1288,108 @@ class testcaseModel extends model { $now = helper::now(); $allChanges = array(); - $oldCases = $this->getByList($caseIDList); + $oldCases = $this->dao->select('*')->from(VIEW_SCENECASE) + ->where('deleted')->eq(0) + ->beginIF($caseIDList)->andWhere('id')->in($caseIDList)->fi() + ->fetchAll('id'); + + /* Split selected nodes into 2 arrays. */ + /* Top level nodes. */ + $finalOldCases1 = array(); + /* Non-top level nodes. */ + $finalOldCases2 = array(); + if(!empty($oldCases)) + { + foreach($oldCases as $k => $v) + { + $flag = 0; + foreach($oldCases as $k2 => $v2) + { + $resFlag = strpos($v->path,$v2->path); + + /* v2 is ancestor. */ + if (($resFlag || $resFlag === 0) && $v->grade > $v2->grade ) $flag = 1; + } + if($flag == 0) + { + /* None selected node is its ancestor, assign to this array. */ + $finalOldCases1[$k] = $v; + } + else + { + /* As one selected node is its ancestor, assign to this array. */ + $finalOldCases2[$k] = $v; + } + } + } + + /* Process all top level nodes of selected nodes. */ foreach($caseIDList as $caseID) { - $oldCase = $oldCases[$caseID]; - if($moduleID == $oldCase->module) continue; + if(!isset($finalOldCases1[$caseID])) continue; + + $oldCase = $finalOldCases1[$caseID]; $case = new stdclass(); $case->lastEditedBy = $this->app->user->account; $case->lastEditedDate = $now; $case->module = $moduleID; - $this->dao->update(TABLE_CASE)->data($case)->autoCheck()->where('id')->eq((int)$caseID)->exec(); - if(!dao::isError()) $allChanges[$caseID] = common::createChanges($oldCase, $case); + if($oldCase->isCase == 2) + { + $case->parent = 0; + $case->path = ",$caseID,"; + $case->grade = 1; + + $this->dao->update(TABLE_SCENE)->data($case) + ->autoCheck() + ->where('id')->eq((int)$caseID - CHANGEVALUE) + ->exec(); + if(!dao::isError()) $allChanges[0][$caseID] = common::createChanges($oldCase, $case); + } + else + { + $case->scene = 0; + $this->dao->update(TABLE_CASE)->data($case) + ->autoCheck() + ->where('id')->eq((int)$caseID) + ->exec(); + if(!dao::isError()) $allChanges[1][$caseID] = common::createChanges($oldCase, $case); + } + } + + /* Process non-top level nodes of selected nodes. */ + foreach($caseIDList as $caseID) + { + if(!isset($finalOldCases2[$caseID])) continue; + + $oldCase = $finalOldCases2[$caseID]; + + $case = new stdclass(); + $case->lastEditedBy = $this->app->user->account; + $case->lastEditedDate = $now; + $case->module = $moduleID; + + if($oldCase->isCase == 2) + { + $resultScene = $this->dao->findById((int)$oldCase->parent - CHANGEVALUE)->from(TABLE_SCENE)->fetch(); + $case->path = $resultScene->path . "$caseID,"; + $case->grade = $resultScene->grade + 1; + + $this->dao->update(TABLE_SCENE)->data($case) + ->autoCheck() + ->where('id')->eq((int)$caseID - CHANGEVALUE) + ->exec(); + if(!dao::isError()) $allChanges[0][$caseID] = common::createChanges($oldCase, $case); + } + else + { + $this->dao->update(TABLE_CASE)->data($case) + ->autoCheck() + ->where('id')->eq((int)$caseID) + ->exec(); + if(!dao::isError()) $allChanges[1][$caseID] = common::createChanges($oldCase, $case); + } } return $allChanges; @@ -1983,7 +2080,7 @@ class testcaseModel extends model * @access public * @return void */ - public function buildSearchForm($productID, $products, $queryID, $actionURL, $projectID = 0) + public function buildSearchForm($productID, $products, $queryID, $actionURL, $projectID = 0, $moduleID = 0, $branch = 0) { $productList = array(); if($this->app->tab == 'project' and empty($productID)) @@ -1997,13 +2094,15 @@ class testcaseModel extends model } $this->config->testcase->search['params']['product']['values'] = array('') + $productList; - $module = $this->loadModel('tree')->getOptionMenu($productID, 'case', 0); + $module = $this->loadModel('tree')->getOptionMenu($productID, 'case', 0, $branch); + $scene = $this->getSceneMenu($productID, $moduleID, $viewType = 'case', $startSceneID = 0, $branch, 0, true); if(!$productID) { $module = array(); foreach($products as $id => $name) $module += $this->loadModel('tree')->getOptionMenu($id, 'case', 0); } $this->config->testcase->search['params']['module']['values'] = $module; + $this->config->testcase->search['params']['scene']['values'] = $scene; $this->config->testcase->search['params']['lib']['values'] = $this->loadModel('caselib')->getLibraries(); @@ -2022,6 +2121,7 @@ class testcaseModel extends model if(!$this->config->testcase->needReview) unset($this->config->testcase->search['params']['status']['values']['wait']); $this->config->testcase->search['actionURL'] = $actionURL; $this->config->testcase->search['queryID'] = $queryID; + $this->config->testcase->search['module'] = $this->app->rawModule; $this->loadModel('search')->setSearchParams($this->config->testcase->search); } @@ -2036,7 +2136,7 @@ class testcaseModel extends model * @access public * @return void */ - public function printCell($col, $case, $users, $branches, $modulePairs = array(), $browseType = '', $mode = 'datatable') + public function printCell($col, $case, $users, $branches, $modulePairs = array(), $browseType = '', $mode = 'datatable', $isCase = 1) { /* Check the product is closed. */ $canBeChanged = common::canBeChanged('case', $case); @@ -2074,45 +2174,84 @@ class testcaseModel extends model if($id == 'lastRunResult') $class .= " {$case->lastRunResult}"; if(strpos(',stage,precondition,keywords,story,', ",{$id},") !== false) $class .= ' text-ellipsis'; - echo ""; + if($id == 'title') + { + if($isCase == 2) + { + echo ""; + } + else + { + echo ""; + } + } + else + { + echo ""; + } if($this->config->edition != 'open') $this->loadModel('flow')->printFlowCell('testcase', $case, $id); switch($id) { case 'id': + $showid = ""; + if($isCase == 2) + { + $showid = substr($case->id,1); + $showid = preg_replace('/^0+/', '', $showid); + } + else + { + $showid = $case->id; + } if($canBatchAction) { $disabled = $canBeChanged ? '' : 'disabled'; - echo html::checkbox('caseIDList', array($case->id => ''), '', $disabled) . html::a(helper::createLink('testcase', 'view', "caseID=$case->id"), sprintf('%03d', $case->id), '', "data-app='{$this->app->tab}'"); + if($isCase == 1){ + echo html::checkbox('caseIDList', array($case->id => ''), '', $disabled) . html::a(helper::createLink('testcase', 'view', "caseID=$case->id"), sprintf('%03d', $showid), '', "data-app='{$this->app->tab}'"); + } + else + { + echo html::checkbox('caseIDList', array($case->id => ''), '', $disabled) . sprintf('%03d', $showid); + } } else { - printf('%03d', $case->id); + printf('%03d', $showid); } break; case 'pri': - echo ""; - echo zget($this->lang->testcase->priList, $case->pri, $case->pri); - echo ""; + if($isCase != 2) + { + echo ""; + echo zget($this->lang->testcase->priList, $case->pri, $case->pri); + echo ""; + } break; case 'title': - if($this->app->tab == 'project') - { - $showBranch = isset($this->config->project->testcase->showBranch) ? $this->config->project->testcase->showBranch : 1; + if($isCase == 1){ + if($this->app->tab == 'project') + { + $showBranch = isset($this->config->project->testcase->showBranch) ? $this->config->project->testcase->showBranch : 1; + } + else + { + $showBranch = isset($this->config->testcase->browse->showBranch) ? $this->config->testcase->browse->showBranch : 1; + } + + $autoIcon = $case->auto == 'auto' ? " " : ''; + if(isset($branches[$case->branch]) and $showBranch) echo "{$branches[$case->branch]} "; + if($modulePairs and $case->module and isset($modulePairs[$case->module])) echo "{$modulePairs[$case->module]} "; + echo $canView ? html::a($caseLink, $case->title, null, "style='color: $case->color' data-app='{$this->app->tab}'") + : "$case->title"; + + $fromLink = ($fromCaseID and $canView) ? helper::createLink('testcase', 'view', "caseID=$fromCaseID") : '#'; + $title = $fromCaseID ? "[#$fromCaseID]$autoIcon" : $autoIcon; + if($case->auto == 'auto') echo html::a($fromLink, $title, '', "data-app='{$this->app->tab}'"); } else { - $showBranch = isset($this->config->testcase->browse->showBranch) ? $this->config->testcase->browse->showBranch : 1; + echo $case->title; } - - $autoIcon = $case->auto == 'auto' ? " " : ''; - if(isset($branches[$case->branch]) and $showBranch) echo "{$branches[$case->branch]} "; - if($modulePairs and $case->module and isset($modulePairs[$case->module])) echo "{$modulePairs[$case->module]} "; - echo $canView ? html::a($caseLink, $case->title, null, "style='color: $case->color' data-app='{$this->app->tab}'") - : "$case->title"; - - $fromLink = ($fromCaseID and $canView) ? helper::createLink('testcase', 'view', "caseID=$fromCaseID") : '#'; - $title = $fromCaseID ? "[#$fromCaseID]$autoIcon" : $autoIcon; - if($case->auto == 'auto') echo html::a($fromLink, $title, '', "data-app='{$this->app->tab}'"); break; case 'branch': echo $branches[$case->branch]; @@ -2152,7 +2291,7 @@ class testcaseModel extends model echo $case->keywords; break; case 'version': - echo $case->version; + if($isCase == 1) echo $case->version; break; case 'openedBy': echo zget($users, $case->openedBy); @@ -2179,23 +2318,32 @@ class testcaseModel extends model if(!helper::isZeroDate($case->lastRunDate)) echo substr($case->lastRunDate, 5, 11); break; case 'lastRunResult': - $class = 'result-' . $case->lastRunResult; - $lastRunResultText = $case->lastRunResult ? zget($this->lang->testcase->resultList, $case->lastRunResult, $case->lastRunResult) : $this->lang->testcase->unexecuted; - echo "" . $lastRunResultText . ""; + if ($isCase == 1) { + $class = 'result-' . $case->lastRunResult; + $lastRunResultText = $case->lastRunResult ? zget($this->lang->testcase->resultList, $case->lastRunResult, $case->lastRunResult) : $this->lang->testcase->unexecuted; + echo "" . $lastRunResultText . ""; + } break; case 'bugs': - echo (common::hasPriv('testcase', 'bugs') and $case->bugs) ? html::a(helper::createLink('testcase', 'bugs', "runID=0&caseID={$case->id}"), $case->bugs, '', "class='iframe'") : $case->bugs; + if ($isCase == 1) echo (common::hasPriv('testcase', 'bugs') and $case->bugs) ? html::a(helper::createLink('testcase', 'bugs', "runID=0&caseID={$case->id}"), $case->bugs, '', "class='iframe'") : $case->bugs; break; case 'results': - echo (common::hasPriv('testtask', 'results') and $case->results) ? html::a(helper::createLink('testtask', 'results', "runID=0&caseID={$case->id}"), $case->results, '', "class='iframe'") : $case->results; + if ($isCase == 1) echo (common::hasPriv('testtask', 'results') and $case->results) ? html::a(helper::createLink('testtask', 'results', "runID=0&caseID={$case->id}"), $case->results, '', "class='iframe'") : $case->results; break; case 'stepNumber': - echo $case->stepNumber; + if ($isCase == 1) echo $case->stepNumber; break; case 'actions': - $case->browseType = $browseType; - echo $this->buildOperateMenu($case, 'browse'); - break; + if ($isCase == 1) + { + $case->browseType = $browseType; + echo $this->buildOperateMenu($case, 'browse'); + break; + } + else + { + echo $this->buildOperateBrowseSceneMenu($case); + } } echo ''; } @@ -2721,4 +2869,967 @@ class testcaseModel extends model foreach($branches as $branchID => $branchName) $modules += $this->tree->getOptionMenu($productID, 'case', 0, $branchID); return $modules; } + + public function batchChangeScene($caseIDList, $sceneId) + { + $now = helper::now(); + $allChanges = array(); + + $ioldCases = $this->dao->select('*')->from(VIEW_SCENECASE) + ->where('deleted')->eq(0) + ->beginIF($caseIDList)->andWhere('id')->in($caseIDList)->fi() + ->fetchAll('id'); + + /* If the target node is root. */ + if(!$sceneId) + { + $oldCases = $ioldCases; + + /* Split selected nodes into 2 arrays. */ + $finalOldCases1 = array(); /* Parent Scenes. */ + $finalOldCases2 = array(); /* Cases and leaf Scenes. */ + if(!empty($oldCases)) + { + foreach($oldCases as $k => $v) + { + $flag = 0; + foreach($oldCases as $k2 => $v2) + { + $resFlag = strpos($v->path,$v2->path); + + /* $v2 is the ancestor of $v. */ + if(($resFlag || $resFlag === 0) && $v->grade > $v2->grade ) $flag = 1; + } + + if($flag == 0) + { + /* None selected node is its ancestor, then assign to this array. */ + $finalOldCases1[$k] = $v; + } + else + { + /* As one selected node is ancestor, then assign to this array. */ + $finalOldCases2[$k] = $v; + } + } + } + + /* Process all top level nodes (all parent scenes) of selected nodes. */ + foreach($caseIDList as $caseID) + { + if(!isset($finalOldCases1[$caseID])) continue; + + $oldCase = $finalOldCases1[$caseID]; + if($sceneId == $oldCase->parent) continue; + if($sceneId == $oldCase->id) continue; + + $case = new stdclass(); + $case->lastEditedBy = $this->app->user->account; + $case->lastEditedDate = $now; + + if($oldCase->isCase == 2) + { + $case->parent = 0; + $case->path = ",$caseID,"; + $case->grade = 1; + + $this->dao->update(TABLE_SCENE)->data($case) + ->autoCheck() + ->where('id')->eq((int)$caseID - CHANGEVALUE) + ->exec(); + if(!dao::isError()) $allChanges[0][$caseID] = common::createChanges($oldCase, $case); + } + else + { + $case->scene = 0; + + $this->dao->update(TABLE_CASE)->data($case) + ->autoCheck() + ->where('id')->eq((int)$caseID) + ->exec(); + if(!dao::isError()) $allChanges[1][$caseID] = common::createChanges($oldCase, $case); + } + } + + /* Process all non-top level nodes of selected nodes. */ + foreach($caseIDList as $caseID) + { + if (!isset($finalOldCases2[$caseID])) continue; + + $oldCase = $finalOldCases2[$caseID]; + if($sceneId == $oldCase->parent) continue; + if($sceneId == $oldCase->id) continue; + + $case = new stdclass(); + $case->lastEditedBy = $this->app->user->account; + $case->lastEditedDate = $now; + + if($oldCase->isCase == 2) + { + $case->parent = $sceneId; + $resultScene = $this->dao->findById((int)$oldCase->parent - CHANGEVALUE)->from(TABLE_SCENE)->fetch(); + $case->path = $resultScene->path . "$caseID,"; + $case->grade = $resultScene->grade + 1; + + $this->dao->update(TABLE_SCENE)->data($case) + ->autoCheck() + ->where('id')->eq((int)$caseID - CHANGEVALUE) + ->exec(); + if(!dao::isError()) $allChanges[0][$caseID] = common::createChanges($oldCase, $case); + } + else + { + $case->scene = $sceneId; + $this->dao->update(TABLE_CASE)->data($case) + ->autoCheck() + ->where('id')->eq((int)$caseID) + ->exec(); + if(!dao::isError()) $allChanges[1][$caseID] = common::createChanges($oldCase, $case); + } + } + } + else + { + $sceneRow = $this->dao->findById((int)$sceneId)->from(VIEW_SCENECASE)->fetch(); + + /* Remove ancestors of target node. Won't change ancestors' scene to be target scene. */ + $oldCases = array(); + if(!empty($ioldCases)) + { + foreach($ioldCases as $k => $v) + { + $resFlag = strpos($sceneRow->path,$v->path); + if(($resFlag || $resFlag === 0) && $sceneRow->grade > $v->grade ) + { + /* The ancestors of the target node. */ + } + else + { + $oldCases[$k] = $v; + } + } + } + + /* These selected nodes removed ancestors are split into 2 arrays. */ + $finalOldCases1 = array(); + $finalOldCases2 = array(); + if (!empty($oldCases)) + { + foreach($oldCases as $k => $v) + { + $flag = 0; + foreach($oldCases as $k2 => $v2) + { + $resFlag = strpos($v->path,$v2->path); + + /* v2 is ancestor. */ + if (($resFlag || $resFlag === 0) && $v->grade > $v2->grade ) $flag = 1; + } + + if($flag == 0) + { + /* None selected node is its ancestor, then assign it to this array. */ + $finalOldCases1[$k] = $v; + } + else + { + /* As one selected node is its ancestor, then assign it to this array. */ + $finalOldCases2[$k] = $v; + } + } + } + + /* Process all top level of selected nodes. */ + foreach($caseIDList as $caseID) + { + if(isset($finalOldCases1[$caseID])) + { + $oldCase = $finalOldCases1[$caseID]; + if($sceneId == $oldCase->parent) continue; + if($sceneId == $oldCase->id) continue; + + $resFlag = strpos($sceneRow->path,$oldCase->path); + /* Target node is child. */ + if(($resFlag || $resFlag === 0) && $sceneRow->grade > $oldCase->grade ) continue; + + $case = new stdclass(); + $case->lastEditedBy = $this->app->user->account; + $case->lastEditedDate = $now; + $case->product = $sceneRow->product; + $case->module = $sceneRow->module; + + if($oldCase->isCase == 2) + { + $case->parent = $sceneId; + $case->path = $sceneRow->path . "$caseID,"; + $case->grade = $sceneRow->grade + 1; + + $this->dao->update(TABLE_SCENE)->data($case)->autoCheck()->where('id')->eq((int)$caseID-CHANGEVALUE)->exec(); + if(!dao::isError()) $allChanges[0][$caseID] = common::createChanges($oldCase, $case); + + } + else + { + $case->scene = $sceneId; + + $this->dao->update(TABLE_CASE)->data($case)->autoCheck()->where('id')->eq((int)$caseID)->exec(); + if(!dao::isError()) $allChanges[1][$caseID] = common::createChanges($oldCase, $case); + } + } + } + + /* Process all non-top level nodes of selected nodes. */ + foreach($caseIDList as $caseID) + { + if(!isset($finalOldCases2[$caseID])) continue; + + $oldCase = $finalOldCases2[$caseID]; + if($sceneId == $oldCase->parent) continue; + if($sceneId == $oldCase->id) continue; + + $resFlag = strpos($sceneRow->path,$oldCase->path); + /* Target node is child. */ + if(($resFlag || $resFlag === 0) && $sceneRow->grade > $oldCase->grade) continue; + + $case = new stdclass(); + $case->lastEditedBy = $this->app->user->account; + $case->lastEditedDate = $now; + $case->product = $sceneRow->product; + $case->module = $sceneRow->module; + + if($oldCase->isCase == 2) + { + $resultScene = $this->dao->findById((int)$oldCase->parent - CHANGEVALUE)->from(TABLE_SCENE)->fetch(); + $case->path = $resultScene->path . "$caseID,"; + $case->grade = $resultScene->grade + 1; + + $this->dao->update(TABLE_SCENE)->data($case) + ->autoCheck() + ->where('id')->eq((int)$caseID - CHANGEVALUE) + ->exec(); + if(!dao::isError()) $allChanges[0][$caseID] = common::createChanges($oldCase, $case); + } + else + { + $this->dao->update(TABLE_CASE)->data($case) + ->autoCheck() + ->where('id')->eq((int)$caseID) + ->exec(); + if(!dao::isError()) $allChanges[1][$caseID] = common::createChanges($oldCase, $case); + } + } + } + + return $allChanges; + } + + public function buildMenuQuery($rootID, $moduleID, $type, $startScene = 0, $branch = 'all') + { + /* Set the start module. */ + $startScenePath = ''; + if($startScene > 0) + { + $startScene = $this->dao->findById((int)$startScene)->from(VIEW_SCENECASE)->fetch(); + if($startScene) $startScenePath = $startScene->path . '%'; + } + + return $this->dao->select('*')->from(VIEW_SCENECASE) + ->where('deleted')->eq(0) + ->beginIF($rootID)->andWhere('product')->eq((int)$rootID)->fi() + ->beginIF(intval($moduleID) > 0)->andWhere('module')->eq((int)$moduleID)->fi() + ->beginIF($startScenePath)->andWhere('path')->like($startScenePath)->fi() + ->beginIF($branch !== 'all' and $branch !== '' and $branch !== false)->andWhere('branch')->eq((int)$branch)->fi() + ->andWhere('isCase')->eq(2) + ->orderBy('grade desc, sort') + ->get(); + } + + public function buildOperateBrowseSceneMenu($scene) + { + $canBeChanged = common::canBeChanged('case', $scene); + if(!$canBeChanged) return ''; + + $params = "sceneID=$scene->id"; + + /* Generate params for editing scene. */ + $editParams = $params; + if($this->app->tab == 'project') $editParams .= "&projectID={$this->session->project}"; + if($this->app->tab == 'execution') $editParams .= "&executionID={$this->session->execution}"; + + $menu = $this->buildMenu('testcase', 'editScene', $editParams, $scene, 'browse', 'edit', '', '', '', '', $this->lang->testcase->editScene); + $menu .= $this->buildMenu('testcase', 'deleteScene', $params, $scene, 'browse', 'trash', 'hiddenwin', '', '', '', $this->lang->testcase->deleteScene); + + return $menu; + } + + public function buildSearchFormAddScene($productID, $products, $queryID, $actionURL, $projectID = 0,$moduleID = 0) + { + $product = ($this->app->tab == 'project' and empty($productID)) ? $products : array($productID => $products[$productID]) + array('all' => $this->lang->testcase->allProduct); + $this->config->testcase->search['params']['product']['values'] = $product; + + $module = $this->loadModel('tree')->getOptionMenu($productID, 'case', 0); + $scene = $this->getSceneMenu($productID, $moduleID, $viewType = 'case', $startSceneID = 0, 0); + + if(!$productID) + { + $module = array(); + foreach($products as $id => $product) $module += $this->loadModel('tree')->getOptionMenu($id, 'case', 0); + } + + $this->config->testcase->search['params']['module']['values'] = $module; + $this->config->testcase->search['params']['parent']['values'] = $scene; + $this->config->testcase->search['params']['lib']['values'] = $this->loadModel('caselib')->getLibraries(); + + if($this->session->currentProductType == 'normal') + { + unset($this->config->testcase->search['fields']['branch']); + unset($this->config->testcase->search['params']['branch']); + } + else + { + $productInfo = $this->loadModel('product')->getByID($productID); + + $this->config->testcase->search['fields']['branch'] = sprintf($this->lang->product->branch, $this->lang->product->branchName[$productInfo->type]); + $this->config->testcase->search['params']['branch']['values'] = array('' => '', '0' => $this->lang->branch->main) + $this->loadModel('branch')->getPairs($productID, '', $projectID) + array('all' => $this->lang->branch->all); + } + + if(!$this->config->testcase->needReview) unset($this->config->testcase->search['params']['status']['values']['wait']); + $this->config->testcase->search['actionURL'] = $actionURL; + $this->config->testcase->search['queryID'] = $queryID; + + $this->loadModel('search')->setSearchParams($this->config->testcase->search); + } + + public function buildTreeArray(& $treeMenu, $scenes, $scene, $sceneName = '/') + { + $parentScenes = explode(',', $scene->path); + foreach($parentScenes as $parentSceneID) + { + if(empty($parentSceneID)) continue; + if(empty($scenes[$parentSceneID])) continue; + + $sceneName .= $scenes[$parentSceneID]->title . '/'; + } + + $sceneName = rtrim($sceneName, '/'); + $sceneName .= "|$scene->id\n"; + + if(isset($treeMenu[$scene->id]) and !empty($treeMenu[$scene->id])) + { + if(isset($treeMenu[$scene->parent])) + { + $treeMenu[$scene->parent] .= $sceneName; + } + else + { + $treeMenu[$scene->parent] = $sceneName; + } + $treeMenu[$scene->parent] .= $treeMenu[$scene->id]; + } + else + { + if(isset($treeMenu[$scene->parent]) and !empty($treeMenu[$scene->parent])) + { + $treeMenu[$scene->parent] .= $sceneName; + } + else + { + $treeMenu[$scene->parent] = $sceneName; + } + } + } + + /** + * Create scene. + * + * @access public + * @return void + */ + public function createScene() + { + $now = helper::now(); + $scene = fixer::input('post') + ->setDefault('openedBy', $this->app->user->account) + ->setDefault('openedDate', $now) + ->setDefault('parent', $this->post->scene === false ? 0 : $this->post->scene) + ->cleanInt('product,module,branch') + ->remove('scene') + ->get(); + + $this->dao->insert(TABLE_SCENE)->data($scene) + ->autoCheck() + ->batchCheck($this->config->testcase->createscene->requiredFields, 'notempty') + ->check('title', 'unique') + ->checkFlow() + ->exec(); + + if(!$this->dao->isError()) + { + $sceneID = $this->dao->lastInsertID(); + $childPath = ""; + $grade = ""; + $viewSceneID = intval($sceneID) + CHANGEVALUE; + + if($scene->parent) + { + $resultScene = $this->dao->findById((int)$scene->parent - CHANGEVALUE)->from(TABLE_SCENE)->fetch(); + $childPath = $resultScene->path . $viewSceneID . ','; + $grade = $resultScene->grade + 1; + $this->dao->update(TABLE_SCENE) + ->set('path')->eq($childPath) + ->set('grade')->eq($grade) + ->set('product')->eq($resultScene->product) + ->set('module')->eq($resultScene->module) + ->set('sort')->eq(intval($sceneID) + CHANGEVALUE) + ->where('id')->eq($sceneID)->limit(1) + ->exec(); + } + else + { + $childPath = ",$viewSceneID,"; + $grade = 1; + $this->dao->update(TABLE_SCENE) + ->set('path')->eq($childPath) + ->set('grade')->eq($grade) + ->set('sort')->eq(intval($sceneID) + CHANGEVALUE) + ->where('id')->eq($sceneID)->limit(1) + ->exec(); + } + + return array('status' => 'created', 'id' => $sceneID); + } + } + + public function getAllChildId($sceneID) + { + if($sceneID == 0) return array(); + + $scene = $this->dao->findById((int)$sceneID)->from(VIEW_SCENECASE)->fetch(); + if(empty($scene)) return array(); + + return $this->dao->select('id')->from(VIEW_SCENECASE) + ->where('path')->like($scene->path . '%') + ->andWhere('deleted')->eq(0) + ->fetchPairs(); + } + + public function getBySearchHasScene($productID, $queryID, $orderBy, $pager = null, $branch = 0, $auto = 'no') + { + if($queryID) + { + $query = $this->loadModel('search')->getQuery($queryID); + if($query) + { + $this->session->set('testcaseQuery', $query->sql); + $this->session->set('testcaseForm', $query->form); + } + else + { + $this->session->set('testcaseQuery', ' 1 = 1'); + } + } + else + { + if($this->session->testcaseQuery == false) $this->session->set('testcaseQuery', ' 1 = 1'); + } + + $queryProductID = $productID; + $allProduct = "`product` = 'all'"; + $caseQuery = '(' . $this->session->testcaseQuery; + if(strpos($this->session->testcaseQuery, $allProduct) !== false) + { + $products = $this->app->user->view->products; + $caseQuery = str_replace($allProduct, '1', $caseQuery); + $caseQuery = $caseQuery . ' AND `product` ' . helper::dbIN($products); + $queryProductID = 'all'; + } + + $allBranch = "`branch` = 'all'"; + if($branch !== 'all' and strpos($caseQuery, '`branch` =') === false) $caseQuery .= " AND `branch` in('$branch')"; + if(strpos($caseQuery, $allBranch) !== false) $caseQuery = str_replace($allBranch, '1', $caseQuery); + $caseQuery .= ')'; + $caseQuery = str_replace('`version`', 't1.`version`', $caseQuery); + + if($this->app->tab == 'project') $caseQuery = str_replace('`product`', 't2.`product`', $caseQuery); + + /* Search criteria under compatible project. */ + $sql = $this->dao->select('*')->from(VIEW_SCENECASE)->alias('t1'); + if($this->app->tab == 'project') $sql->leftJoin(TABLE_PROJECTCASE)->alias('t2')->on('t1.id=t2.case'); + $cases = $sql + ->where($caseQuery) + ->andWhere('isCase')->eq(1) + ->beginIF($this->app->tab == 'project')->andWhere('t2.project')->eq($this->session->project)->fi() + ->beginIF($this->app->tab == 'project' and !empty($productID) and $queryProductID != 'all')->andWhere('t2.product')->eq($productID)->fi() + ->beginIF($this->app->tab != 'project' and !empty($productID) and $queryProductID != 'all')->andWhere('t1.product')->eq($productID)->fi() + ->beginIF($auto != 'unit')->andWhere('t1.auto')->ne('unit')->fi() + ->beginIF($auto == 'unit')->andWhere('t1.auto')->eq('unit')->fi() + ->andWhere('t1.deleted')->eq(0) + ->orderBy($orderBy)->page($pager)->fetchAll('id'); + + return $cases; + } + + /** + * Get scenes and cases list. + * + * @param mixed $productID int|array + * @param string $branch number|all + * @param int $moduleID + * @param array $caseIdArr + * @param object $pager object|NULL + * @param string $type + * @param array $topIdList + * @param string $browseType + * @access public + * @return array + */ + public function getList($productID,$branch, $moduleID, $caseIdArr, $pager = NULL, $type = '', $topIdList = array(), $browseType, &$executionSql = NULL) + { + /* Get list of module and its children module. */ + $modules = $moduleID ? $this->loadModel('tree')->getAllChildId($moduleID) : '0'; + + /* Get scenes and all cases in $caseIdArr. */ + $cases = $this->dao->select('id,path')->from(VIEW_SCENECASE) + ->where('id')->in($caseIdArr) + ->beginIF($browseType != 'bysearch')->orWhere('( isCase')->eq(2)->fi() + ->beginIF($browseType == 'bysearch')->andWhere('( 1')->eq(1)->fi() + ->beginIF(is_array($productID))->andWhere('product')->in($productID)->fi() + ->beginIF(!is_array($productID) and intval($productID) > 0)->andWhere('product')->eq($productID)->fi() + ->beginIF($branch != 'all')->andWhere('branch')->eq($branch)->fi() + ->beginIF(intval($moduleID) > 0)->andWhere('module')->in($modules)->fi() + ->markRight(1) + ->fetchPairs('id','path'); + + $sceneIdArr = array(); + foreach ($cases as $path) { + $tmpArr = explode(',', trim($path, ',')); + $sceneIdArr = array_merge($sceneIdArr,$tmpArr); + } + $sceneIdArr = array_unique($sceneIdArr); + + /* Get path list. */ + $pathList = $this->dao->select('id,path')->from(VIEW_SCENECASE) + ->where('deleted')->eq(0) + ->beginIF(is_array($productID))->andWhere('product')->in($productID)->fi() + ->beginIF(!is_array($productID) and intval($productID) > 0)->andWhere('product')->eq($productID)->fi() + ->andWhere('id')->in($sceneIdArr) + ->beginIF($modules)->andWhere('module')->in($modules)->fi() + ->beginIF($this->cookie->onlyScene)->andWhere('isCase')->eq(2)->fi() + ->orderBy('id_asc') + ->fetchPairs('id'); + + /* Get all IDs list. */ + $objectIdList = array(); + foreach($pathList as $path) + { + if($type == 'child' and !empty($topIdList)) + { + $paths = explode(',', trim($path, ',')); + $topID = $paths[0]; + + if(!in_array($topID, $topIdList)) continue; + } + + foreach(explode(',', trim($path, ',')) as $pathID) $objectIdList[$pathID] = $pathID; + } + + /* Get paginated data with all IDs list. */ + $queryFunction = function($modules, $type, $objectIdList, $branch) + { + $this->dao->reset(); + $rawMethod = $this->app->rawMethod; + $rawModule = $this->app->rawModule; + return $this->dao->select('*')->from(VIEW_SCENECASE) + ->where('deleted')->eq(0) + ->beginIF($this->cookie->onlyScene)->andWhere('isCase')->eq(2)->fi() + ->beginIF($modules)->andWhere('module')->in($modules)->fi() + ->beginIF($rawMethod == 'browse' and $type === 'top')->andWhere('parent')->eq(0)->andWhere('id')->in($objectIdList)->fi() + ->beginIF($rawMethod == 'browse' and $type === 'child')->andWhere('id')->in($objectIdList)->fi() + ->beginIF($rawModule == 'project' and $rawMethod == 'testcase' and intval($branch) > 0)->andWhere('branch')->eq($branch)->fi() + ->beginIF($rawModule == 'project' and $rawMethod == 'testcase' and $type === 'top')->andWhere('parent')->eq(0)->andWhere('id')->in($objectIdList)->fi() + ->beginIF($rawModule == 'project' and $rawMethod == 'testcase' and $type === 'child')->andWhere('id')->in($objectIdList)->fi(); + }; + + /* Sort by product ID for project list. */ + $orderBy = 'product_desc,sort_asc'; + + /* Get sql for batch execution. */ + if($executionSql !== NULL) $executionSql = $queryFunction($modules, $type, $objectIdList, $branch)->andWhere('isCase')->eq(1)->orderBy($orderBy)->get(); + + return $queryFunction($modules, $type, $objectIdList, $branch)->orderBy($orderBy)->page($pager)->fetchAll('id'); + } + + public function getParents($viewID) + { + if($viewID == 0) return '/'; + + $path = $this->dao->select('path')->from(VIEW_SCENECASE)->where('id')->eq((int)$viewID)->fetch('path'); + $path = trim($path, ','); + if(!$path) return '/'; + + $pathArr = explode(',', $path); + $scenePath = ""; + foreach ($pathArr as $vid) + { + $title = $this->dao->select('title')->from(VIEW_SCENECASE)->where('id')->eq((int)$vid)->fetch('title'); + $scenePath .= '  '.$title; + } + + return substr($scenePath,34); + } + + public function getSceneMenu($rootID, $moduleID, $type = '', $startScene = 0, $branch = 0, $currentScene = 0, $emptyMenu = false) + { + if(empty($branch)) $branch = 0; + + /* If type of $branch is array, get scenes of these branches. */ + if(is_array($branch)) + { + $scenes = array(); + foreach($branch as $branchID) $scenes[$branchID] = $this->getOptionMenu($rootID,$moduleID, $type, $startScene, $branchID,$currentScene); + + return $scenes; + } + + if($type == 'line') $rootID = 0; + + $branches = array($branch => ''); + if($branch != 'all' and strpos('story|bug|case', $type) !== false) + { + $product = $this->loadModel('product')->getById($rootID); + if($product and $product->type != 'normal') + { + $branchPairs = $this->loadModel('branch')->getPairs($rootID, 'all'); + $branches = array($branch => $branchPairs[$branch]); + } + elseif($product and $product->type == 'normal') + { + $branches = array(0 => ''); + } + } + + $treeMenu = array(); + foreach($branches as $branchID => $branch) + { + $scenes = array(); + $stmt = $this->dbh->query($this->buildMenuQuery($rootID, $moduleID, $type, $startScene, $branchID)); + while($scene = $stmt->fetch()) + { + if ($scene->id != $currentScene) $scenes[$scene->id] = $scene; + } + + foreach($scenes as $scene) + { + $branchName = (isset($product) and $product->type != 'normal' and $scene->branch === BRANCH_MAIN) ? $this->lang->branch->main : $branch; + + $this->buildTreeArray($treeMenu, $scenes, $scene, (empty($branchName)) ? '/' : "/$branchName/"); + } + } + + ksort($treeMenu); + $topMenu = @array_shift($treeMenu); + $topMenu = explode("\n", trim((string)$topMenu)); + $lastMenu[] = '/'; + foreach($topMenu as $menu) + { + if(!strpos($menu, '|')) continue; + list($label, $sceneID) = explode('|', $menu); + $lastMenu[$sceneID] = $label; + } + + /* Attach empty option. */ + if($emptyMenu) $lastMenu['null'] = $this->lang->null; + + return $lastMenu; + } + + public function getScenesName($moduleIdList, $allPath = true, $branchPath = false) + { + if(!$allPath) return $this->dao->select('id, title')->from(VIEW_SCENECASE)->where('id')->in($moduleIdList)->andWhere('deleted')->eq(0)->fetchPairs('id', 'title'); + + $modules = $this->dao->select('id, title, path, branch')->from(VIEW_SCENECASE)->where('id')->in($moduleIdList)->andWhere('deleted')->eq(0)->fetchAll('path'); + $allModules = $this->dao->select('id, title')->from(VIEW_SCENECASE)->where('id')->in(join(array_keys($modules)))->andWhere('deleted')->eq(0)->fetchPairs('id', 'title'); + + $branchIDList = array(); + $modulePairs = array(); + foreach($modules as $module) + { + $paths = explode(',', trim($module->path, ',')); + $moduleName = ''; + foreach($paths as $path) $moduleName .= '/' . $allModules[$path]; + $modulePairs[$module->id] = $moduleName; + + if($module->branch) $branchIDList[$module->branch] = $module->branch; + } + + if(!$branchPath) return $modulePairs; + + $branchs = $this->dao->select('id, title')->from(VIEW_SCENECASE)->where('id')->in($branchIDList)->andWhere('deleted')->eq(0)->fetchALL('id'); + foreach($modules as $module) + { + if(isset($modulePairs[$module->id])) + { + $branchName = isset($branchs[$module->branch]) ? '/' . $branchs[$module->branch]->name : ''; + $modulePairs[$module->id] = $branchName . $modulePairs[$module->id]; + } + } + + return $modulePairs; + } + + public function getTestCaseHasScene($productID, $branch, $browseType, $queryID, $moduleID, $sort, $pager, $auto = 'no') + { + /* Set modules and browse type. */ + $modules = $moduleID ? $this->loadModel('tree')->getAllChildId($moduleID) : '0'; + $browseType = ($browseType == 'bymodule' and $this->session->caseBrowseType and $this->session->caseBrowseType != 'bysearch') ? $this->session->caseBrowseType : $browseType; + $group = $this->lang->navGroup->testcase; + + /* By module or all cases. */ + $cases = array(); + if($browseType == 'bymodule' or $browseType == 'all' or $browseType == 'wait') + { + if($this->app->tab == 'project') + { + $cases = $this->getModuleProjectCases($productID, $branch, $modules, $sort, $pager, $browseType, $auto); + } + else + { + $cases = $this->getModuleCases($productID, $branch, $modules, $sort, $pager, $browseType, $auto); + } + } + /* Cases need confirmed. */ + elseif($browseType == 'needconfirm') + { + $cases = $this->dao->select('distinct t1.*, t2.title AS storyTitle')->from(TABLE_CASE)->alias('t1') + ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id') + ->leftJoin(TABLE_PROJECTCASE)->alias('t3')->on('t1.id = t3.case') + ->where("t2.status = 'active'") + ->andWhere('t1.deleted')->eq(0) + ->andWhere('t2.version > t1.storyVersion') + ->beginIF(!empty($productID))->andWhere('t1.product')->eq($productID)->fi() + ->beginIF($this->app->tab == 'project')->andWhere('t3.project')->eq($this->session->project)->fi() + ->beginIF($branch !== 'all')->andWhere('t1.branch')->eq($branch)->fi() + ->beginIF($modules)->andWhere('t1.module')->in($modules)->fi() + ->beginIF($auto != 'unit')->andWhere('t1.auto')->ne('unit')->fi() + ->beginIF($auto == 'unit')->andWhere('t1.auto')->eq('unit')->fi() + ->orderBy($sort) + ->page($pager, 't1.id') + ->fetchAll(); + } + elseif($browseType == 'bysuite') + { + $cases = $this->getBySuite($productID, $branch, $queryID, $modules, $sort, $pager, $auto); + } + /* By search. */ + elseif($browseType == 'bysearch') + { + $cases = $this->getBySearchHasScene($productID, $queryID, $sort, $pager, $branch, $auto); + } + + return $cases; + } + + public function getXmindConfig() + { + return $this->loadExtension('xmind')->getXmindConfig(); + } + + public function getXmindExport($productID, $moduleID,$branch) + { + return $this->loadExtension('xmind')->getXmindExport($productID, $moduleID,$branch); + } + + public function getXmindImport($xmlPath) + { + return $this->loadExtension('xmind')->getXmindImport($xmlPath); + } + + public function istrcut($text, $length) + { + return (mb_strlen($text, 'utf8') > $length) ? mb_substr($text, 0, $length, 'utf8').'...' : $text; + } + public function printHead($col, $orderBy, $vars, $checkBox = true) + { + $id = $col->id; + if($col->show) + { + $fixed = $col->fixed == 'no' ? 'true' : 'false'; + $width = is_numeric($col->width) ? "{$col->width}px" : $col->width; + $title = isset($col->title) ? "title='$col->title'" : ''; + $title = (isset($col->name) and $col->name) ? "title='$col->name'" : $title; + if($id == 'id' and (int)$width < 90) $width = '120px'; + + $align = $id == 'actions' ? 'text-center' : ''; + $align = in_array($id, array('budget', 'teamCount', 'estimate', 'consume', 'consumed', 'left')) ? 'text-right' : $align; + + $style = ''; + $data = ''; + $data .= "data-width='$width'"; + $style .= "width:$width;"; + + if(isset($col->minWidth)) + { + $data .= "data-minWidth='{$col->minWidth}px'"; + $style .= "min-width:{$col->minWidth}px;"; + } + + if(isset($col->maxWidth)) + { + $data .= "data-maxWidth='{$col->maxWidth}px'"; + $style .= "max-width:{$col->maxWidth}px;"; + } + + if(isset($col->pri)) $data .= "data-pri='{$col->pri}'"; + if($col->title == $this->lang->testcase->title) + { + echo ""; + } + else + { + echo ""; + } + + if($id == 'actions') + { + echo $this->lang->actions; + } + else + { + if($id == 'id' && $checkBox) echo "
"; + if($col->title == $this->lang->testcase->title) + { + echo $this->lang->testcase->generalTitle; + } + else + { + echo $col->title; + } + } + + echo ''; + } + } + + public function saveXmindConfig() + { + return $this->loadExtension('xmind')->saveXmindConfig(); + } + + public function saveXmindImport() + { + return $this->loadExtension('xmind')->saveXmindImport(); + } + + public function updateScene($sceneID) + { + /* Get original data. */ + $scene = $this->dao->findById((int)$sceneID)->from(VIEW_SCENECASE)->fetch(); + $now = helper::now(); + + /* Collect changed data. */ + $scenePost = fixer::input('post') + ->add('id', $sceneID - CHANGEVALUE) + ->setDefault('lastEditedBy', $this->app->user->account) + ->add('lastEditedDate', $now) + ->cleanInt('product,module') + ->get(); + + /* Update scene with changed data. */ + $this->dao->update(TABLE_SCENE)->data($scenePost) + ->batchCheck($this->config->testcase->createscene->requiredFields, 'notempty') + ->where('id')->eq((int)$sceneID - CHANGEVALUE) + ->checkFlow() + ->exec(); + + /* Verify database error. */ + if($this->dao->isError()) return; + + $sceneNew = $this->dao->findById((int)$sceneID - CHANGEVALUE)->from(TABLE_SCENE)->fetch(); + $childPath = ""; + $grade = ""; + $viewSceneID = $sceneID; + + /* Not change key fields. */ + if($scene->parent == $sceneNew->parent and $scene->product == $sceneNew->product and $scene->branch == $sceneNew->branch and $scene->module == $sceneNew->module) return array('status' => 'updated', 'id' => $sceneID);; + + if($sceneNew->parent) + { + /* Update product, module, branch, path and grade field with parent scene. */ + $resultScene = $this->dao->findById((int)$sceneNew->parent - CHANGEVALUE)->from(TABLE_SCENE)->fetch(); + $childPath = $resultScene->path . $viewSceneID . ','; + $grade = $resultScene->grade + 1; + $this->dao->update(TABLE_SCENE) + ->set('path')->eq($childPath) + ->set('grade')->eq($grade) + ->set('product')->eq($resultScene->product) + ->set('module')->eq($resultScene->module) + ->set('branch')->eq($resultScene->branch) + ->where('id')->eq($sceneID - CHANGEVALUE) + ->exec(); + } + else + { + /* Update path and grade field without parent scene. */ + $childPath = ",$viewSceneID,"; + $grade = 1; + $this->dao->update(TABLE_SCENE) + ->set('path')->eq($childPath) + ->set('grade')->eq($grade) + ->where('id')->eq($sceneID - CHANGEVALUE) + ->exec(); + } + + /* Get children scenes and cases. */ + $children = $this->dao->select('*')->from(VIEW_SCENECASE) + ->where('deleted')->eq(0) + ->andWhere('(path')->like($scene->path.'%') + ->orWhere('path')->like(",$sceneID,%") + ->markRight(1) + ->orderBy('grade asc') + ->fetchAll('id'); + + foreach($children as $id => $childScene) + { + if(!$id or $id == $sceneID or !$childScene->parent) continue; + + /* Get grade of child with parent scene. */ + $parentScene = $this->dao->findById($childScene->parent - CHANGEVALUE)->from(TABLE_SCENE)->fetch(); + + /* Update sub-scene. */ + if($childScene->isCase == 2) + { + /* The grade of child node must be greater than parent node. */ + if($childScene->grade <= $scene->grade) continue; + + $viewID = $id; + $childPath = $parentScene->path . $viewID . ','; + $grade = $parentScene->grade + 1; + + $this->dao->update(TABLE_SCENE) + ->set('path')->eq($childPath) + ->set('grade')->eq($grade) + ->set('product')->eq($parentScene->product) + ->set('module')->eq($parentScene->module) + ->set('branch')->eq($parentScene->branch) + ->set('lastEditedBy')->eq($this->app->user->account) + ->set('lastEditedDate')->eq($now) + ->where('id')->eq($id - CHANGEVALUE) + ->exec(); + + continue; + } + + /* Update case. */ + $this->dao->update(TABLE_CASE) + ->set('product')->eq($parentScene->product) + ->set('module')->eq($parentScene->module) + ->set('branch')->eq($parentScene->branch) + ->set('lastEditedBy')->eq($this->app->user->account) + ->set('lastEditedDate')->eq($now) + ->where('id')->eq($id) + ->exec(); + } + + return array('status' => 'updated', 'id' => $sceneID); + } } diff --git a/module/testcase/view/batchcreate.html.php b/module/testcase/view/batchcreate.html.php index 33e4fe176e..16d042dc8a 100644 --- a/module/testcase/view/batchcreate.html.php +++ b/module/testcase/view/batchcreate.html.php @@ -57,6 +57,7 @@ idAB;?> branchBox'>product->branch;?> moduleBox'>testcase->module;?> + sceneBox'>testcase->scene;?> storyBox'> testcase->story;?> testcase->title;?> testcase->type;?> @@ -81,6 +82,7 @@ testcase->batchCreate; $i++):?> testcase->typeList['ditto'] = $lang->testcase->ditto; if($i != 1) $lang->testcase->priList['ditto'] = $lang->testcase->ditto; $type = $i == 1 ? 'feature' : 'ditto'; @@ -89,7 +91,8 @@ branchBox'> - moduleBox' style='overflow:visible'> + moduleBox' style='overflow:visible'> + ' style='overflow:visible;'> storyBox' style='overflow:visible'> id : '', 'class="form-control picker-select"');?>
@@ -140,7 +143,8 @@ %s branchBox'> - moduleBox' style='overflow:visible'> + moduleBox' style='overflow:visible'> + ' style='overflow:visible'> storyBox' style='overflow:visible'>
@@ -179,7 +183,8 @@ branchBox'> - moduleBox' style='overflow:visible'> + moduleBox' style='overflow:visible'> + ' style='overflow:visible'> storyBox' style='overflow:visible'> id : '', 'class="form-control picker-select"');?>
diff --git a/module/testcase/view/batchedit.html.php b/module/testcase/view/batchedit.html.php index b3c62fe092..a251acb8fe 100644 --- a/module/testcase/view/batchedit.html.php +++ b/module/testcase/view/batchedit.html.php @@ -57,6 +57,9 @@ testcase->branch;?> '>testcase->module;?> + + '>testcase->scene;?> + '>testcase->story;?> testcase->title;?> testcase->type;?> @@ -96,7 +99,10 @@ branch : '', "class='form-control chosen' onchange='loadBranches($branchProductID, this.value, $caseID, {$cases[$caseID]->branch})', $disabled");?> - ' style='overflow:visible'> '/')), $cases[$caseID]->module, "class='form-control chosen' onchange='loadStories($productID, this.value, $caseID)'");?> + ' style='overflow:visible'> '/')), $cases[$caseID]->module, "class='form-control chosen' onchange='loadStories2($productID, this.value, $caseID)'");?> + + ' style='overflow:visible'> '/')), $cases[$caseID]->scene, "class='form-control chosen' data-drop_direction='down'");?> + ' style='overflow:visible'>story, "class='form-control picker-select'");?>
diff --git a/module/testcase/view/browse.html.php b/module/testcase/view/browse.html.php index 8a0b65fac5..7dece08a85 100644 --- a/module/testcase/view/browse.html.php +++ b/module/testcase/view/browse.html.php @@ -13,20 +13,25 @@ session->caseBrowseType == 'bysearch') ? 'all' : $this->session->caseBrowseType); -js::set('moduleID' , $moduleID); -js::set('confirmDelete', $lang->testcase->confirmDelete); -js::set('batchDelete', $lang->testcase->confirmBatchDelete); -js::set('productID', $productID); -js::set('branch', $branch); -js::set('suiteID', $suiteID); -js::set('automation', !empty($automation) ? $automation->id : 0); -js::set('runCaseConfirm', $lang->zanode->runCaseConfirm); -js::set('confirmURL', $this->createLink('testtask', 'batchRun', "productID=$productID&orderBy=$orderBy&from=testcase&taskID=0&confirm=yes")); -js::set('cancelURL', $this->createLink('testtask', 'batchRun', "productID=$productID&orderBy=$orderBy&from=testcase&taskID=0&confirm=no")); +js::set('browseType', $browseType); +js::set('caseBrowseType', ($browseType == 'bymodule' and $this->session->caseBrowseType == 'bysearch') ? 'all' : $this->session->caseBrowseType); +js::set('moduleID' , $moduleID); +js::set('confirmDelete', $lang->testcase->confirmDelete); +js::set('batchDelete', $lang->testcase->confirmBatchDeleteSceneCase); +js::set('productID', $productID); +js::set('branch', $branch); +js::set('suiteID', $suiteID); +js::set('automation', !empty($automation) ? $automation->id : 0); +js::set('runCaseConfirm', $lang->zanode->runCaseConfirm); +js::set('confirmURL', $this->createLink('testtask', 'batchRun', "productID=$productID&orderBy=$orderBy&from=testcase&taskID=0&confirm=yes")); +js::set('cancelURL', $this->createLink('testtask', 'batchRun', "productID=$productID&orderBy=$orderBy&from=testcase&taskID=0&confirm=no")); +js::set('orderBy', $orderBy); +js::set('differentProduct', $lang->testcase->differentProduct); +js::set('langRowIndex', $lang->testcase->rowIndex); +js::set('langNestTotal', $lang->testcase->nestTotal); +js::set('langNormal', $lang->testcase->normal); ?> app->tab == 'project'):?> + + + + + diff --git a/module/testcase/view/caseheader.html.php b/module/testcase/view/caseheader.html.php index 413c957646..5c31522a5b 100644 --- a/module/testcase/view/caseheader.html.php +++ b/module/testcase/view/caseheader.html.php @@ -202,6 +202,11 @@
+ + +
diff --git a/module/testcase/view/create.html.php b/module/testcase/view/create.html.php index da6a8caf1c..6db5f14614 100644 --- a/module/testcase/view/create.html.php +++ b/module/testcase/view/create.html.php @@ -45,8 +45,8 @@ foreach(explode(',', $config->testcase->create->requiredFields) as $field) testcase->module : $lang->testcase->product;?> '>
- - type) and $product->type != 'normal') echo html::select('branch', $branches, $branch, "onchange='loadBranch();' class='form-control' style='width:120px'");?> + + type) and $product->type != 'normal') echo html::select('branch', $branches, $branch, "onchange='loadBranchNew();' class='form-control' style='width:120px'");?>
'> @@ -55,12 +55,12 @@ foreach(explode(',', $config->testcase->create->requiredFields) as $field) testcase->module?> "; echo html::a($this->createLink('tree', 'browse', "rootID=$productID&view=case¤tModuleID=0&branch=$branch", '', true), $lang->tree->manage, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'"); - echo html::a("javascript:void(0)", $lang->refreshIcon, '', "id='refresh' class='refresh' title='$lang->refresh' onclick='loadProductModules($productID)'"); + echo html::a("javascript:void(0)", $lang->refreshIcon, '', "id='refresh' class='refresh' title='$lang->refresh' onclick='loadProductModulesNew($productID)'"); echo ''; } ?> @@ -107,6 +107,14 @@ foreach(explode(',', $config->testcase->create->requiredFields) as $field)
+ + testcase->scene;?> + +
+ +
+ + testcase->title;?> diff --git a/module/testcase/view/createscene.html.php b/module/testcase/view/createscene.html.php new file mode 100644 index 0000000000..dbb26ed4a2 --- /dev/null +++ b/module/testcase/view/createscene.html.php @@ -0,0 +1,90 @@ + + * @package case + * @version $Id: create.html.php 4904 2013-06-26 05:37:45Z wyd621@gmail.com $ + * @link http://www.zentao.net + */ +?> +getModuleRoot() . 'common/view/header.html.php';?> +getModuleRoot() . 'common/view/kindeditor.html.php';?> + +testcase->deleteStep);?> +testcase->insertBefore);?> +testcase->insertAfter);?> + +app->tab);?> + + +app->tab == 'project') js::set('objectID', $projectID);?> +
+
+
+

testcase->newScene;?>

+
+ testcase->createscene->requiredFields) as $field) + { + if($field and strpos($showFields, $field) === false) $showFields .= ',' . $field; + } + ?> +
+ + + + + + + + + + + + + + + + + + + + + +
testcase->product;?> +
+ + type) and $product->type != 'normal') echo html::select('branch', $branches, $branch, "onchange='loadBranchNew();' class='form-control' style='width:120px'");?> +
+
+
+ testcase->module?> + "; + echo html::a($this->createLink('tree', 'browse', "rootID=$productID&view=case¤tModuleID=0&branch=$branch", '', true), $lang->tree->manage, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'"); + echo html::a("javascript:void(0)", $lang->refresh, '', "class='refresh' onclick='loadProductModulesNew($productID)'"); + echo ''; + } + ?> +
+
testcase->parentScene;?> +
+ +
+
testcase->sceneTitle;?> + +
+ + goback, '', 'class="btn btn-wide"') : html::backButton();?> +
+
+
+
+testcase->module)?> +getModuleRoot() . 'common/view/footer.html.php';?> diff --git a/module/testcase/view/datatable.fix.html.php b/module/testcase/view/datatable.fix.html.php new file mode 100644 index 0000000000..dc1b5db75b --- /dev/null +++ b/module/testcase/view/datatable.fix.html.php @@ -0,0 +1,134 @@ +app->rawModule;?> +app->rawMethod;?> +moduleName . ucfirst($this->methodName);?> + + + + + diff --git a/module/testcase/view/edit.html.php b/module/testcase/view/edit.html.php index 0505f61736..d1a095da57 100644 --- a/module/testcase/view/edit.html.php +++ b/module/testcase/view/edit.html.php @@ -51,6 +51,14 @@
+
+
testcase->scene;?>
+
+
+ +
+
+
testcase->precondition;?>
precondition, "rows='2' class='form-control'");?>
@@ -179,8 +187,8 @@ testcase->product;?>
- - type) and $product->type != 'normal') echo html::select('branch', $branchTagOption, $case->branch, "onchange='loadBranch($case->branch);' class='form-control'");?> + + type) and $product->type != 'normal') echo html::select('branch', $branchTagOption, $case->branch, "onchange='loadBranchNew($case->branch);' class='form-control'");?>
@@ -189,13 +197,13 @@
"; echo html::a($this->createLink('tree', 'browse', "rootID=$productID&view=case¤tModuleID=0&branch=$case->branch", '', true), $lang->tree->manage, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'"); echo '  '; - echo html::a("javascript:void(0)", $lang->refreshIcon, '', "class='refresh' onclick='loadProductModules($productID)'"); + echo html::a("javascript:void(0)", $lang->refreshIcon, '', "class='refresh' onclick='loadProductModulesNew($productID)'"); echo ''; } diff --git a/module/testcase/view/editscene.html.php b/module/testcase/view/editscene.html.php new file mode 100644 index 0000000000..51ed68b39f --- /dev/null +++ b/module/testcase/view/editscene.html.php @@ -0,0 +1,99 @@ + + * @package case + * @version $Id: create.html.php 4904 2013-06-26 05:37:45Z wyd621@gmail.com $ + * @link http://www.zentao.net + */ +?> +getModuleRoot() . 'common/view/header.html.php';?> +getModuleRoot() . 'common/view/kindeditor.html.php';?> + + + + +testcase->deleteStep);?> +testcase->insertBefore);?> +testcase->insertAfter);?> +id);?> +branch);?> + +app->tab);?> +app->tab == 'execution') js::set('objectID', $executionID);?> +
+
+ id, 1); + $showid = preg_replace('/^0+/', '', $showid); + ?> +
+

+ + title;?> +

+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
testcase->product;?> +
+ + type) and $product->type != 'normal') echo html::select('branch', $branchTagOption, $scene->branch, "onchange='loadBranchNew();' class='form-control' style='width:120px'");?> +
+
+
+ testcase->module?> + "; + echo html::a($this->createLink('tree', 'browse', "rootID=$productID&view=case¤tModuleID=0&branch=$branch", '', true), $lang->tree->manage, '', "class='text-primary' data-toggle='modal' data-type='iframe' data-width='95%'"); + echo html::a("javascript:void(0)", $lang->refresh, '', "class='refresh' onclick='loadProductModulesNew($productID)'"); + echo ''; + } + ?> +
+
testcase->parentScene;?> +
+ +
+
testcase->sceneTitle;?> +
+
+ title, "class='form-control'");?> +
+
+
+ + goback, '', 'class="btn btn-wide"') : html::backButton();?> +
+
+
+
+testcase->module)?> +getModuleRoot() . 'common/view/footer.html.php';?> diff --git a/module/testsuite/control.php b/module/testsuite/control.php index 477eb95c91..15fb81b198 100644 --- a/module/testsuite/control.php +++ b/module/testsuite/control.php @@ -338,8 +338,13 @@ class testsuite extends control $this->loadModel('testcase'); $this->config->testcase->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($productID, $viewType = 'case', 0, 'all'); $this->config->testcase->search['params']['lib']['values'] = $this->loadModel('caselib')->getLibraries(); + $this->config->testcase->search['module'] = 'testsuite'; $this->config->testcase->search['actionURL'] = inlink('linkCase', "suiteID=$suiteID¶m=myQueryID"); + + $scene = $this->testcase->getSceneMenu($productID, 0, '', 0, 0,0); + $this->config->testcase->search['params']['scene']['values'] = array('' => '') + $scene; + unset($this->config->testcase->search['fields']['product']); unset($this->config->testcase->search['params']['product']); unset($this->config->testcase->search['fields']['branch']); diff --git a/module/testtask/control.php b/module/testtask/control.php index 8d24f87392..347a9ca15a 100644 --- a/module/testtask/control.php +++ b/module/testtask/control.php @@ -540,6 +540,7 @@ class testtask extends control $this->config->testcase->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($productID, $viewType = 'case'); $this->config->testcase->search['params']['status']['values'] = array('' => '') + $this->lang->testcase->statusList; $this->config->testcase->search['params']['lib']['values'] = $this->loadModel('caselib')->getLibraries(); + $this->config->testcase->search['params']['scene']['values'] = $this->testcase->getSceneMenu($productID, $moduleID, 'case', 0, 0); $this->config->testcase->search['queryID'] = $queryID; $this->config->testcase->search['fields']['assignedTo'] = $this->lang->testtask->assignedTo; @@ -1109,8 +1110,10 @@ class testtask extends control $this->loadModel('testcase'); $this->config->testcase->search['params']['product']['values'] = array($productID => $this->products[$productID]); $this->config->testcase->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($productID, 'case', 0, $task->branch); - $this->config->testcase->search['actionURL'] = inlink('linkcase', "taskID=$taskID&type=$type¶m=$param"); - $this->config->testcase->search['style'] = 'simple'; + $this->config->testcase->search['actionURL'] = inlink('linkcase', "taskID=$taskID&type=$type¶m=$param"); + $this->config->testcase->search['params']['scene']['values'] = $this->testcase->getSceneMenu($productID, 0, $viewType = 'case', $startSceneID = 0, 0); + $this->config->testcase->search['style'] = 'simple'; + $build = $this->loadModel('build')->getByID($task->build); $stories = array(); diff --git a/www/js/mindmap/css/zui.mindmap.css b/www/js/mindmap/css/zui.mindmap.css new file mode 100644 index 0000000000..8504439d5b --- /dev/null +++ b/www/js/mindmap/css/zui.mindmap.css @@ -0,0 +1,264 @@ +/*! + * ZUI: 思维导图 - v1.10.0 - 2022-05-20 + * http://openzui.com + * GitHub: https://github.com/easysoft/zui.git + * Copyright (c) 2022 cnezsoft.com; Licensed MIT + */ + +.mindmap { + position: relative; + z-index: 0; + overflow: hidden; + } +.mindmap-container { + position: absolute; + top: 0; + left: 0; + z-index: 1; + width: 100%; + height: 100%; + background: #fff; + border: 1px solid #e5e5e5; + } +.mindmap-container.dragging { + cursor: pointer; + } +.mindmap-bg { + position: absolute; + top: 0; + left: 0; + z-index: 5; + max-width: inherit; + } +.mindmap-desktop { + position: absolute; + top: 0; + left: 0; + z-index: 10; + width: 100%; + height: 100%; + overflow: hidden; + -webkit-user-select: none; + -moz-user-select: none; + } +.mindmap-node { + position: absolute; + border-radius: 24px; + } +.mindmap-node > .wrapper { + min-height: 28px; + background: #ebf2f9; + border: 4px solid #aaa; + border-radius: 22px; + -webkit-transition: all .2s cubic-bezier(.175, .885, .32, 1); + -o-transition: all .2s cubic-bezier(.175, .885, .32, 1); + transition: all .2s cubic-bezier(.175, .885, .32, 1); + } +.mindmap-node > .wrapper > .text { + padding: 8px 12px; + font-size: 14px; + font-weight: bold; + cursor: default; + } +.mindmap-node > .wrapper > .text * { + display: inline; + } +.mindmap-node > .wrapper > .text br { + display: none; + } +.mindmap-node > .wrapper > .text:focus { + outline: none; + } +.mindmap-node > .wrapper > .text:empty:not(:focus) { + position: relative; + top: 7px; + min-width: 10px; + min-height: 10px; + background: #508dee; + border-radius: 50%; + } +.mindmap-node > .wrapper > .caption { + display: none; + } +.mindmap-node:hover { + -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .4); + box-shadow: 0 0 5px rgba(0, 0, 0, .4); + } +.mindmap-node:hover > .wrapper { + border-color: #999; + } +.mindmap-node[data-type="root"] > .wrapper { + min-width: 45px; + min-height: 45px; + } +.mindmap-node[data-type="root"] > .wrapper > .text { + text-align: center; + } +.mindmap-node[data-type="root"] > .wrapper > .text:empty:not(:focus) { + top: 0; + min-width: 37px; + min-height: 37px; + background: none; + border-radius: 50%; + } +.mindmap-node[data-type="sub"] { + border-radius: 6px; + } +.mindmap-node[data-type="sub"] > .wrapper { + background: #fff; + border-width: 2px; + border-radius: 5px; + } +.mindmap-node[data-type="sub"] > .wrapper > .text { + padding: 3px 5px; + font-size: 13px; + font-weight: normal; + } +.mindmap-node[data-type="sub"] > .wrapper > .text:empty:not(:focus) { + background: none; + } +.mindmap-node[data-type="node"] { + border-radius: 4px; + } +.mindmap-node[data-type="node"] > .wrapper { + background: transparent; + border: 2px solid transparent; + border-radius: 3px; + } +.mindmap-node[data-type="node"] > .wrapper > .text { + padding: 3px 3px; + font-size: 13px; + font-weight: normal; + } +.mindmap-node[data-type="node"]:hover > .wrapper { + color: #353535; + background: #fff; + border-color: #999; + } +.mindmap-node.active, +.mindmap-node.active:hover, +.mindmap-node.drag-shadow { + z-index: 900; + -webkit-box-shadow: none; + box-shadow: none; + } +.mindmap-node.active > .wrapper, +.mindmap-node.active:hover > .wrapper, +.mindmap-node.drag-shadow > .wrapper { + color: #fff; + background: #508dee; + border-color: #2a74ea; + } +.mindmap-node.focus, +.mindmap-node.focus:hover, +.mindmap-node.drop-to { + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(20, 92, 205, .6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(20, 92, 205, .6); + } +.mindmap-node.focus > .wrapper, +.mindmap-node.focus:hover > .wrapper, +.mindmap-node.drop-to > .wrapper { + color: #353535; + background: #fff; + border-color: #145ccd; + } +.mindmap-node.focus > .wrapper > .text, +.mindmap-node.focus:hover > .wrapper > .text, +.mindmap-node.drop-to > .wrapper > .text { + cursor: text; + } +.mindmap-node .btn-toggle { + position: absolute; + top: 50%; + right: -14px; + display: none; + width: 16px; + height: 16px; + margin-top: -9px; + font-size: 16px; + line-height: 12px; + color: #fff; + text-align: center; + cursor: pointer; + visibility: hidden; + background: #808080; + border: 1px solid transparent; + border-radius: 50%; + opacity: 0; + -webkit-transition-duration: .2s; + -o-transition-duration: .2s; + transition-duration: .2s; + -webkit-transition-property: visibility, opacity, border-color, -webkit-box-shadow; + -o-transition-property: visibility, opacity, border-color, box-shadow; + transition-property: visibility, opacity, border-color, -webkit-box-shadow; + transition-property: visibility, opacity, border-color, box-shadow; + transition-property: visibility, opacity, border-color, box-shadow, -webkit-box-shadow; + } +.mindmap-node .btn-toggle:hover { + background: #3280fc; + border-color: rgba(0, 0, 0, .1); + -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .4); + box-shadow: 0 0 5px rgba(0, 0, 0, .4); + } +.mindmap-node .btn-toggle:before { + content: '-'; + } +.mindmap-node.mindmap-side-left .btn-toggle { + right: auto; + left: -14px; + } +.mindmap-node.mindmap-collapesed .btn-toggle:before { + content: '+'; + } +.mindmap-node[data-type="root"] .btn-toggle { + display: none!important; + } +.mindmap-show-toggle-btn .mindmap-has-child .btn-toggle { + display: block; + } +.mindmap-show-toggle-btn .mindmap-node.focus .btn-toggle { + display: none; + } +.mindmap-show-toggle-btn .mindmap-node.mindmap-collapesed .btn-toggle, +.mindmap-show-toggle-btn .mindmap-node:hover .btn-toggle { + visibility: visible; + opacity: 1; + } +.mindmap-shadow { + position: absolute; + z-index: 1000; + width: 100%; + height: 100%; + -webkit-transition: all .4s cubic-bezier(.175, .885, .32, 1); + -o-transition: all .4s cubic-bezier(.175, .885, .32, 1); + transition: all .4s cubic-bezier(.175, .885, .32, 1); + } +.mindmap-shadow.shadow-left, +.mindmap-shadow.shadow-right { + top: 0; + width: 20px; + } +.mindmap-shadow.shadow-left { + left: -20px; + } +.mindmap-shadow.shadow-right { + right: -20px; + } +.mindmap-shadow.shadow-top, +.mindmap-shadow.shadow-bottom { + left: 0; + height: 20px; + } +.mindmap-shadow.shadow-top { + top: -20px; + } +.mindmap-shadow.shadow-bottom { + bottom: -20px; + } +.shadow-left > .shadow-left, +.shadow-right > .shadow-right, +.shadow-top > .shadow-top, +.shadow-bottom > .shadow-bottom { + -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, .8); + box-shadow: 0 0 8px rgba(0, 0, 0, .8); + } diff --git a/www/js/mindmap/js/hotkey.min.js b/www/js/mindmap/js/hotkey.min.js new file mode 100644 index 0000000000..fcbe8cab63 --- /dev/null +++ b/www/js/mindmap/js/hotkey.min.js @@ -0,0 +1,12 @@ +/*! + * jQuery Hotkeys Plugin + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Based upon the plugin by Tzury Bar Yochay: + * http://github.com/tzuryby/hotkeys + * + * Original idea by: + * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ +*/ +!function(e){function t(t){if("string"==typeof t.data){var s=t.handler,a=t.data.toLowerCase().split(" ");t.handler=function(t){if(this===t.target||!/textarea|select/i.test(t.target.nodeName)&&"text"!==t.target.type){var r="keypress"!==t.type&&e.hotkeys.specialKeys[t.which],f=String.fromCharCode(t.which).toLowerCase(),i="",h={};t.altKey&&"alt"!==r&&(i+="alt+"),t.ctrlKey&&"ctrl"!==r&&(i+="ctrl+"),t.metaKey&&!t.ctrlKey&&"meta"!==r&&(i+="meta+"),t.shiftKey&&"shift"!==r&&(i+="shift+"),r?h[i+r]=!0:(h[i+f]=!0,h[i+e.hotkeys.shiftNums[f]]=!0,"shift+"===i&&(h[e.hotkeys.shiftNums[f]]=!0));for(var o=0,c=a.length;o","/":"?","\\":"|"}},e.each(["keydown","keyup","keypress"],function(){e.event.special[this]={add:t}})}(jQuery); \ No newline at end of file diff --git a/www/js/mindmap/js/zui.mindmap.js b/www/js/mindmap/js/zui.mindmap.js new file mode 100644 index 0000000000..f5adf46755 --- /dev/null +++ b/www/js/mindmap/js/zui.mindmap.js @@ -0,0 +1,1438 @@ +/*! + * ZUI: 思维导图 - v1.10.0 - 2022-05-20 + * http://openzui.com + * GitHub: https://github.com/easysoft/zui.git + * Copyright (c) 2022 cnezsoft.com; Licensed MIT + */ + +/* ======================================================================== + * ZUI: mindmap.js + * http://openzui.com + * ======================================================================== + * Copyright (c) 2014-2016 cnezsoft.com; Licensed MIT + * ======================================================================== */ + + +(function($, window, document, Math, undefined) { + 'use strict'; + + var newColorIndex = 0; + + var selectText = function($e) { + var doc = document; + var element = $e[0], + range; + if(doc.body.createTextRange) { + range = document.body.createTextRange(); + range.moveToElementText(element); + range.select(); + } else if(window.getSelection) { + var selection = window.getSelection(); + range = document.createRange(); + range.selectNodeContents(element); + selection.removeAllRanges(); + selection.addRange(range); + } + }; + + var Mindmap = function(element, options) { + this.$ = $(element); + this.options = this.getOptions(options); + this.lang = this.getLang(); + this.data = this.options.data; + this.dirtyData = true; + this.offsetX = 0; + this.offsetY = 0; + this.init(); + + if(!this.options.hotkeyEnable) { + $.zui.messager.show(this.lang.hotkeyDisabled); + } + }; + + Mindmap.DEFAULTS = { + hotkeyEnable: true, + hotkeys: { + selectPrev: 'Up', + selectNext: 'Down', + selectLeft: 'left', + selectRight: 'right', + deleteNode: 'del backspace', + addBrother: 'return', + addChild: 'tab insert', + centerCanvas: 'space', + toggleNode: '+ -' + }, + lang: 'zh_cn', + langs: { + 'zh_cn': { + defaultName: '灵光闪现', + defaultSubName: '灵光', + defaultNodeName: '闪现', + readonlyTip: '该节点已被设置为只读,无法进行编辑。', + hotkeyDisabled: '快捷键不可用,需要 jquery.hotkeys 插件支持。' + } + }, + data: { + text: 'New Project', + type: 'root', + collapesed: false, + theme: 'default', + caption: '', + id: $.zui.uuid() + '' + }, + nodeTeamplate: "
{text}
{caption}
", + hSpace: 100, + vSpace: 10, + canvasPadding: 20, + removingNodeTip: null, + lineCurvature: 60, + subLineWidth: 4, + lineColor: 'rainbow', + lineOpacity: 1, + lineSaturation: 90, + lineLightness: 40, + nodeLineWidth: 2, + showToggleButton: false, + readonly: false, + enableDrag: true + }; // default options + + Mindmap.prototype.getOptions = function(options) { + Mindmap.DEFAULTS.data.text = Mindmap.DEFAULTS.langs.zh_cn.defaultName; + + options = $.extend({}, Mindmap.DEFAULTS, this.$.data(), options); + + options.hotkeyEnable = options.hotkeyEnable && ($.hotkeys !== undefined); + + return options; + }; + + Mindmap.prototype.getLang = function() { + var options = this.options; + if(!options.lang) { + if(window.config !== undefined && window.config.clientLang) { + options.lang = config.clientLang; + } else { + var hl = $('html').attr('lang'); + options.lang = hl ? hl : 'en'; + } + options.lang = options.lang.replace(/-/, '_').toLowerCase(); + } + var lang = options.langs[options.lang] || options.langs[Mindmap.DEFAULTS.lang]; + + if(options.defaultSubName) lang.defaultSubName = options.defaultSubName; + if(options.defaultNodeName) lang.defaultNodeName = options.defaultNodeName; + + return lang; + }; + + Mindmap.prototype.init = function() { + this.initDom(); + this.initSize(); + this.bindEvents(); + + this.render(); + }; + + Mindmap.prototype.initDom = function() { + var $this = this.$; + + if(!$this.attr('id')) { + $this.attr('id', 'mindmap-' + $.zui.uuid()); + } + this.id = $this.attr('id'); + + this.$container = $this.children('.mindmap-container'); + if(!this.$container.length) { + $this.prepend("
"); + this.$container = $this.children('.mindmap-container'); + } + + this.$canvas = this.$container.children('canvas'); + if(!this.$canvas.length) { + this.$container.prepend("".format(this.options.canvasSize)); + this.$canvas = this.$container.children('canvas'); + } + + this.$desktop = this.$container.children('.mindmap-desktop'); + if(!this.$desktop.length) { + this.$container.append("
"); + this.$desktop = this.$container.children('.mindmap-desktop'); + } + this.$desktop.attr('unselectable', 'on').toggleClass('mindmap-show-toggle-btn', !!this.options.showToggleButton); + // var $shadows = $this.children('.shadow'); + // if(!$shadows.length) { + // $this.append("
"); + // } + }; + + Mindmap.prototype.initSize = function() { + var $this = this.$; + this.winWidth = $this.width(); + this.winHeight = $this.height(); + this.centerX = Math.floor(this.winWidth / 2); + this.centerY = Math.floor(this.winHeight / 2); + + // $canvas.attr('width', this.width) + // .attr('height', this.height); + + if(!this.dirtyData) this.display(); + }; + + Mindmap.prototype.callEvent = function(name, params) { + return $.zui.callEvent(this.options[name], params, this); + }; + + /* compute position with offset */ + Mindmap.prototype.computePosition = function(pos, inverse) { + var flag = inverse ? -1 : 1; + if(pos.left !== undefined) pos.left -= (this.left - this.options.canvasPadding) * flag; + if(pos.top !== undefined) pos.top -= this.top * flag; + if(pos.x !== undefined) pos.x -= (this.left - this.options.canvasPadding) * flag; + if(pos.y !== undefined) pos.y -= this.top * flag; + return pos; + }; + + /* compute color with hue value in hsl format*/ + Mindmap.prototype.computeColor = function(color, alpha) { + if (!(color instanceof $.zui.Color)) { + color = new $.zui.Color(color); + } + if (typeof alpha === 'number') { + color.fade(alpha); + } + return color.toCssStr(); + }; + + Mindmap.prototype.getNodeData = function(id, nodeData) { + if(typeof(id) == 'number') id = id.toString(); + if(!nodeData) { + nodeData = this.data; + } + + if(id == nodeData.id) { + return nodeData; + } else if(Array.isArray(nodeData.children) && nodeData.children.length > 0) { + for(var i in nodeData.children) { + var node = this.getNodeData(id, nodeData.children[i]); + if(node) return node; + } + } + return null; + }; + + Mindmap.prototype.createDefaultNodeData = function(parentData) { + var data = { + collapesed: false, + id: $.zui.uuid() + '', + parent: parentData.id + }; + if(parentData.type === 'root') { + data.type = 'sub'; + data.text = this.lang.defaultSubName; + } else { + data.type = 'node'; + data.text = this.lang.defaultNodeName; + } + return data; + }; + + Mindmap.prototype.getNode = function(idOrData) { + if(typeof(idOrData) === 'string') { + return this.$desktop.children('[data-id="' + idOrData + '"]'); + } + if(idOrData.id !== undefined) { + return idOrData.ui.element; + } + }; + + Mindmap.prototype.removeNode = function(nodeData) { + this.getNode(nodeData).remove(); + if(nodeData.count > 0) { + for(var i in nodeData.children) { + this.removeNode(nodeData.children[i]); + } + } + }; + + Mindmap.prototype.toggleNode = function(idOrData, collapesed) { + var that = this; + if (idOrData === 'focused') { + idOrData = that.activeNode.id; + } + var nodeData = typeof idOrData === 'string' ? that.getNodeData(idOrData) : idOrData; + if (nodeData && nodeData.type !== 'root') { + if (collapesed === undefined) { + collapesed = !nodeData.collapesed; + } + that.update({data: nodeData, action: 'update', collapesed: collapesed}); + if (collapesed && nodeData.children && nodeData.children.length) { + var deleteChildren = function(children) { + for (var i = 0; i < children.length; ++i) { + var child = children[i]; + that.getNode(child).remove(); + if (child.children && child.children.length) { + deleteChildren(child.children); + } + } + }; + deleteChildren(nodeData.children); + } + return collapesed; + } + }; + + /* update nodeData changes and decide whether to rerender mindmap */ + Mindmap.prototype.update = function(changes, forceShow, forceLoad) { + var changed = false, + options = this.options; + + if($.isPlainObject(changes)) { + changes = [changes]; + } + if(Array.isArray(changes)) { + for(var i in changes) { + var change = changes[i]; + var node = change.data; + if(!node) node = this.getNodeData(change.id); + /* Enable readonly node to collapse sub-nodes. */ + /* if(!node || node.readonly) return; */ + + var action = change.action || 'update'; + if(action === 'remove' || action === 'delete') { + var parent = this.getNodeData(node.parent); + if(parent) { + parent.children.splice(node.index, 1); + + this.removeNode(node); + this.clearNodeStatus(); + + if(node.changed !== 'add') { + if(!Array.isArray(parent.deletions)) { + parent.deletions = [node]; + } else { + parent.deletions.push(node); + } + node.changed = 'delete'; + parent.changed = 'delete children'; + } + + forceLoad = true; + forceShow = true; + changed = true; + } + } else if(action === 'add') { + if(!Array.isArray(node.children)) { + node.children = [change.newData]; + } else { + node.children.push(change.newData); + } + node.count += 1; + change.newData.index = node.count; + change.newData.changed = 'add'; + + forceLoad = true; + forceShow = true; + changed = true; + } else if(action === 'move') { + if(change.newParent !== node.parent) { + var newParent = this.getNodeData(change.newParent); + var parent = this.getNodeData(node.parent); + + if(parent && newParent) { + if(newParent.type === 'root') { + if(node.type === 'node') { + node.color = null; + } + + node.type = 'sub'; + node.subSide = null; + } else { + node.type = 'node'; + node.subSide = newParent.subSide; + } + + parent.children.splice(node.index, 1); + parent.count -= 1; + if(!Array.isArray(newParent.children)) { + newParent.children = [node]; + } else { + newParent.children.push(node); + } + newParent.count += 1; + node.index = newParent.count; + + forceLoad = true; + forceShow = true; + changed = true; + + if(node.changed !== 'add') node.changed = 'move'; + } + } + } else if(action === 'sort') { + if(node.count > 1) { + node.children.sort(change.func); + for(var i in node.children) { + var child = node.children[i]; + if(child.index !== i) { + child.index = i; + if(child.changed !== 'add') child.changed = 'sort'; + forceLoad = true; + forceShow = true; + changed = true; + } + } + } + } else { + if(change.text !== undefined && change.text !== node.text) { + node.text = change.text; + // if(node.count > 0 || node.subSide === 'left') + // { + forceShow = true; + // } + changed = true; + } + + if(change.collapesed !== undefined && change.collapesed !== node.collapesed) { + node.collapesed = change.collapesed; + forceLoad = true; + forceShow = true; + changed = true; + } + + if(change.subSide !== undefined && change.subSide !== node.subSide) { + node.subSide = change.subSide; + forceLoad = true; + forceShow = true; + changed = true; + } + + if(changed) { + if(node.changed !== 'add') node.changed = 'edit'; + } + } + } + } + + if(forceLoad) this.loadNode(); + if(forceShow) this.showNode(); + + if(changed) { + this.callEvent('onChange', { + changes: changes, + data: this.data + }); + } + }; + + + Mindmap.prototype.clearChangeFlag = function(nodeData) { + if(nodeData === undefined) { + nodeData = this.data; + } + + if(nodeData.changed) nodeData.changed = null; + + if(nodeData.children) { + for(var i in nodeData.children) { + this.clearChangeFlag(nodeData.children[i]); + } + } + }; + + Mindmap.prototype.exportData = function() { + var data = $.extend({}, this.data); + this.fixExport(data); + return data; + }; + + Mindmap.prototype.exportJSON = function() { + return JSON.stringify(this.exportData()); + }; + + Mindmap.prototype.exportArray = function(nodeData, ar) { + if(nodeData === undefined) { + nodeData = this.data; + } + + if(ar === undefined) { + ar = []; + } + + var data = $.extend({}, nodeData); + delete data.ui; + delete data.children; + delete data.subSide; + + ar.push(data); + + if(nodeData.children) { + for(var i in nodeData.children) { + this.exportArray(nodeData.children[i], ar); + } + } + + return ar; + }; + + Mindmap.prototype.fixExport = function(data) { + delete data.ui; + if(data.children) { + for(var i in data.children) { + this.fixExport(data.children[i]); + } + } + }; + + Mindmap.prototype.load = function(data) { + this.data = data; + this.render(data); + }; + + Mindmap.prototype.render = function() { + this.loadNode(); + this.showNode(); + // console.log(this.data); + // console.log(JSON.stringify(this.data)); + }; + + Mindmap.prototype.loadNode = function(nodeData, parent, index) { + if(!nodeData) { + nodeData = this.data; + } + + var options = this.options, + $desktop = this.$desktop, + parentId = typeof(parent) == 'object' ? (parent.id ? parent.id : '') : ''; + + if(nodeData.collapesed === undefined) nodeData.collapesed = false; + if(nodeData.data === undefined) nodeData.data = {}; + if(nodeData.type === undefined) nodeData.type = 'node'; + if(nodeData.id === undefined) nodeData.id = $.zui.uuid() + ''; + if(nodeData.readonly === undefined) nodeData.readonly = false; + if(nodeData.ui === undefined) nodeData.ui = {}; + nodeData.parent = parentId; + + var $node = $desktop.children('.mindmap-node[data-id="' + nodeData.id + '"]'); + if($node.length) // updated node existed + { + $node.toggleClass('mindmap-collapesed', !!nodeData.collapesed) + .toggleClass('mindmap-expanded', !nodeData.collapesed) + .toggleClass('mindmap-has-child', !!(nodeData.children && nodeData.children.length)) + .attr('data-type', nodeData.type) + .attr('data-parent', parentId || 'root'); + $node.children('.text').html(nodeData.text); + $node.children('.caption').html(nodeData.caption); + } else // create new node + { + $node = $(options.nodeTeamplate.format({ + type: nodeData.type || 'node', + collapesed: nodeData.collapesed, + caption: nodeData.caption || '', + id: nodeData.id, + parent: parentId || 'root', + text: nodeData.text + })).toggleClass('mindmap-collapesed', !!nodeData.collapesed) + .toggleClass('mindmap-expanded', !nodeData.collapesed).appendTo($desktop) + .toggleClass('mindmap-has-child', !!(nodeData.children && nodeData.children.length)); + + this.bindNodeEvents($node); + + nodeData.ui.element = $node; + } + + //modify by dyh + this.options.afterNodeLoad && this.options.afterNodeLoad({$node:$node,data:nodeData}); + + if(nodeData.type === 'root') { + nodeData.ui.subLeftSide = 0; + nodeData.ui.subRightSide = 0; + nodeData.ui.vLeftSpan = 0; + nodeData.ui.vRightSpan = 0; + } else if(nodeData.type === 'sub') { + var subSide = nodeData.subSide; + if(!subSide) { + if(parent.ui.subRightSide > parent.ui.subLeftSide) { + subSide = 'left'; + } else { + subSide = 'right'; + } + } + if(subSide === 'left') { + parent.ui.subLeftSide++; + } else { + parent.ui.subRightSide++; + } + nodeData.subSide = subSide; + + if(nodeData.color === undefined) { + nodeData.color = { + h: Math.floor(((newColorIndex++) * 43) % 360), + s: this.options.lineSaturation / 100, + l: this.options.lineLightness / 100, + a: this.options.lineOpacity + }; + } + } else { + nodeData.subSide = parent.subSide; + } + + $node.data('origin-text', nodeData.text) + .data('node', nodeData) + .toggleClass('readonly', nodeData.readonly) + .toggleClass('mindmap-side-left', nodeData.subSide === 'left') + .toggleClass('mindmap-side-right', nodeData.subSide === 'right'); + if(nodeData.className) $node.addClass(nodeData.className); + + /* load children nodes */ + var vSpan = 1 + nodeData.count = 0; + if(!nodeData.collapesed && nodeData.children && nodeData.children.length > 0) { + nodeData.ui.topSpanTemp = (nodeData.type === 'root') ? { + left: 0, + right: 0 + } : 0; + var vLeftSpan = 0, + vRightSpan = 0, + lastChild = null; + vSpan = 0; + + nodeData.children.sort(function(nodeA, nodeB) { + return nodeA.index - nodeB.index; + }); + + for(var i in nodeData.children) { + var child = nodeData.children[i]; + if(child.ui === undefined) child.ui = {}; + + if(child.type !== 'sub' && child.color === undefined) { + child.color = nodeData.color; + } + + child.ui.nextBrother = null; + if(lastChild) { + child.ui.prevBrother = lastChild.id; + lastChild.ui.nextBrother = child.id; + } else { + child.ui.prevBrother = null; + } + lastChild = child; + + this.loadNode(child, nodeData, i); + + child.index = nodeData.count++; + if(child.order === undefined) { + child.order = child.index; + } + + vSpan += child.ui.vSpan; + if(child.type === 'sub') { + if(child.subSide === 'left') { + vLeftSpan += child.ui.vSpan; + child.ui.topSpan = nodeData.ui.topSpanTemp.left; + nodeData.ui.topSpanTemp.left += child.ui.vSpan; + } else { + vRightSpan += child.ui.vSpan; + child.ui.topSpan = nodeData.ui.topSpanTemp.right; + nodeData.ui.topSpanTemp.right += child.ui.vSpan; + } + } else { + child.ui.topSpan = nodeData.ui.topSpanTemp; + nodeData.ui.topSpanTemp += child.ui.vSpan; + } + } + + if(nodeData.type === 'root') { + nodeData.ui.vLeftSpan = vLeftSpan; + nodeData.ui.vRightSpan = vRightSpan; + } + } + if(nodeData.type !== 'root') { + nodeData.ui.vSpan = vSpan; + } + + this.dirtyData = false; + + if(nodeData.type === 'root') { + this.callEvent('afterLoad', { + data: nodeData, + $node: $node + }); + } + }; + + /* show on desktop with right position */ + Mindmap.prototype.showNode = function(nodeData, parent, hide) { + if(!nodeData) { + nodeData = this.data; + } + + var options = this.options, + ui = nodeData.ui, + node = nodeData.ui.element; + + ui.width = node.outerWidth(); + ui.height = node.outerHeight(); + + if(nodeData.type === 'root') { + ui.left = 0 - Math.floor(ui.width / 2); + ui.top = 0 - Math.floor(ui.height / 2); + + this.left = 0 - Math.floor(Math.max(ui.width + options.canvasPadding, this.winWidth) / 4); + this.right = 0 - this.left; + this.top = 0 - Math.floor(Math.max(ui.height + options.canvasPadding, this.winHeight) / 4); + this.bottom = 0 - this.top; + } else if($.isPlainObject(ui.dragPos)) { + ui.left = ui.dragPos.left; + ui.top = ui.dragPos.top; + } else { + if(nodeData.type === 'sub') { + var parentVSpan = 0; + if(nodeData.subSide === 'left') { + ui.left = parent.ui.left - options.hSpace - 20 - ui.width; + parentVSpan = parent.ui.vLeftSpan; + } else { + ui.left = parent.ui.left + parent.ui.width + options.hSpace + 20; + parentVSpan = parent.ui.vRightSpan; + } + + var areaHeight = parentVSpan * ui.height + (parentVSpan - 1) * options.vSpace; + var areaX = 0 - Math.floor(areaHeight / 2); + var nodeAreaTop = ui.topSpan * (ui.height) + (ui.topSpan) * options.vSpace; + var nodeAreaHeight = ui.vSpan * ui.height + (ui.vSpan - 1) * options.vSpace; + ui.top = areaX + nodeAreaTop + Math.floor((nodeAreaHeight - ui.height) / 2); + } else { + if(nodeData.subSide === 'left') { + ui.left = parent.ui.left - options.hSpace - ui.width; + } else { + ui.left = parent.ui.left + parent.ui.width + options.hSpace; + } + + var areaHeight = parent.ui.vSpan * ui.height + (parent.ui.vSpan - 1) * options.vSpace; + var areaX = parent.ui.top + Math.floor(parent.ui.height / 2) - Math.floor(areaHeight / 2); + var nodeAreaTop = ui.topSpan * ui.height + (ui.topSpan) * options.vSpace; + var nodeAreaHeight = ui.vSpan * ui.height + (ui.vSpan - 1) * options.vSpace; + + ui.top = areaX + nodeAreaTop + Math.floor((nodeAreaHeight - ui.height) / 2); + } + + if(nodeData.subSide === 'left') { + this.left = Math.min(this.left, ui.left); + } else { + this.right = Math.max(this.right, ui.left + ui.width); + } + this.top = Math.min(this.top, ui.top); + this.bottom = Math.max(this.bottom, ui.top + ui.height); + } + + /* load children nodes */ + if(!nodeData.collapesed && nodeData.children && nodeData.children.length > 0) { + for(var i in nodeData.children) { + this.showNode(nodeData.children[i], nodeData, hide || nodeData.collapesed); + } + } + + if(nodeData.type === 'root') { + var pd = this.options.canvasPadding; + this.left -= pd; + this.top -= pd; + this.right += pd * 2; + this.bottom += pd; + this.width = this.right - this.left; + this.height = this.bottom - this.top; + + this.display(); + this.draw(); + + this.callEvent('afterShow', { + data: nodeData + }) + } + }; + + Mindmap.prototype.display = function(x, y, relative) { + if(typeof x === 'number' && typeof y === 'number') { + if(relative) { + x += this.offsetX; + y += this.offsetY; + } + this.offsetX = x; + this.offsetY = y; + } + + //这里是根节点水平垂直居中对齐 + //this.x = this.centerX + this.left + this.offsetX; + //this.y = this.centerY + this.top + this.offsetY; + + //这里是水平左侧对齐,垂直居中对齐 + this.x = this.left + this.offsetX + this.data.ui.width/2; + this.y = this.centerY + this.top + this.offsetY; + + this.$container.css({ + width: this.width, + height: this.height, + top: this.y, + left: this.x + }); + + var pd = this.options.canvasPadding; + this.$.toggleClass('shadow-left', this.x < 0 - pd) + .toggleClass('shadow-right', this.x + this.width > this.winWidth + pd) + .toggleClass('shadow-top', this.y < 0 - pd) + .toggleClass('shadow-bottom', this.y + this.height > this.winHeight + pd); + }; + + Mindmap.prototype.makeNodeVisble = function($node) { + var node = this.getNodeData($node.data('id')); + if(!node) return; + + var realX = node.ui.left - this.left + this.x, + realY = node.ui.top - this.top + this.y, + pd = this.options.canvasPadding; + + if(realX < 0) { + this.offsetX += 0 - realX; + } else { + var delta = realX + node.ui.width - (this.winWidth + pd); + if(delta > 0) this.offsetX += 0 - delta - 80; + } + + if(realY < 0) { + this.offsetY += 0 - realY; + } else { + var delta = realY + node.ui.height - (this.winWidth + pd); + if(delta > 0) this.offsetY += 0 - delta - 80; + } + + this.display(); + }; + + Mindmap.prototype.clearCanvasArea = function(nodeData, parent) { + this.$canvas[0].getContext("2d").clearRect(0, 0, this.width, this.height); + }; + + Mindmap.prototype.draw = function(nodeData, parent, hide) { + if(!nodeData) { + nodeData = this.data; + } + + if(nodeData.type === 'root') { + this.$canvas.attr({ + width: this.width, + height: this.height + }); + this.clearCanvasArea(); + } + + if(parent) { + if (hide) { + return; + } + var isLeft = (nodeData.subSide === 'left'), + options = this.options; + var ctx = this.$canvas[0].getContext("2d"), + start = { + x: parent.ui.left + (parent.type === 'root' ? (Math.floor(parent.ui.width / 2)) : (isLeft ? 0 : parent.ui.width)), + y: parent.ui.top + Math.floor(parent.ui.height / 2) + }, + end = { + x: nodeData.ui.left + (isLeft ? nodeData.ui.width : 0), + y: nodeData.ui.top + Math.floor(nodeData.ui.height / 2) + }; + var p1 = { + x: start.x + (isLeft ? -1 : 1) * options.lineCurvature, + y: start.y + }, + p2 = { + x: end.x + (isLeft ? 1 : -1) * options.lineCurvature, + y: end.y + }; + start = this.computePosition(start); + end = this.computePosition(end); + p1 = this.computePosition(p1); + p2 = this.computePosition(p2); + + ctx.beginPath(); + ctx.strokeStyle = this.computeColor(nodeData.color, nodeData.ui.canDrop ? 25 : 100); + ctx.lineCap = "round"; + ctx.lineWidth = (nodeData.type === 'sub') ? options.subLineWidth : options.nodeLineWidth; + + ctx.moveTo(start.x, start.y); + ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, end.x, end.y); + ctx.stroke(); + } + + nodeData.ui.element.css(this.computePosition({ + left: nodeData.ui.left, + top: nodeData.ui.top + })); + + if(!nodeData.collapesed && nodeData.children && nodeData.children.length > 0) { + for(var i in nodeData.children) { + this.draw(nodeData.children[i], nodeData, hide || nodeData.collapesed); + } + } + }; + + Mindmap.prototype.bindNodeEvents = function($node) { + var that = this, + data; + $node.on('click', function(event) { + var $target = $(event.target); + if ($target.hasClass('btn-toggle')) { + return that.toggleNode($node.data('id')); + } + that.onNodeClick(event, $node); + }) + .mousedown(function(event) { + event.stopPropagation(); + }); + + if (!that.options.readonly) { + $node.find('.text') + .on('keyup paste blur', function(event) { + that.onNodeTextChanged(event, $node); + }) + // .on('keydown', function(event) {}); + if($node.data('type') !== 'root' && that.options.enableDrag == true) { + $node.droppable({ + container: that.$, + target: '#' + that.id + ' .mindmap-node:not([data-id="' + $node.data('id') + '"])', + before: function(e) { + if(!that.callEvent('beforeDrag', { + node: $node + })) return false; + + if(e.element.hasClass('focus')) { + return false; + } + + data = that.getNodeData(e.element.data('id')); + if(!data) return false; + }, + start: function(e) { + that.callEvent('startDrag', { + node: $node + }); + if(!e.element.hasClass('active')) { + that.clearNodeStatus(); + that.activeNode($node); + } + }, + drag: function(e) { + e.position.left -= that.x; + e.position.top -= that.y; + data.ui.dragPos = that.computePosition(e.position, true); + data.ui.canDrop = e.isIn; + that.showNode(); + }, + beforeDrop: function(e) { + if(e.isIn) { + if(e.target.data('id') == data.parent) return false; + } else { + if(!that.callEvent('beforeSort', { + node: $node, + event: e + })) return; + + var subSide = data.subSide; + if(data.type === 'sub') { + if(data.ui.left < -30) { + subSide = 'left'; + } else if(data.ui.left > 30) { + subSide = 'right'; + } + } + that.update([{ + action: 'sort', + data: that.getNodeData(data.parent), + func: function(a, b) { + return a.ui.top - b.ui.top; + } + }, { + data: data, + subSide: subSide + }]); + + that.callEvent('afterSort', { + node: $node, + event: e + }); + } + }, + drop: function(e) { + if(!that.callEvent('beforeMove', { + node: data, + event: e + })) return; + + that.update({ + action: 'move', + data: data, + newParent: e.target.data('id') + }); + + that.callEvent('afterMove', { + node: data, + event: e + }); + }, + finish: function(e) { + data.ui.dragPos = null; + data.ui.canDrop = false; + that.showNode(); + } + }); + } + } + + this.callEvent('onBindEvents', { + node: $node + }); + } + + Mindmap.prototype.onNodeClick = function(event, $node) { + if($node.find("[contenteditable=true]").length > 0){ + event.stopPropagation(); + return; + } + + if($node.hasClass('active')) { + this.focusNode($node); + } else { + this.clearNodeStatus(); + this.activeNode($node); + } + + this.callEvent('onNodeClick', { + node: $node + }); + + event.stopPropagation(); + }; + + Mindmap.prototype.onNodeTextChanged = function(event, $node) { + var $text = $node.find('.text'); + var text = $text.text(); + if(text !== $node.data('origin-text')) { + if(text === '') { + $text.text(''); + } + $node.data('origin-text', text); + this.update({ + id: $node.data('id'), + text: text + }); + + this.callEvent('onTextChanged', { + node: $node, + text: text + }); + } + if(event.type === 'blur') { + $text.text(text); + } + }; + + Mindmap.prototype.activeNode = function($node) { + if($node === undefined) $node = this.$desktop.children('.mindmap-node[data-type="sub"]').first(); + if(!$node.length) $node = this.$desktop.children('.mindmap-node').first(); + + if(!this.callEvent('beforeNodeActive', { + node: $node + })) return; + + $node.addClass('active'); + this.makeNodeVisble($node); + + this.activedNode = $node; + this.isActive = true; + + this.callEvent('onNodeActive', { + node: $node + }); + }; + + Mindmap.prototype.setEditPosition = function(el,type){ + el.focus(); + if (window.getSelection) { + var range = window.getSelection(); + range.selectAllChildren(el); + if(type=='start') + range.collapseToStart(); + else + range.collapseToEnd(); + } else if (document.selection) { + var range = document.selection.createRange(); + range.moveToElementText(el); + if(type=='start') + range.collapse(); + else + range.collapse(false); + + range.select(); + } + } + + Mindmap.prototype.focusNode = function($node, selectAll) { + if(this.options.readonly) return; + if($node.hasClass('readonly')) { + $.zui.messager.show(this.lang.readonlyTip); + return; + } + if(!$node.hasClass('active')) return; + + if(!this.callEvent('beforeNodeFocus', { + node: $node + })) return; + + var text = $node.addClass('focus').find('.text'); + text.attr('contenteditable', 'true'); + this.makeNodeVisble($node); + text.focus(); + if(selectAll || selectAll === undefined) selectText(text); + + this.setEditPosition(text.get(0),"start"); + + this.isFocus = true; + }; + + Mindmap.prototype.clearActiveNode = function($node) { + if($node === undefined) $node = this.$desktop.children('.mindmap-node.active'); + $node.removeClass('active'); + this.isActive = false; + this.activedNode = null; + }; + + Mindmap.prototype.clearFocusNode = function($node) { + if($node === undefined) $node = this.$desktop.children('.mindmap-node.focus'); + + $node.removeClass('focus').find('.text').attr('contenteditable', 'false').blur(); + this.isFocus = false; + }; + + Mindmap.prototype.bindEvents = function() { + var $this = this.$, + that = this; + + $this.resize(this.initSize.bind(this)).click(this.onDesktopClick.bind(this)); + + this.bindGlobalHotkeys(); + this.$container.draggable({ + before: function(e) { + if(!that.callEvent('beforeMoveCanvas')) return false; + }, + finish: function(e) { + that.display(e.smallOffset.x, e.smallOffset.y, true); + }, + drag: function(e) { + that.display(e.smallOffset.x, e.smallOffset.y, true); + } + }); + }; + + Mindmap.prototype.bindGlobalHotkeys = function() { + var options = this.options; + if(!options.hotkeyEnable) return; + + var that = this, + hotkeys = options.hotkeys; + $(document).on('keydown', null, hotkeys.selectPrev, function() { + if(!that.callEvent('beforeHotkey', { + event: event, + hotkey: hotkeys.selectPrev + })) return; + that.selectNode('prev'); + }).on('keydown', null, hotkeys.selectNext, function() { + if(!that.callEvent('beforeHotkey', { + event: event, + hotkey: hotkeys.selectNext + })) return; + that.selectNode('next'); + }).on('keydown', null, hotkeys.selectLeft, function() { + if(!that.callEvent('beforeHotkey', { + event: event, + hotkey: hotkeys.selectLeft + })) return; + that.selectNode('left'); + }).on('keydown', null, hotkeys.selectRight, function() { + if(!that.callEvent('beforeHotkey', { + event: event, + hotkey: hotkeys.selectRight + })) return; + that.selectNode('right'); + }).on('keydown', null, hotkeys.toggleNode, function(e) { + if(!that.callEvent('beforeHotkey', { + event: event, + hotkey: hotkeys.toggleNode + })) return; + that.toggleNode('focused'); + }).on('keydown', null, hotkeys.centerCanvas, function() { + if(!that.callEvent('beforeHotkey', { + event: event, + hotkey: hotkeys.centerCanvas + })) return; + that.display(0, 0); + }); + + if(!that.options.readonly) { + $(document).on('keydown', null, hotkeys.deleteNode, function() { + if(!that.callEvent('beforeHotkey', { + event: event, + hotkey: hotkeys.deleteNode + })) return; + that.deleteNode(); + if(event.keyCode == 8 && !that.isFocus) { + event.preventDefault(); + } + }).on('keydown', null, hotkeys.addBrother, function() { + if(!that.callEvent('beforeHotkey', { + event: event, + hotkey: hotkeys.addBrother + })) return; + that.addBrotherNode(); + }).on('keydown', null, hotkeys.addChild, function(event) { + if(!that.callEvent('beforeHotkey', { + event: event, + hotkey: hotkeys.addChild + })) return; + that.addChildNode(); + if(event.keyCode == 9) { + event.preventDefault(); + } + }).on('keydown', function() { + if(!that.callEvent('beforeHotkey', { + event: event, + type: 'keydown' + })) return; + if(event.keyCode >= 48 && event.keyCode <= 111 && that.isActive && (!that.isFocus)) { + var node = that.activedNode; + if(node) { + node.find('.text').text(''); + that.focusNode(node); + } + } + }); + } + }; + + Mindmap.prototype.addBrotherNode = function() { + if(this.isActive) { + var node = this.getNodeData(this.activedNode.data('id')); + if(node) { + var parent = node.type === 'root' ? node : this.getNodeData(node.parent); + var newNode = this.createDefaultNodeData(parent); + + if(!this.callEvent('beforeAdd', { + node: parent, + newNode: newNode + })) return; + + this.update({ + action: 'add', + data: parent, + newData: newNode + }); + + this.clearNodeStatus(); + var $newNode = this.getNode(newNode.id); + this.activeNode($newNode); + this.focusNode($newNode, true); + + this.callEvent('afterAdd', { + node: parent, + newNode: newNode + }); + } + } + }; + + Mindmap.prototype.addChildNode = function() { + if(this.isActive) { + var node = this.getNodeData(this.activedNode.data('id')); + if(node) { + var newNode = this.createDefaultNodeData(node); + + if(!this.callEvent('beforeAdd', { + node: node, + newNode: newNode + })) return; + + this.update({ + action: 'add', + data: node, + newData: newNode + }); + + this.clearNodeStatus(); + var $newNode = this.getNode(newNode.id); + this.activeNode($newNode); + this.focusNode($newNode, true); + + this.callEvent('afterAdd', { + node: node, + newNode: newNode + }); + } + } + }; + + Mindmap.prototype.deleteNode = function() { + if(this.isFocus) return; + if(this.isActive) { + var node = this.getNodeData(this.activedNode.data('id')); + if(node) { + if(!this.callEvent('beforeDelete', { + node: node + })) return; + + this.update({ + action: 'remove', + data: node + }); + + this.callEvent('afterDelete', { + node: node + }); + } + } + }; + + /* select node */ + Mindmap.prototype.selectNode = function(type) { + if(this.isFocus) return; + if(!this.isActive) { + this.activeNode(); + return; + } + + var nodeData = null; + + var node = this.getNodeData(this.activedNode.data('id')); + var selectId = null; + + if(node.type === 'root') { + if(type === 'prev' || type === 'left') type = 'left'; + else type = 'right'; + } else if(type === 'left') { + type = (node.subSide == 'left') ? 'child' : 'parent'; + } else if(type === 'right') { + type = (node.subSide == 'right') ? 'child' : 'parent'; + } + + switch(type) { + case 'prev': + selectId = node.ui.prevBrother; + break; + case 'next': + selectId = node.ui.nextBrother; + break; + case 'parent': + selectId = node.parent; + break; + case 'child': + if(node.count > 0 && !node.collapesed) selectId = node.children[0].id; + break; + case 'left': + if(node.count > 0 && !node.collapesed) { + selectId = node.children[0].id; + for(var i in node.children) { + var child = node.children[i]; + if(child.subSide == 'left') { + selectId = child.id; + break; + } + } + } + break; + case 'right': + if(node.count > 0 && !node.collapesed) { + selectId = node.children[0].id; + for(var i in node.children) { + var child = node.children[i]; + if(child.subSide == 'right') { + selectId = child.id; + break; + } + } + } + break; + } + + if(selectId) { + nodeData = this.getNodeData(selectId); + } + + if(nodeData) { + this.clearNodeStatus(); + this.activeNode(nodeData.ui.element); + } + }; + + /* select next brother node */ + Mindmap.prototype.selectNext = function() { + if(this.isFocus) return; + var next = null; + + if(!this.isActive) { + this.activeNode(); + } + + var node = this.getNodeData(this.activedNode.data('id')); + if(node.ui.nextBrother !== null) { + next = this.getNodeData(node.ui.nextBrother); + } + + if(next) { + this.clearNodeStatus(); + this.activeNode(next.ui.element); + } + }; + + /* select next brother node */ + Mindmap.prototype.selectLeft = function() { + if(this.isFocus) return; + var left = null; + + if(!this.isActive) { + this.activeNode(); + } + + var node = this.getNodeData(this.activedNode.data('id')); + if(node.ui.leftBrother !== null) { + left = this.getNodeData(node.ui.leftBrother); + } + + if(left) { + this.clearNodeStatus(); + this.activeNode(left.ui.element); + } + }; + + Mindmap.prototype.onDesktopClick = function() { + var $desktop = this.$desktop; + + /* remove status flag from node elements */ + this.clearNodeStatus(); + }; + + Mindmap.prototype.clearNodeStatus = function() { + this.clearActiveNode(); + this.clearFocusNode(); + }; + + $.fn.mindmap = function(option) { + return this.each(function() { + var $this = $(this); + var data = $this.data('zui.mindmap'); + var options = typeof option == 'object' && option; + + if(!data) $this.data('zui.mindmap', (data = new Mindmap(this, options))); + + if(typeof option == 'string') data[option](); + }); + }; + + $.fn.mindmap.Constructor = Mindmap; +}(jQuery, window, document, Math, undefined)); From 8f76c455eee14ce7f257249ea69314dcffc3f2ff Mon Sep 17 00:00:00 2001 From: zhaoke Date: Fri, 31 Mar 2023 10:14:51 +0800 Subject: [PATCH 02/14] * Add import/export xmind to testcase module. --- db/update18.3.sql | 132 ++ db/zentao.sql | 131 ++ module/testcase/control.php | 478 ++++++ module/testcase/css/showxmindimport.css | 93 + module/testcase/js/showxmindimport.js | 1496 +++++++++++++++++ module/testcase/model.php | 577 ++++++- module/testcase/view/caseheader.html.php | 10 + module/testcase/view/exportxmind.html.php | 108 ++ module/testcase/view/importxmind.html.php | 72 + module/testcase/view/showxmindimport.html.php | 96 ++ 10 files changed, 3168 insertions(+), 25 deletions(-) create mode 100644 module/testcase/css/showxmindimport.css create mode 100644 module/testcase/js/showxmindimport.js create mode 100644 module/testcase/view/exportxmind.html.php create mode 100644 module/testcase/view/importxmind.html.php create mode 100644 module/testcase/view/showxmindimport.html.php diff --git a/db/update18.3.sql b/db/update18.3.sql index 304c292bf4..54a4152fe3 100644 --- a/db/update18.3.sql +++ b/db/update18.3.sql @@ -67,3 +67,135 @@ CREATE TABLE `zt_pivot` ( KEY(`dimension`), KEY(`group`) ) ENGINE = InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE zt_case ADD scene int(11) DEFAULT 0; +ALTER TABLE zt_case ADD sort int(11) DEFAULT 0; + +DROP TABLE IF EXISTS `zt_scene`; +CREATE TABLE `zt_scene` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` mediumint(8) unsigned NOT NULL DEFAULT 0, + `module` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL, + `sort` int(11) unsigned NOT NULL DEFAULT 0, + `openedBy` char(30) NOT NULL DEFAULT '''''', + `openedDate` datetime DEFAULT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `parent` int(11) DEFAULT NULL, + `grade` tinyint(3) DEFAULT NULL, + `path` varchar(1000) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +DROP VIEW IF EXISTS `ztv_scenecase`; +CREATE OR REPLACE VIEW `ztv_scenecase` AS SELECT + `zt_case`.`id` AS `id`, + `zt_case`.`title` AS `title`, + `zt_case`.`project` AS `project`, + `zt_case`.`product` AS `product`, + `zt_case`.`execution` AS `execution`, + `zt_case`.`branch` AS `branch`, + `zt_case`.`lib` AS `lib`, + `zt_case`.`module` AS `module`, + `zt_case`.`story` AS `story`, + `zt_case`.`storyVersion` AS `storyVersion`, + `zt_case`.`precondition` AS `precondition`, + `zt_case`.`keywords` AS `keywords`, + `zt_case`.`pri` AS `pri`, + `zt_case`.`type` AS `type`, + `zt_case`.`auto` AS `auto`, + `zt_case`.`frame` AS `frame`, + `zt_case`.`stage` AS `stage`, + `zt_case`.`howRun` AS `howRun`, + `zt_case`.`scriptedBy` AS `scriptedBy`, + `zt_case`.`scriptedDate` AS `scriptedDate`, + `zt_case`.`scriptStatus` AS `scriptStatus`, + `zt_case`.`scriptLocation` AS `scriptLocation`, + `zt_case`.`status` AS `status`, + `zt_case`.`subStatus` AS `subStatus`, + `zt_case`.`color` AS `color`, + `zt_case`.`frequency` AS `frequency`, + IF(`zt_case`.`sort` = 0, `zt_case`.`id`, `zt_case`.`sort`) AS `sort`, + `zt_case`.`openedBy` AS `openedBy`, + `zt_case`.`openedDate` AS `openedDate`, + `zt_case`.`reviewedBy` AS `reviewedBy`, + `zt_case`.`reviewedDate` AS `reviewedDate`, + `zt_case`.`lastEditedBy` AS `lastEditedBy`, + `zt_case`.`lastEditedDate` AS `lastEditedDate`, + `zt_case`.`version` AS `version`, + `zt_case`.`linkCase` AS `linkCase`, + `zt_case`.`fromBug` AS `fromBug`, + `zt_case`.`fromCaseID` AS `fromCaseID`, + `zt_case`.`fromCaseVersion` AS `fromCaseVersion`, + `zt_case`.`deleted` AS `deleted`, + `zt_case`.`lastRunner` AS `lastRunner`, + `zt_case`.`lastRunDate` AS `lastRunDate`, + `zt_case`.`lastRunResult` AS `lastRunResult`, + `zt_case`.`scene` AS `parent`, + `zt_case`.`scene` AS `scene`, + ifnull(`zt_scene`.`grade` + 1 , 1) AS `grade`, + ifnull( + concat( + `zt_scene`.`path`, + `zt_case`.`id`, + ',' + ), + CONVERT( + concat(',' , `zt_case`.`id` , ',') USING utf8 + ) + ) AS `path`, + 1 AS `isCase` +FROM (`zt_case` LEFT JOIN `zt_scene` ON( `zt_case`.`scene` = `zt_scene`.`id`+100000000)) +UNION + SELECT + `zt_scene`.`id`+ 100000000 AS `id`, + `zt_scene`.`title` AS `title`, + 0 AS `project`, + `zt_scene`.`product` AS `product`, + 0 AS `execution`, + `zt_scene`.`branch` AS `branch`, + 0 AS `lib`, + `zt_scene`.`module` AS `module`, + 0 AS `story`, + 0 AS `storyVersion`, + '' AS `precondition`, + '' AS `keywords`, + 0 AS `pri`, + '' AS `type`, + '' AS `auto`, + '' AS `frame`, + '' AS `stage`, + '' AS `howRun`, + '' AS `scriptedBy`, + '' AS `scriptedDate`, + '' AS `scriptStatus`, + '' AS `scriptLocation`, + '' AS `status`, + '' AS `subStatus`, + '' AS `color`, + '' AS `frequency`, + `zt_scene`.`sort` AS `sort`, + `zt_scene`.`openedBy` AS `openedBy`, + `zt_scene`.`openedDate` AS `openedDate`, + '' AS `reviewedBy`, + '' AS `reviewedDate`, + `zt_scene`.`lastEditedBy` AS `lastEditedBy`, + `zt_scene`.`lastEditedDate` AS `lastEditedDate`, + 0 AS `version`, + '' AS `linkCase`, + 0 AS `fromBug`, + 0 AS `fromCaseID`, + '' AS `fromCaseVersion`, + `zt_scene`.`deleted` AS `deleted`, + '' AS `lastRunner`, + '' AS `lastRunDate`, + '' AS `lastRunResult`, + `zt_scene`.`parent` AS `parent`, + `zt_scene`.`parent` AS `scene`, + `zt_scene`.`grade` AS `grade`, + `zt_scene`.`path` AS `path`, + 2 AS `isCase` + FROM `zt_scene`; \ No newline at end of file diff --git a/db/zentao.sql b/db/zentao.sql index 8d466a639e..5b83c5af1f 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -401,6 +401,8 @@ CREATE TABLE IF NOT EXISTS `zt_case` ( `lastRunner` varchar(30) NOT NULL, `lastRunDate` datetime NOT NULL, `lastRunResult` char(30) NOT NULL, + `scene` int(11) NOT NULL default '0', + `sort` int(11) NOT NULL default '0', PRIMARY KEY (`id`), KEY `product` (`product`), KEY `story` (`story`), @@ -10010,6 +10012,135 @@ CREATE TABLE IF NOT EXISTS `zt_gapanalysis` ( UNIQUE KEY `project_account` (`project`,`account`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- DROP TABLE IF EXISTS `zt_scene`; +CREATE TABLE `zt_scene` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` mediumint(8) unsigned NOT NULL DEFAULT 0, + `module` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL, + `sort` int(11) unsigned NOT NULL DEFAULT 0, + `openedBy` char(30) NOT NULL DEFAULT '''''', + `openedDate` datetime DEFAULT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `parent` int(11) DEFAULT NULL, + `grade` tinyint(3) DEFAULT NULL, + `path` varchar(1000) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- DROP VIEW IF EXISTS `ztv_scenecase`; +CREATE OR REPLACE VIEW `ztv_scenecase` AS SELECT + `zt_case`.`id` AS `id`, + `zt_case`.`title` AS `title`, + `zt_case`.`project` AS `project`, + `zt_case`.`product` AS `product`, + `zt_case`.`execution` AS `execution`, + `zt_case`.`branch` AS `branch`, + `zt_case`.`lib` AS `lib`, + `zt_case`.`module` AS `module`, + `zt_case`.`story` AS `story`, + `zt_case`.`storyVersion` AS `storyVersion`, + `zt_case`.`precondition` AS `precondition`, + `zt_case`.`keywords` AS `keywords`, + `zt_case`.`pri` AS `pri`, + `zt_case`.`type` AS `type`, + `zt_case`.`auto` AS `auto`, + `zt_case`.`frame` AS `frame`, + `zt_case`.`stage` AS `stage`, + `zt_case`.`howRun` AS `howRun`, + `zt_case`.`scriptedBy` AS `scriptedBy`, + `zt_case`.`scriptedDate` AS `scriptedDate`, + `zt_case`.`scriptStatus` AS `scriptStatus`, + `zt_case`.`scriptLocation` AS `scriptLocation`, + `zt_case`.`status` AS `status`, + `zt_case`.`subStatus` AS `subStatus`, + `zt_case`.`color` AS `color`, + `zt_case`.`frequency` AS `frequency`, + IF(`zt_case`.`sort` = 0, `zt_case`.`id`, `zt_case`.`sort`) AS `sort`, + `zt_case`.`openedBy` AS `openedBy`, + `zt_case`.`openedDate` AS `openedDate`, + `zt_case`.`reviewedBy` AS `reviewedBy`, + `zt_case`.`reviewedDate` AS `reviewedDate`, + `zt_case`.`lastEditedBy` AS `lastEditedBy`, + `zt_case`.`lastEditedDate` AS `lastEditedDate`, + `zt_case`.`version` AS `version`, + `zt_case`.`linkCase` AS `linkCase`, + `zt_case`.`fromBug` AS `fromBug`, + `zt_case`.`fromCaseID` AS `fromCaseID`, + `zt_case`.`fromCaseVersion` AS `fromCaseVersion`, + `zt_case`.`deleted` AS `deleted`, + `zt_case`.`lastRunner` AS `lastRunner`, + `zt_case`.`lastRunDate` AS `lastRunDate`, + `zt_case`.`lastRunResult` AS `lastRunResult`, + `zt_case`.`scene` AS `parent`, + `zt_case`.`scene` AS `scene`, + ifnull(`zt_scene`.`grade` + 1 , 1) AS `grade`, + ifnull( + concat( + `zt_scene`.`path`, + `zt_case`.`id`, + ',' + ), + CONVERT( + concat(',' , `zt_case`.`id` , ',') USING utf8 + ) + ) AS `path`, + 1 AS `isCase` +FROM (`zt_case` LEFT JOIN `zt_scene` ON( `zt_case`.`scene` = `zt_scene`.`id`+100000000)) +UNION + SELECT + `zt_scene`.`id`+ 100000000 AS `id`, + `zt_scene`.`title` AS `title`, + 0 AS `project`, + `zt_scene`.`product` AS `product`, + 0 AS `execution`, + `zt_scene`.`branch` AS `branch`, + 0 AS `lib`, + `zt_scene`.`module` AS `module`, + 0 AS `story`, + 0 AS `storyVersion`, + '' AS `precondition`, + '' AS `keywords`, + 0 AS `pri`, + '' AS `type`, + '' AS `auto`, + '' AS `frame`, + '' AS `stage`, + '' AS `howRun`, + '' AS `scriptedBy`, + '' AS `scriptedDate`, + '' AS `scriptStatus`, + '' AS `scriptLocation`, + '' AS `status`, + '' AS `subStatus`, + '' AS `color`, + '' AS `frequency`, + `zt_scene`.`sort` AS `sort`, + `zt_scene`.`openedBy` AS `openedBy`, + `zt_scene`.`openedDate` AS `openedDate`, + '' AS `reviewedBy`, + '' AS `reviewedDate`, + `zt_scene`.`lastEditedBy` AS `lastEditedBy`, + `zt_scene`.`lastEditedDate` AS `lastEditedDate`, + 0 AS `version`, + '' AS `linkCase`, + 0 AS `fromBug`, + 0 AS `fromCaseID`, + '' AS `fromCaseVersion`, + `zt_scene`.`deleted` AS `deleted`, + '' AS `lastRunner`, + '' AS `lastRunDate`, + '' AS `lastRunResult`, + `zt_scene`.`parent` AS `parent`, + `zt_scene`.`parent` AS `scene`, + `zt_scene`.`grade` AS `grade`, + `zt_scene`.`path` AS `path`, + 2 AS `isCase` + FROM `zt_scene`; + REPLACE INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES (1,'activity','assignTo'), (1,'activity','batchCreate'), diff --git a/module/testcase/control.php b/module/testcase/control.php index aa6e9dc377..912cf5d713 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -3030,4 +3030,482 @@ class testcase extends control ->exec(); } } + public function exportXMind($productID, $moduleID, $branch) + { + if($_POST) + { + if (isset($_POST['imodule'])) $imoduleID = $_POST['imodule']; + + $configResult = $this->testcase->saveXmindConfig(); + if($configResult['result'] == 'fail') return print(js::alert($configResult['message'])); + + $context = $this->testcase->getXmindExport($productID, $imoduleID, $branch); + + $xmlDoc = new DOMDocument('1.0', 'UTF-8'); + $xmlDoc->formatOutput = true; + + $versionAttr = $xmlDoc->createAttribute('version'); + $versionAttrValue = $xmlDoc->createTextNode('1.0.1'); + $versionAttr->appendChild($versionAttrValue); + + $mapNode = $xmlDoc->createElement('map'); + $mapNode->appendChild($versionAttr); + $xmlDoc->appendChild($mapNode); + + $productName = ''; + if(count($context['caseList'])) + { + $productName = $context['caseList'][0]->productName; + } + else + { + $product = $this->product->getById($productID); + $productName = $product->name; + } + + $productNode = $xmlDoc->createElement('node'); + $textAttr = $xmlDoc->createAttribute('TEXT'); + $textAttrValue = $xmlDoc->createTextNode($this->toText("$productName", $productID)); + + $textAttr->appendChild($textAttrValue); + $productNode->appendChild($textAttr); + $mapNode->appendChild($productNode); + + $sceneNodes = array(); + $moduleNodes = array(); + + $this->createModuleNode($xmlDoc, $context, $productNode, $moduleNodes); + $this->createSceneNode($xmlDoc, $context, $productNode, $moduleNodes, $sceneNodes); + $this->createTestcaseNode($xmlDoc, $context, $productNode, $moduleNodes, $sceneNodes); + + $xmlStr = $xmlDoc->saveXML(); + $this->fetch('file', 'sendDownHeader', array('fileName' => $productName, 'mm', $xmlStr)); + } + + $tree = $moduleID ? $this->tree->getByID($moduleID) : ''; + $product = $this->product->getById($productID); + $config = $this->testcase->getXmindConfig(); + + $this->view->settings = $config; + $this->view->moduleName = $tree != '' ? $tree->name : '/'; + $this->view->productName = $product->name; + $this->view->moduleID = $moduleID; + $this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch); + + $this->display(); + } + + function createModuleNode($xmlDoc, $context, $productNode, &$moduleNodes) + { + $config = $context['config']; + $moduleList = $context['moduleList']; + + foreach($moduleList as $key => $name) + { + $suffix = $config['module'].':'.$key; + $moduleNode = $this->createNode($xmlDoc, $name, $suffix, array('nodeType' => 'module')); + $productNode->appendChild($moduleNode); + + $moduleNodes[$key] = $moduleNode; + } + } + + function createSceneNode($xmlDoc, $context, $productNode, &$moduleNodes, &$sceneNodes) + { + $sceneMaps = $context['sceneMaps']; + $config = $context['config']; + + $topScenes = $context['topScenes']; + + foreach($topScenes as $scene) + { + $suffix = $config['scene'].':'.$scene->sceneID; + $sceneNode = $this->createNode($xmlDoc, $scene->sceneName, $suffix, array('nodeType' => 'scene')); + + $this->createNextChildScenesNode($scene, $sceneNode, $xmlDoc, $context, $moduleNodes, $sceneNodes); + + if(isset($moduleNodes[$scene->moduleID])) + { + $moduleNode = $moduleNodes[$scene->moduleID]; + $moduleNode->appendChild($sceneNode); + } + else + { + $productNode->appendChild($sceneNode); + } + + $sceneNodes[$scene->sceneID] = $sceneNode; + } + } + + function createNextChildScenesNode($parentScene,$parentNode, $xmlDoc, $context, &$moduleNodes, &$sceneNodes) + { + $sceneMaps = $context['sceneMaps']; + $config = $context['config']; + + foreach($sceneMaps as $key => $scene) + { + if($scene->parentID != $parentScene->sceneID + CHANGEVALUE) continue; + + $suffix = $config['scene'].':'.$scene->sceneID; + $sceneNode = $this->createNode($xmlDoc, $scene->sceneName, $suffix, array('nodeType'=>'scene')); + + $this->createNextChildScenesNode($scene, $sceneNode, $xmlDoc, $context, $moduleNodes, $sceneNodes); + + $parentNode->appendChild($sceneNode); + $sceneNodes[$scene->sceneID] = $sceneNode; + } + } + + function createTestcaseNode($xmlDoc, $context, $productNode, &$moduleNodes, &$sceneNodes) + { + $caseList = $context['caseList']; + + foreach($caseList as $case) + { + if(empty($case->testcaseID)) continue; + + $parentNode = $sceneNodes[$case->sceneID]; + if(!isset($parentNode)) $parentNode = $moduleNodes[$case->moduleID]; + if(!isset($parentNode)) $parentNode = $productNode; + + $this->createTestcaseNodeImpl($case, $xmlDoc, $context, $parentNode); + } + } + + function createTestcaseNodeImpl($case, $xmlDoc, $context, $parentNode) + { + $caseList = $context['caseList']; + $stepList = $context['stepList']; + $config = $context['config']; + $suffix = $config['case'].':'.$case->testcaseID.','.$config['pri'].':'.$case->pri; + $caseNode = $this->createNode($xmlDoc, $case->name, $suffix, array('nodeType'=>'testcase')); + + $parentNode->appendChild($caseNode); + + $topStepList = $this->findTopStepListByCase($case, $stepList); + + foreach($topStepList as $step) + { + $subStepList = $this->findSubStepListByStep($step,$stepList); + + $suffix = count($subStepList) > 0 ? $config['group'] : ''; + $stepNode = $this->createNode($xmlDoc, $step->desc, $suffix, array('nodeType' => 'step')); + $caseNode->appendChild($stepNode); + + if(count($subStepList)) + { + foreach($subStepList as $sub) + { + $subNode = $this->createNode($xmlDoc, $sub->desc, '', array('nodeType'=>'substep')); + $stepNode->appendChild($subNode); + + if(!empty($sub->expect)) + { + $expectNode = $this->createNode($xmlDoc, $sub->expect, '', array('nodeType'=>'expect')); + $subNode->appendChild($expectNode); + } + } + } + + if(count($subStepList) == 0 && !empty($step->expect)) + { + $expectNode = $this->createNode($xmlDoc, $step->expect, '', array('nodeType'=>'expect')); + $stepNode->appendChild($expectNode); + } + } + } + + function findSubStepListByStep($step,$stepList) + { + $subList = array(); + foreach($stepList as $one) + { + if($one->parentID == $step->stepID) + { + $subList[] = $one; + } + } + + return $subList; + } + + public function findTopStepListByCase($case,$stepList) + { + $topList = array(); + foreach($stepList as $step) + { + if($step->parentID == '0' && $step->testcaseID == $case->testcaseID) + { + $topList[] = $step; + } + } + + return $topList; + } + + public function createNode($xmlDoc, $text, $suffix = '', $attrs=array()) + { + $node = $xmlDoc->createElement('node'); + + $textAttr = $xmlDoc->createAttribute('TEXT'); + $textAttrValue = $xmlDoc->createTextNode($this->toText($text,$suffix)); + + $textAttr->appendChild($textAttrValue); + $node->appendChild($textAttr); + + $positionAttr = $xmlDoc->createAttribute("POSITION"); + $positionAttrValue = $xmlDoc->createTextNode('right'); + + $positionAttr->appendChild($positionAttrValue); + $node->appendChild($positionAttr); + + foreach($attrs as $key => $value) + { + $attr = $xmlDoc->createAttribute($key); + $attrValue = $xmlDoc->createTextNode($value); + + $attr->appendChild($attrValue); + $node->appendChild($attr); + } + + return $node; + } + + public function toText($str, $suffix) + { + if(empty($suffix)) return $str; + return $str . '['.$suffix.']'; + } + + public function getXmindConfig() + { + $result = $this->testcase->getXmindConfig(); + $this->send($result); + } + + public function getXmindImport() + { + $folder = $this->session->xmindImport; + $type = $this->session->xmindImportType; + + if($type == 'xml') + { + $xmlPath = "$folder/content.xml"; + $results = $this->testcase->getXmindImport($xmlPath); + + echo $results; + } + else + { + $jsonPath = "$folder/content.json"; + $jsonStr = file_get_contents($jsonPath); + + echo $jsonStr; + } + } + + public function importXMind($productID, $branch) + { + if($_FILES) + { + if($_FILES['file']['size'] == 0) return print(js::alert($this->lang->testcase->errorFileNotEmpty)); + + $configResult = $this->testcase->saveXmindConfig(); + if($configResult['result'] == 'fail') return print(js::alert($configResult['message'])); + + $tmpName = $_FILES['file']['tmp_name']; + $fileName = $_FILES['file']['name']; + $extName = trim(strtolower(pathinfo($fileName, PATHINFO_EXTENSION))); + if($extName != 'xmind') return print(js::alert($this->lang->testcase->errorFileFormat)); + + $newPureName = $this->app->user->id."-xmind"; + $importFolder = $this->app->getTmpRoot() . "import"; + if(!is_dir($importFolder)) mkdir($importFolder, 0755, true); + + $dest = $this->app->getTmpRoot() . "import/".$newPureName.$extName; + if(!move_uploaded_file($tmpName, $dest)) return print(js::alert($this->lang->testcase->errorXmindUpload)); + + $extractFolder = $this->app->getTmpRoot() . "import/".$newPureName; + $this->classFile = $this->app->loadClass('zfile'); + if(is_dir($extractFolder)) $this->classFile->removeDir($extractFolder); + + $this->app->loadClass('pclzip', true); + $zip = new pclzip($dest); + + $files = $zip->listContent(); + $removePath = $files[0]['filename']; + if($zip->extract(PCLZIP_OPT_PATH, $extractFolder, PCLZIP_OPT_REMOVE_PATH, $removePath) == 0) + { + return print(js::alert($this->lang->testcase->errorXmindUpload)); + } + + $this->classFile->removeFile($dest); + + $jsonPath = $extractFolder."/content.json"; + if(file_exists($jsonPath) == true) + { + + $fetchResult = $this->fetchByJSON($extractFolder, $productID, $branch); + } + else + { + $fetchResult = $this->fetchByXML($extractFolder, $productID, $branch); + } + + if($fetchResult['result'] == 'fail') + { + return print(js::alert($fetchResult['message'])); + } + + $this->session->set('xmindImport', $extractFolder); + $this->session->set('xmindImportType', $fetchResult['type']); + + $pId = $fetchResult['pId']; + + return print(js::locate($this->createLink('testcase', 'showXmindImport', "productID=$pId&branch=$branch"), 'parent.parent')); + } + + $config = $this->testcase->getXmindConfig(); + + $this->view->settings = $config; + + $this->display(); + } + + function fetchByXML($extractFolder, $productID, $branch) + { + $filePath = $extractFolder."/content.xml"; + $xmlNode = simplexml_load_file($filePath); + $title = $xmlNode->sheet->topic->title; + if(strlen($title) == 0) + { + return array('result'=>'fail','message'=>$this->lang->testcase->errorXmindUpload); + } + + $pId = $productID; + if($this->endsWith($title,"]") == true) + { + $tmpId = $this->getBetween($title,"[","]"); + if(empty($tmpId) == false) + { + $projectCount = $this->dao->select('count(*) as count') + ->from(TABLE_PRODUCT) + ->where('id') + ->eq((int)$tmpId) + ->andWhere('deleted')->eq('0') + ->fetch('count'); + + if((int)$projectCount == 0) return array('result'=>'fail','message'=>$this->lang->testcase->errorImportBadProduct); + + $pId = $tmpId; + } + } + + return array('result'=>'success','pId'=>$pId, 'type'=>'xml'); + } + + function fetchByJSON($extractFolder, $productID, $branch) + { + $filePath = $extractFolder."/content.json"; + $jsonStr = file_get_contents($filePath); + $jsonDatas = json_decode($jsonStr, true); + $title = $jsonDatas[0]['rootTopic']['title']; + if(strlen($title) == 0) + { + return array('result'=>'fail','message'=>$this->lang->testcase->errorXmindUpload); + } + + $pId = $productID; + if($this->endsWith($title,"]") == true) + { + $tmpId = $this->getBetween($title,"[","]"); + if(empty($tmpId) == false) + { + $projectCount = $this->dao->select('count(*) as count') + ->from(TABLE_PRODUCT) + ->where('id') + ->eq((int)$tmpId) + ->andWhere('deleted')->eq('0') + ->fetch('count'); + + if((int)$projectCount == 0) return array('result'=>'fail','message'=>$this->lang->testcase->errorImportBadProduct); + + $pId = $tmpId; + } + } + + return array('result'=>'success','pId'=>$pId,'type'=>'json'); + } + + function getBetween($kw1, $mark1, $mark2) + { + $kw = $kw1; + $kw = '123' . $kw . '123'; + $st = strripos($kw, $mark1); + $ed = strripos($kw, $mark2); + + if(($st == false || $ed == false) || $st >= $ed) return 0; + + $kw = substr($kw, ($st + 1), ($ed - $st - 1)); + return $kw; + } + + function endsWith($haystack, $needle) + { + return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0; + } + + public function saveXmindConfig() + { + $result = $this->testcase->saveXmindConfig(); + $this->send($result); + } + + public function saveXmindImport() + { + if(!empty($_POST)) + { + $result = $this->testcase->saveXmindImport(); + return $this->send($result); + } + + $this->send(array('result' => 'fail', 'message' => $this->lang->errorSaveXmind)); + } + + public function showXMindImport($productID,$branch) + { + $product = $this->product->getById($productID); + $branches = (isset($product->type) and $product->type != 'normal') ? $this->loadModel('branch')->getPairs($productID, 'active') : array(); + $config = $this->testcase->getXmindConfig(); + + /* Set menu. */ + if($this->app->tab == 'project') $this->loadModel('project')->setMenu(null); + if($this->app->tab == 'qa') $this->testcase->setMenu($this->products, $productID, $branch); + + $jsLng = array(); + $jsLng['caseNotExist'] = $this->lang->testcase->caseNotExist; + $jsLng['saveFail'] = $this->lang->testcase->saveFail; + $jsLng['set2Scene'] = $this->lang->testcase->set2Scene; + $jsLng['set2Testcase'] = $this->lang->testcase->set2Testcase; + $jsLng['clearSetting'] = $this->lang->testcase->clearSetting; + $jsLng['setModule'] = $this->lang->testcase->setModule; + $jsLng['pickModule'] = $this->lang->testcase->pickModule; + $jsLng['clearBefore'] = $this->lang->testcase->clearBefore; + $jsLng['clearAfter'] = $this->lang->testcase->clearAfter; + $jsLng['clearCurrent'] = $this->lang->testcase->clearCurrent; + $jsLng['removeGroup'] = $this->lang->testcase->removeGroup; + $jsLng['set2Group'] = $this->lang->testcase->set2Group; + + $this->view->title = $this->lang->testcase->xmindImport; + $this->view->settings = $config; + $this->view->productID = $productID; + $this->view->branch = $branch; + $this->view->product = $product; + $this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch); + $this->view->gobackLink = $this->createLink('testcase', 'browse', "productID=$productID"); + $this->view->jsLng = $jsLng; + + $this->display(); + } } diff --git a/module/testcase/css/showxmindimport.css b/module/testcase/css/showxmindimport.css new file mode 100644 index 0000000000..717d1e32e2 --- /dev/null +++ b/module/testcase/css/showxmindimport.css @@ -0,0 +1,93 @@ +.mindmap-container { + border: none !important; +} + +.mindmap-node { + display: flex; + align-items: center; +} + +.pri-level,.testcase-pri-root a { + border-width: 2px; + border-style: solid; + border-radius: 50%; + width: 20px; + height: 20px; + text-align:center; + line-height:16px; + display: block; +} + +.pri-1 { + color: #d50000; + border-color: #d50000; +} + +.pri-2 { + color: #ff9800; + border-color: #ff9800; +} + +.pri-3 { + color: #2098ee; + border-color: #2098ee; +} + +.pri-4 { + color: #009688; + border-color: #009688; +} + +.pri-empty { + background-color: lightgray; +} + +.testcase-pri-root a { + margin: 8px; +} + +.testcase-pri-root { + display: flex; + position: absolute; + padding-left: 10px; + padding-right: 10px; + z-index: 20; +} + +.effect{ + z-index: 15; + box-shadow:0px 1px 4px rgba(0,0,0,0.8),0px 0px 40px rgba(0,0,0,0.1) inset; + background-color: white; +} + +.effect a { + width: 24px; + height: 24px; + line-height: 20px; +} + +.effect:before,.effect:after{ + content:""; + border-radius:100px/10px; + box-shadow:0 0px 20px rgba(0,0,0,0.8); +} + +.scene-indicator { + margin-left: 5px; + color: lightgray; +} + +.scene-indicator-yes { + color: darkgreen; +} + +.suffix { + color: darkred; + display: none; +} + +#productName { + height: 32px; + width: 100%; + padding-left: 10px; +} diff --git a/module/testcase/js/showxmindimport.js b/module/testcase/js/showxmindimport.js new file mode 100644 index 0000000000..5420118fcf --- /dev/null +++ b/module/testcase/js/showxmindimport.js @@ -0,0 +1,1496 @@ +var mindmapMgr = undefined; + +$(document).ready(function() +{ + mindmapMgr = new ztmindmap.Wraper({ + product: productID, + branch: branch, + typeKeys:{ + module: userConfig_module, + scene: userConfig_scene, + testcase: userConfig_case, + stepGroup: userConfig_group, + pri: userConfig_pri + } + }); + + $.get(createLink('testcase', 'getXmindImport'),function(res) + { + var data = JSON.parse(res); + if(ztmindmap.isArray(data)) + { + var rootTopic = data[0].rootTopic; + mindmapMgr.initFromJSON(rootTopic); + } + else + { + var sheet = data["xmap-content"]["sheet"]; + if(ztmindmap.isArray(sheet)) sheet = sheet[0]; + mindmapMgr.initFromSheet(sheet); + } + + mindmapMgr.render(); + }); + + $("#xmindmapSave").click(function(e) + { + var data = mindmapMgr.toJson(); + if(data.testcaseList.length == 0) + { + alert(jsLng.caseNotExist); + return; + } + + $.post(createLink('testcase', 'saveXmindImport'),data,function(res) + { + var res = JSON.parse(res); + if(res.result != "success") + { + if(res.message && res.message !== '') + { + alert(res.message); + return; + } + + alert(jsLng.saveFail); + } + else + { + //var params = "productID=" + productID + "&branch=" + branch; + var params = "productID=" + productID; + parent.location = createLink('testcase', 'browse',params); + } + }); + }) +}); + +function setStories(){ + //do nothing +} + +if(window.ztmindmap == undefined) window.ztmindmap = {}; + +ztmindmap.ModuleManager = function(wraper){ this.wraper = wraper;} +ztmindmap.ModuleManager.prototype.clearNodeDisplay = function($node, data){ $node.find(".suffix").hide();}; + +ztmindmap.ModuleManager.prototype.getContextMenuList = function(data) +{ + if(data.$.typeBy.import == true) return []; + + //如果上一个节点时模型,当前节点可以在 ”场景“ 和 ”测试用例间切换“ + var parentNode = this.wraper.findParentNode(data); + if(parentNode.$.type == "module") + { + var menus = []; + + if(data.$.type == "testcase" || data.$.type == undefined) menus.push({action:"addScene", label:jsLng.set2Scene}); + if(data.$.type == "scene" || data.$.type == undefined) menus.push({action:"addTestcase", label:jsLng.set2Testcase}); + if(data.$.type != undefined) menus.push({action:"delType", label:jsLng.clearSetting}); + + return menus; + } + + return []; +}; + +ztmindmap.ModuleManager.prototype.delType = function($node,data) +{ + this.wraper.changeNodeType($node,data,undefined); + this.wraper.clearTypeAfter($node,data); + //this.refreshDisplay(); +} + +ztmindmap.ModuleManager.prototype.addScene = function($node,data) +{ + var moduleData = this.wraper.findParentData(data); + data.$.moduleID = moduleData.$.moduleID; + this.wraper.changeNodeType($node,data,"scene"); + this.wraper.clearTypeAfter($node,data); + //this.refreshDisplay(); +} + +ztmindmap.ModuleManager.prototype.addTestcase = function($node,data) +{ + this.wraper.changeNodeType($node,data,"testcase"); + this.wraper.clearTypeAfter($node,data); + //this.refreshDisplay(); +} + +ztmindmap.ModuleManager.prototype.refreshNodeDisplay = function($node, data) +{ + $node.find(".suffix").hide(); + + if(data.$.moduleID != undefined) + { + $node.find(".suffix").show(); + $node.find(".suffix").find(".content").html(this.wraper.disKeys['module'] + ":" + data.$.moduleID); + } +} + +ztmindmap.ModuleManager.prototype.refreshDisplay = function(){ this.wraper.refreshDisplay();}; + + +if(window.ztmindmap == undefined) window.ztmindmap = {}; + +ztmindmap.SceneManager = function(wraper) +{ + this.wraper = wraper; + this.proprMgr = new ztmindmap.SceneProperyManager(this); + this.proprMgr.init(); +}; + +ztmindmap.SceneManager.prototype.clearNodeDisplay = function($node, data) +{ + $node.find(".suffix").hide(); + $node.find(".scene-indicator").hide(); + + $node.removeAttr("title"); + data.tooltip = undefined; + data.$.moduleID = undefined; + $node.tooltip('destroy'); +}; + +ztmindmap.SceneManager.prototype.refreshNodeDisplay = function($node, data) +{ + $node.find(".scene-indicator").show(); + + $node.find(".scene-indicator").removeClass("scene-indicator-yes"); + + if(data.$.moduleID != undefined) + { + $node.attr("title",data.tooltip); + $node.tooltip(); + + $node.find(".scene-indicator").addClass("scene-indicator-yes"); + } + + if(data.$.sceneID != undefined) + { + $node.find(".suffix").show(); + $node.find(".suffix").find(".content").html(this.wraper.disKeys['scene'] + ":" + data.$.sceneID); + } +}; + +ztmindmap.SceneManager.prototype.getContextMenuList = function(data) +{ + var parentNode = this.wraper.findParentNode(data); + if(parentNode.$.type == "scene" && (data.$.type != "testcase" && data.$.type != "scene")) + return [{action:"addTestcase", label:jsLng.set2Testcase}]; + + if(data.$.typeBy.import == true || data.$.type != "scene") return []; + + //如果有父场景(包括父父节点),当前节点禁止设置所属模块,只能最前面的场景节点设置 + var parents = this.wraper.findParentNodes(data); + for(var i=0;ispan").html(); + + this.data.$.moduleID = moduleID; + this.data.tooltip = moduleName; + this.mgr.refreshNodeDisplay(this.$node,this.data); + + this.mgr.wraper.autoSetSceneModuleAfter(this.$node,this.data); + this.mgr.refreshDisplay(); + + this.$modal.modal("hide"); +}; + + +if(window.ztmindmap == undefined) window.ztmindmap = {}; + +ztmindmap.StepGroupManager = function(wraper){ this.wraper = wraper;}; + +ztmindmap.StepGroupManager.prototype.clearNodeDisplay = function($node, data){ $node.find(".suffix").hide();}; + +ztmindmap.StepGroupManager.prototype.refreshNodeDisplay = function($node, data) +{ + $node.find(".suffix").hide(); + $node.find(".suffix").show(); + $node.find(".suffix").find(".content").html(this.wraper.disKeys['stepGroup']); +}; + + +if(window.ztmindmap == undefined) window.ztmindmap = {}; + +/** + * 设为场景:节点后面的自动推断, 按照最多4级来判断:测试用例 -> 步骤分组 ->步骤 -> 期望结果 + * + * 清除前面场景:不做其他推断 + * 清除后面场景:直接子节点设置为测试用例 + * 清除当前场景:仅在只有一个场景的情况下有这个菜单,同时删除所有用例 + * + * 设为废弃:当前节点及其后续节点全部丢掉 + * 重新启用:当前节点及其后续节点全部启用 + * + * 清除场景: 操作不够明确,无法判断用户意图,这里不提供 + */ + +ztmindmap.nodeTemplate = + "
" + + " " + + " " + + "
" + + "
{text}
" + + "
{caption}
" + + "
" + + "
" + + "
[M:10000]
" + + "
"; + +ztmindmap.defaultTypeKeys = { + module: "M", + scene: "S", + testcase: "C", + stepGroup:"G", + pri: "P" +} + + +ztmindmap.Wraper = function(params) +{ + this.data = undefined; + this.productID = params.product; + this.branch = params.branch; + this.typeKeys = $.extend({},ztmindmap.defaultTypeKeys,params.typeKeys); + this.disKeys = $.extend({},ztmindmap.defaultTypeKeys,params.typeKeys); + + for(var key in this.typeKeys) this.typeKeys[key] = this.typeKeys[key].toLowerCase(); + + this.nodeMgrs = { + scene: new ztmindmap.SceneManager(this), + testcase: new ztmindmap.TestcaseManager(this), + module: new ztmindmap.ModuleManager(this), + stepGroup: new ztmindmap.StepGroupManager(this) + } +}; + +ztmindmap.Wraper.prototype.initFromJSON = function(root) +{ + var rootNode = this.paserXmindNode(root,0); + + this.xmindJson2zendao(root,rootNode,1); + + this.data = rootNode; + + this.inheritImportProps(); +} + +ztmindmap.Wraper.prototype.initFromSheet = function(sheet) +{ + var title = ztmindmap.isObject(sheet.title) ? sheet.title.text : sheet.title; + + var root = {id:sheet.id, text:title,children:[],type:"tmpTop"}; + + this.xmindXml2zendao(sheet.topic, root, 0); + + this.data = root.children[0]; + + this.inheritImportProps(); +}; + +ztmindmap.Wraper.prototype.render = function() +{ + var that = this; + + that.instance = $('#mindmap').mindmap({ + hSpace:80, + showToggleButton: true, + nodeTeamplate: ztmindmap.nodeTemplate, + data: that.data, + enableDrag: false, + subLineWidth: 2, + afterNodeLoad: function(event) + { + var data = event.data; + var $node = event.$node; + + that.refreshNodeDisplay($node,data); + that.register($node,data); + + $node.on("contextmenu",function(e) + { + var contextMenuList = that.getContextMenuList(data); + if(contextMenuList.length == 0) return; + + e.preventDefault(); + + $.zui.ContextMenu.show(contextMenuList,{event:e,onClickItem:function(item) + { + if(item.type == undefined) + that[item.action] && that[item.action]($node,data); + else + that.nodeMgrs[item.type][item.action] && that.nodeMgrs[item.type][item.action]($node, data); + }}); + }); + } + }).data('zui.mindmap'); + + for(var key in this.nodeMgrs) + { + this.nodeMgrs[key]["init"] && this.nodeMgrs[key]["init"](); + } +}; + +ztmindmap.Wraper.prototype.refreshDisplay = function(){ this.instance.showNode();}; + +ztmindmap.Wraper.prototype.getContextMenuList = function(data) +{ + var contextMenuList = []; + + if(data.$.typeBy.import == false) + { + if(data.$.type != "scene") + { + var parents = this.findParentNodes(data); + var canChangeToScene = true; + for(var i=parents.length-1;i>=0;i--) + { + var item = parents[i]; + if(item.$.type == "testcase" || item.$.type == "stepGroup") + { + canChangeToScene = false; + break; + } + } + + if(canChangeToScene == true) contextMenuList.push({ action: "setAsScene", label: jsLng.set2Scene}); + } + + if(data.$.type == "scene") + { + var removeBeforeScene = this.canRemoveSceneBefore(data); + var removeAfterScene = this.canRemoveSceneAfter(data); + + if(removeBeforeScene == true) contextMenuList.push({ action: "clearBeforeScene", label: jsLng.clearBefore }); + + if(removeAfterScene == true) contextMenuList.push({ action: "clearAfterScene", label: jsLng.clearAfter }); + + if(removeBeforeScene == false && removeAfterScene == false && data.$.typeBy.import == false) contextMenuList.push({ action: "clearCurrentScene", label: jsLng.clearCurrent }); + } + } + + for(var key in this.nodeMgrs) + { + var mgr = this.nodeMgrs[key]; + var menuList = mgr.getContextMenuList != undefined ? mgr.getContextMenuList(data) : []; + + if(menuList.length > 0 && contextMenuList.length >0) contextMenuList.push({type:"divider"}); + + for(var i=0; i 测试步骤 -> 预期 + //测试用例 -> 测试步骤分组 -> 测试步骤 -> 预期 + if(length > 4) + { + //本身 + 子元素长度 大于 4 的肯定不是测试用例,这里自动推断为场景 + childData.$.moduleID = data.$.moduleID; + childData.tooltip = data.tooltip; + this.changeNodeType($childNode,childData,"scene"); + + this.autoReasoningAfter($childNode,childData); + } + else + { + //本身 + 子元素长度 小于等于 4 的可能是场景,也可能是测试用例,这里默认推断为测试用例 + //这里需要用户通过界面手动指定 + this.changeNodeType($childNode,childData,"testcase"); + } + } +} + +/** + * 移除给点节点下面的所有场景 和 测试用例 + * @param {Object} $node 给定节点$Dom + * @param {Object} data 给定节点数据 + */ +ztmindmap.Wraper.prototype.autoRemoveSceneAfer = function($node,data) +{ + var children = data.children || []; + for(var i=0; i=2 ? parentList[parentList.length-2] : undefined; +} + +/** + * 查找给数据的父节点,父父数据的集合(包括当前叶子节点) + * @param {Object} purData + */ +ztmindmap.Wraper.prototype.findParentDatas = function(purData) +{ + var results = []; + + function findParentDataPath(data) + { + if(data == purData) return true; + + var children = data.children || []; + for(var child of children) + { + if(findParentDataPath(child) == true) + { + results.unshift(child); + return true; + } + } + + return false; + } + + var flag = findParentDataPath(this.data); + + if(flag == true) results.unshift(this.data); + + return results; +} + +/** + * 查找给数据的父节点,父父节点的集合 + * 倒序,使用的时候注意坑 + * @param {Object} data + */ +ztmindmap.Wraper.prototype.findParentNodes = function(data) +{ + + var results = []; + + function findParentPath(iData) + { + if(iData.id == data.parent) + { + results.push(iData); + return true; + } + + var children = iData.children || []; + for(var child of children) + { + if(findParentPath(child) == true) + { + results.push(child); + return true; + } + } + + return false; + } + + findParentPath(this.instance.data); + + return results; +} + +/** + * 查找给定数据的父节点 + * @param {Object} data 给定数据 + */ +ztmindmap.Wraper.prototype.findParentNode = function(data) +{ + function findParentImpl(iData) + { + if(iData.id == data.parent) return iData; + + var children = iData.children || []; + for(var child of children) + { + var result = findParentImpl(child); + if(result != undefined) return result; + } + + return undefined; + } + + return findParentImpl(this.instance.data); +} + +ztmindmap.Wraper.prototype.refreshNodeDisplay = function($node,data) +{ + var mgr = this.nodeMgrs[data.$.type]; + mgr && mgr.refreshNodeDisplay && mgr.refreshNodeDisplay($node,data); +} + +ztmindmap.Wraper.prototype.register = function($node, data) +{ + var mgr = this.nodeMgrs[data.$.type]; + mgr && mgr.register && mgr.register($node,data); +} + +ztmindmap.Wraper.prototype.changeNodeType = function($node,data,newType) +{ + if(data.$.type == newType) return; + + var oldMgr = this.nodeMgrs[data.$.type]; + var newMgr = this.nodeMgrs[newType]; + + data.$.type = newType; + + if(oldMgr != undefined) + { + oldMgr["clearNodeDisplay"] && oldMgr["clearNodeDisplay"]($node,data); + oldMgr["unRegister"] && oldMgr["unRegister"]($node,data); + } + + if(newMgr != undefined) + { + newMgr["refreshNodeDisplay"] && newMgr["refreshNodeDisplay"]($node,data); + newMgr["register"] && newMgr["register"]($node,data); + } +}; + +ztmindmap.Wraper.prototype.xmindXml2zendao = function(topic, parent,level) +{ + var obj = this.paserXmindNode(topic,level); + parent.children.push(obj); + + var joinChildren = []; + + if(topic.children != undefined) + { + var children = topic.children; + if(ztmindmap.isObject(children)) children = [children]; + + for(child of children) + { + var topicsList = child.topics; + if(ztmindmap.isObject(topicsList)) topicsList = [topicsList]; + + for(var topics of topicsList) + { + var topic = topics.topic; + if(ztmindmap.isObject(topic)) topic = [topic]; + for(var t of topic) + { + joinChildren.push(t); + } + } + } + } + + for(var i=0; i=0;i--) + { + if(dataList[i].$.type == "scene") + { + lastIndex = i; + break; + } + } + + if(lastIndex < 0 || lastIndex == dataList.length-1) return; + + var nextData = dataList[lastIndex+1]; + nextData.$.type = "testcase"; + } + + function checkOtherBetweenScene(dataList) + { + var firstSceneIndex = -1; + var lastSceneIndex = -1; + + for(var i=0;i=0;i--) + { + if(dataList[i].$.type == "scene") + { + lastSceneIndex = i; + break; + } + } + + if(firstSceneIndex == lastSceneIndex) + return; + + for(var i=firstSceneIndex;i<=lastSceneIndex;i++) + { + dataList[i].$.type = "scene"; + } + } + + function checkSceneAfterModuleNode(dataList) + { + var referIndex = -1; + for(var i=0;i=0;i--) + { + if(dataList[i].$.type == "scene" && dataList[i].$.typeBy.import == true) + { + lastSceneIndex = i; + break; + } + } + + if(firstSceneIndex == lastSceneIndex) return; + + for(var i=firstSceneIndex;i<=lastSceneIndex;i++) + { + dataList[i].$.typeBy.import = true; + dataList[i].$.typeBy.create = "auto"; + } + } +} + +ztmindmap.Wraper.prototype.paserXmindNode = function(nodeData, level) +{ + var titleInfo = ztmindmap.splitText(getNodeText(nodeData.title)); + + var obj = { id:nodeData.id, + text: titleInfo.text, + type: level == 0 ? "root": (level == 1 ? "sub" : "node"), + children:[], + subSide:"right", + $:{ + type: undefined, + typeBy: { import:false, create: "auto" } + }}; + + var props = ztmindmap.kv2Obj(titleInfo.suffix); + if(obj.type == "root" && titleInfo.suffix != undefined) + this['setPropsInProduct'] && this['setPropsInProduct'](obj, titleInfo.suffix); + else if(props[this.typeKeys.module] != undefined) + this['setPropsInModule'] && this['setPropsInModule'](obj, props); + else if(props[this.typeKeys.scene] != undefined) + this['setPropsInScene'] && this['setPropsInScene'](obj, props); + else if(props[this.typeKeys.testcase] != undefined) + this['setPropsInTestcase'] && this['setPropsInTestcase'](obj, props); + else if(props[this.typeKeys.stepGroup] != undefined) + { + obj.$.type = "stepGroup"; + obj.$.typeBy.import = true; + } + + return obj; + + function getNodeText(v) + { + return ztmindmap.isObject(v) ? v.text : v; + } +}; + +ztmindmap.Wraper.prototype.setPropsInProduct = function(obj, suffix) +{ + if(ztmindmap.isID(suffix) == false) return; + + obj.readonly = true; + obj.$.productID = suffix * 1; + obj.$.type = "product"; + obj.$.typeBy.import = true; + obj.$.typeBy.create = "auto"; +} + +ztmindmap.Wraper.prototype.setPropsInModule = function(obj, props) +{ + var moduleID = props[this.typeKeys.module]; + + obj.$.type = "module"; + obj.$.typeBy.create = "auto"; + + if(ztmindmap.isID(moduleID) == true) + { + obj.readonly = true; + obj.$.moduleID = moduleID; + obj.$.typeBy.import = true; + } +} + +ztmindmap.Wraper.prototype.setPropsInScene = function(obj, props) +{ + var sceneID = props[this.typeKeys.scene]; + + obj.$.type = "scene"; + obj.$.typeBy.create = "auto"; + + if(ztmindmap.isID(sceneID)) + { + obj.$.sceneID = sceneID; + obj.$.typeBy.import = true; + } +} + +ztmindmap.Wraper.prototype.setPropsInTestcase = function(obj, props) +{ + + var testcaseID = props[this.typeKeys.testcase]; + + obj.$.type = "testcase"; + obj.$.pri = props[this.typeKeys['pri']] || 3; + obj.$.typeBy.create = "auto"; + + if(ztmindmap.isID(testcaseID)) + { + obj.$.testcaseID = testcaseID; + obj.$.typeBy.import = true; + } +} + +ztmindmap.Wraper.prototype.toJson = function() +{ + var that = this; + + var sceneList = []; + var testcaseList = []; + + var rootData = this.instance.data; + + function sceneToJson(data) + { + if(data.$.type == "scene") sceneList.push(that.singleSceneToJson(data)); + if(data.$.type == "testcase") return; + + var children = data.children || []; + for(var child of children) sceneToJson(child); + } + + function testcaseToJson(data) + { + if(data.$.type == "testcase") + testcaseList.push(that.singleTestCaseToJson(data)); + else { + var children = data.children || []; + for(var child of children) + testcaseToJson(child); + } + } + + sceneToJson(rootData); + testcaseToJson(rootData); + + return {sceneList:sceneList, testcaseList:testcaseList}; +}; + +ztmindmap.Wraper.prototype.singleSceneToJson = function(data) +{ + var obj = { + name: data.text, + module: ztmindmap.toNum(data.$.moduleID,undefined), + product: this.productID * 1, + branch: this.branch * 1, + tmpId: data.id, + tmpPId: data.parent, + } + + if(data.$.sceneID != undefined && data.$.sceneID != "") obj.id = data.$.sceneID * 1; + + return obj; +}; + +ztmindmap.Wraper.prototype.singleTestCaseToJson = function(data) +{ + var parentNode = this.findParentNode(data); + + //获取测试用例数据 + var obj = { + pri: ztmindmap.toNum(data.$.pri,3), + name: data.text, + //module: ztmindmap.toNum(scene.$.moduleID,0), + product: this.productID * 1, + branch: this.branch * 1, + tmpId: data.id, + tmpPId: data.parent, + } + + if(parentNode.$.type == "scene") + obj.module = ztmindmap.toNum(parentNode.$.moduleID,0); + else if(parentNode.$.type == "module") + obj.module = ztmindmap.toNum(parentNode.$.moduleID,0); + else + obj.module = 0; + + //设置测试用例的id + if(data.$.testcaseID != undefined && data.$.testcaseID != "") + obj.id = data.$.testcaseID * 1; + + //获取测试用例的步骤 + obj.stepList = []; + var children = data.children || []; + for(var child of children) + { + var step = { + type: "step", + desc: child.text, + tmpId: child.id, + tmpPId: child.parent, + } + + obj.stepList.push(step); + + var maxLng = ztmindmap.getLengthOfNode(child); + var nextChilds = child.children || []; + + //child的子元素: + //1) 有且仅只有一个,这个子元素作为期望结果来处理 + if(maxLng == 2 && child.$.type != "stepGroup") + { + step.expect = nextChilds[0].text; + step.type = "step"; + continue; + } + + //2) 有多个子元素, 当前元素是步骤分组, 这些子元素作为子步骤 + if(maxLng > 2 || child.$.type == "stepGroup") + { + step.type = "group"; + for(var nextChild of nextChilds) + { + var subStep = { + type: "item", + desc: nextChild.text, + tmpId: nextChild.id, + tmpPId: nextChild.parent + }; + + //如果后续只有一个子元素,作为期望的结果来处理,超过一个子元素,丢掉后面的子元素 + var nextNextChild = nextChild.children || []; + if(nextNextChild.length == 1) + subStep.expect = nextNextChild[0].text; + + obj.stepList.push(subStep); + } + } + } + + return obj; +}; + + +if(window.ztmindmap == undefined) window.ztmindmap = {}; + +ztmindmap.TestcaseManager = function(wraper) +{ + this.wraper = wraper; + this.priSelector = undefined; +} + +ztmindmap.TestcaseManager.prototype.init = function() +{ + this.priSelector = new ztmindmap.PriSelector(this); +} + +ztmindmap.TestcaseManager.prototype.register = function($node, data) +{ + let that = this; + + $node.find(".pri-level").click(function(e) + { + that.priSelector.show($node,data); + }); +}; + +ztmindmap.TestcaseManager.prototype.unRegister = function($node, data) +{ + $node.find(".pri-level").unbind(); +}; + +ztmindmap.TestcaseManager.prototype.getContextMenuList = function(data) +{ + var parent = this.wraper.findParentNode(data); + if(parent == undefined || parent.$.type != "testcase") + return []; + + if(data.$.type == "stepGroup") + return [{action:"removeStepGroup", label:jsLng.removeGroup}]; + else + return [{action:"addStepGroup", label:jsLng.set2Group}]; +}; + +ztmindmap.TestcaseManager.prototype.removeStepGroup = function($node,data) +{ + this.wraper.changeNodeType($node,data,undefined); + this.refreshDisplay(); +} + +ztmindmap.TestcaseManager.prototype.addStepGroup = function($node,data) +{ + this.wraper.changeNodeType($node,data,"stepGroup"); + this.refreshDisplay(); +} + +ztmindmap.TestcaseManager.prototype.clearNodeDisplay = function($node, data) +{ + $node.find(".suffix").hide(); + $node.find(".pri-level").hide(); +}; + +ztmindmap.TestcaseManager.prototype.refreshNodeDisplay = function($node, data) +{ + $node.find(".pri-level").show(); + + $node.find(".pri-level").removeClass("pri-1"); + $node.find(".pri-level").removeClass("pri-2"); + $node.find(".pri-level").removeClass("pri-3"); + $node.find(".pri-level").removeClass("pri-4"); + + if(data.$.pri == undefined) + { + $node.find(".pri-level").addClass("pri-empty"); + $node.find(".pri-level").html(""); + } + else + { + $node.find(".pri-level").removeClass("pri-empty"); + $node.find(".pri-level").html(data.$.pri); + $node.find(".pri-level").addClass("pri-" + data.$.pri); + } + + if(data.$.testcaseID != undefined) + { + $node.find(".suffix").show(); + $node.find(".suffix").find(".content").html(this.wraper.disKeys['testcase'] + ":" + data.$.testcaseID); + } +}; + +ztmindmap.TestcaseManager.prototype.refreshDisplay = function() +{ + this.wraper.refreshDisplay(); +} + +ztmindmap.PriSelector = function(mgr) +{ + this.$gradeDom = undefined; + this.$node = undefined; + this.data = undefined; + this.mgr = mgr; + + this.init(); +}; + +ztmindmap.PriSelector.prototype.init = function() +{ + var that = this; + + var htmlStr = ""; + htmlStr += "
"; + htmlStr += " 1"; + htmlStr += " 2"; + htmlStr += " 3"; + htmlStr += " 4"; + htmlStr += "
"; + + var $priDom = $(htmlStr); + + $(".mindmap-container").append($priDom); + + $priDom.find("a").on("click",function(e) + { + var pri = $(e.currentTarget).attr("pri"); + that.data.$.pri = pri; + that.mgr.refreshNodeDisplay(that.$node,that.data); + that.mgr.refreshDisplay(); + }); + + $(document).click(function() + { + that.hide(); + }) + + $priDom.hide(); + that.$priDom = $priDom; +}; + +ztmindmap.PriSelector.prototype.show = function($node, data) +{ + this.$node = $node; + this.data = data; + + var nodeLeft = $node.position().left - 15; + var nodeTop = $node.position().top; + var nodeHeight = $node.height(); + + this.$priDom.css({left:nodeLeft, top:nodeTop + nodeHeight + 10}); + + this.$priDom.show(); +}; + +ztmindmap.PriSelector.prototype.hide = function($node, data) +{ + this.$node = undefined; + this.data = undefined; + + this.$priDom && this.$priDom.hide(); +}; + + +if(window.ztmindmap == undefined) window.ztmindmap = {}; + +ztmindmap.isArray = function(obj) +{ + return Object.prototype.toString.call(obj) === '[object Array]'; +} + +ztmindmap.isObject = function(obj) +{ + return Object.prototype.toString.call(obj) === '[object Object]'; +}; + +ztmindmap.getLengthOfNode = function(data) +{ + var maxLength = 0; + + var children = data.children || []; + for(var i=0; iloadExtension('xmind')->getXmindConfig(); - } - - public function getXmindExport($productID, $moduleID,$branch) - { - return $this->loadExtension('xmind')->getXmindExport($productID, $moduleID,$branch); - } - - public function getXmindImport($xmlPath) - { - return $this->loadExtension('xmind')->getXmindImport($xmlPath); - } - public function istrcut($text, $length) { return (mb_strlen($text, 'utf8') > $length) ? mb_substr($text, 0, $length, 'utf8').'...' : $text; @@ -3710,16 +3695,6 @@ class testcaseModel extends model } } - public function saveXmindConfig() - { - return $this->loadExtension('xmind')->saveXmindConfig(); - } - - public function saveXmindImport() - { - return $this->loadExtension('xmind')->saveXmindImport(); - } - public function updateScene($sceneID) { /* Get original data. */ @@ -3832,4 +3807,556 @@ class testcaseModel extends model return array('status' => 'updated', 'id' => $sceneID); } + + public function getXmindImport($fileName) + { + $xmlNode = simplexml_load_file($fileName); + $testData = $this->xmlToArray($xmlNode); + + return json_encode($testData); + } + + public function saveXmindImport() + { + $this->dao->begin(); + + $sceneIds = array(); + $sceneList = $this->post->sceneList; + foreach($sceneList as $scene) + { + $tmpId = $scene["tmpId"]; + $tmpPId = $scene["tmpPId"]; + + $result = $this->saveScene($scene,$sceneIds); + /* Rollback. */ + if($result["result"] == "fail") + { + $this->dao->rollBack(); + return $result; + } + + $sceneIds[$tmpId] = array("id"=>$result['sceneID'], "tmpPId"=>$tmpPId); + } + + $testcaseList = $this->post->testcaseList; + foreach($testcaseList as $testcase) + { + $tmpId = $testcase["tmpId"]; + $tmpPId = $testcase["tmpPId"]; + + $result = $this->saveTestcase($testcase,$sceneIds); + if($result["result"] == "fail") + { + $this->dao->rollBack(); + return $result; + } + + $sceneIds[$tmpId] = array("id"=>$result['testcaseID'], "tmpPId"=>$tmpPId); + } + + $this->dao->commit(); + + return array("result"=>"success","message"=>1); + } + + public function saveTestcase($testcaseData, $sceneIds) + { + $tmpPId = $testcaseData["tmpPId"]; + $scene = 0; + + if(isset($sceneIds[$tmpPId])) + { + $pScene = $sceneIds[$tmpPId]; + $scene = $pScene["id"] + CHANGEVALUE; + } + + $id = isset($testcaseData["id"]) ? $testcaseData["id"] : -1; + $module = $testcaseData["module"]; + $product = $testcaseData["product"]; + $branch = $testcaseData["branch"]; + $title = $testcaseData["name"]; + $pri = $testcaseData["pri"]; + $now = helper::now(); + $testcaseID = -1; + $version = 1; + + if(!isset($testcaseData["id"])) + { + $testcase = new stdclass(); + $testcase->scene = $scene; + $testcase->module = $module; + $testcase->product = $product; + $testcase->branch = $branch; + $testcase->title = $title; + $testcase->pri = $pri; + $testcase->type = "feature"; + $testcase->status = "normal"; + $testcase->version = $version; + $testcase->openedBy = $this->app->user->account; + $testcase->openedDate = $now; + + $this->dao->insert(TABLE_CASE)->data($testcase)->autoCheck()->exec(); + $testcaseID = $this->dao->lastInsertID(); + + $order = new stdclass(); + $order->sort = $testcaseID; + $this->dao->update(TABLE_CASE)->data($order)->where('id')->eq((int)$testcaseID)->exec(); + } + else + { + $oldCase = $this->dao->select('version,id')->from(TABLE_CASE)->where('id')->eq((int)$id)->fetch(); + + if(isset($oldCase->id)) + { + if(!isset($oldCase->version)) return array('result' => 'fail', 'message' => 'not exist testcase'); + + $version = $oldCase->version + 1; + + $testcase = new stdclass(); + $testcase->id = $id; + $testcase->scene = $scene; + $testcase->module = $module; + $testcase->product = $product; + $testcase->branch = $branch; + $testcase->title = $title; + $testcase->pri = $pri; + $testcase->version = $version; + $testcase->lastEditedBy = $this->app->user->account; + $testcase->lastEditedDate = $now; + + $testcaseID = $id; + $this->dao->update(TABLE_CASE)->data($testcase)->where('id')->eq((int)$id)->exec(); + } + else + { + $testcase = new stdclass(); + $testcase->scene = $scene; + $testcase->module = $module; + $testcase->product = $product; + $testcase->branch = $branch; + $testcase->title = $title; + $testcase->pri = $pri; + $testcase->type = "feature"; + $testcase->status = "normal"; + $testcase->version = $version; + $testcase->openedBy = $this->app->user->account; + $testcase->openedDate = $now; + + $this->dao->insert(TABLE_CASE)->data($testcase)->autoCheck()->exec(); + $testcaseID = $this->dao->lastInsertID(); + + $order = new stdclass(); + $order->sort = $testcaseID; + $this->dao->update(TABLE_CASE)->data($order)->where('id')->eq((int)$testcaseID)->exec(); + } + } + + if($this->dao->isError()) + { + return array('result' => 'fail', 'message' => $this->dao->getError(true)); + } + + $stepList = isset($testcaseData["stepList"]) ? $testcaseData["stepList"] : array(); + if(isset($stepList)) + { + foreach($stepList as $step) + { + $tmpPId = $step["tmpPId"]; + $pObj = isset($sceneIds[$tmpPId]) ? $sceneIds[$tmpPId] : array(); + + $parent = 0; + if(isset($sceneIds[$tmpPId])) $parent = $pObj["id"]; + + $case = $testcaseID; + $type = $step["type"]; + $desc = $step["desc"]; + $expect = isset($step["expect"]) ? $step["expect"] : ''; + + $casestep = new stdclass(); + $casestep->case = $case; + $casestep->version = $version; + $casestep->type = $type; + $casestep->parent = $parent; + $casestep->desc = $desc; + $casestep->expect = $expect; + + $this->dao->insert(TABLE_CASESTEP)->data($casestep)->autoCheck()->exec(); + $casestepID = $this->dao->lastInsertID(); + + if($this->dao->isError()) return array('result' => 'fail', 'message' => $this->dao->getError(true)); + + $sceneIds[$step["tmpId"]] = array("id"=>$casestepID, "tmpPId"=>$tmpPId); + } + } + + return array('result' => 'success', 'message' => 1,'testcaseID'=>$testcaseID); + } + + public function saveScene($sceneData, $sceneIds) + { + $id = isset($sceneData["id"]) ? $sceneData["id"] : -1; + $name = $sceneData["name"]; + $module = isset($sceneData["module"]) ? $sceneData["module"] : 0; + $product = $sceneData["product"]; + $branch = $sceneData["branch"]; + $now = helper::now(); + $sceneID = -1; + + if(!isset($sceneData["id"])) + { + $scene = new stdclass(); + $scene->title = $name; + $scene->module = $module; + $scene->product = $product; + $scene->branch = $branch; + $scene->openedBy = $this->app->user->account; + $scene->openedDate = $now; + + $this->dao->insert(TABLE_SCENE)->data($scene)->autoCheck()->exec(); + $sceneID = $this->dao->lastInsertID(); + + $order = new stdclass(); + $order->sort = ($sceneID + CHANGEVALUE); + + $this->dao->update(TABLE_SCENE)->data($order)->where('id')->eq((int)$sceneID)->exec(); + } + else + { + $scene = new stdclass(); + $scene->title = $name; + $scene->module = $module; + $scene->product = $product; + $scene->branch = $branch; + $scene->lastEditedBy = $this->app->user->account; + $scene->lastEditedDate = $now; + + $sceneID = $id; + $affectedRows = $this->dao->update(TABLE_SCENE)->data($scene)->where('id')->eq((int)$id)->exec(); + if(empty($affectedRows)) return array('result' => 'fail', 'message' => sprintf($this->lang->testcase->errorSceneNotExist, $id)); + } + + if($this->dao->isError()) return array('result' => 'fail', 'message' => $this->dao->getError(true)); + + $tmpPId = $sceneData["tmpPId"]; + $pScene = isset($sceneIds[$tmpPId]) ? $sceneIds[$tmpPId] : array(); + $parent = 0; + $grade = 1; + $path = ",".($sceneID + CHANGEVALUE).","; + + if(isset($sceneIds[$tmpPId])) + { + $parent = $pScene["id"]; + $parentScene = $this->dao->findById((int)$parent)->from(TABLE_SCENE)->fetch(); + $path = $parentScene->path . ($sceneID + CHANGEVALUE).","; + $grade = $parentScene->grade + 1; + } + + if($parent != 0) $parent = $parent + CHANGEVALUE; + + $this->dao->update(TABLE_SCENE) + ->set('parent')->eq($parent) + ->set('path')->eq($path) + ->set('grade')->eq($grade) + ->where('id')->eq($sceneID) + ->limit(1) + ->exec(); + + if($this->dao->isError()) return array('result' => 'fail', 'message' => $this->dao->getError(true)); + + return array('result' => 'success', 'message' => 1,"sceneID"=>$sceneID); + } + + public function getXmindExport($productID, $moduleID, $branch) + { + $caseList = $this->getCaseByProductAndModule($productID, $moduleID); + $stepList = $this->getStepByProductAndModule($productID, $moduleID); + $sceneInfo = $this->getSceneByProductAndModule($productID, $moduleID); + $moduleList = $this->getModuleByProductAndModel($productID, $moduleID, $branch); + + $config = $this->getXmindConfig(); + + return array( + 'caseList' =>$caseList, + 'stepList' =>$stepList, + 'sceneMaps' =>$sceneInfo['sceneMaps'], + 'topScenes' =>$sceneInfo['topScenes'], + 'moduleList'=>$moduleList, + 'config' =>$config + ); + } + + function getModuleByProductAndModel($productID, $moduleID, $branch) + { + $moduleList = array(); + + if($moduleID > 0) + { + $module = $this->loadModel('tree')->getByID($moduleID); + + $moduleList[$module->id] = $module->name; + } + else + { + $moduleList = $this->loadModel('tree')->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, ($branch === 'all' or !isset($branches[$branch])) ? 0 : $branch); + + unset($moduleList['0']); + } + + return $moduleList; + } + + function getCaseByProductAndModule($productID, $moduleID) + { + $fields = "t2.id as productID," + . "t2.`name` as productName," + . "t3.id as moduleID," + . "t3.`name` as moduleName," + . "t4.id as sceneID," + . "t4.title as sceneName," + . "t1.id as testcaseID," + . "t1.title as `name`," + . "t1.pri"; + + $caseList = $this->dao->select($fields)->from(TABLE_CASE)->alias('t1') + ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id') + ->leftJoin(TABLE_MODULE)->alias('t3')->on('t1.module = t3.id') + ->leftJoin(TABLE_SCENE)->alias('t4')->on('t1.scene = t4.id+' . CHANGEVALUE) + ->where('t1.deleted')->eq(0) + ->andWhere('t1.product')->eq($productID) + ->beginIF($moduleID > 0)->andWhere('t1.module')->eq($moduleID)->fi() + ->fetchAll(); + + return $caseList; + } + + function getStepByProductAndModule($productID, $moduleID) + { + $fields = "t1.id as testcaseID," + . "t2.id as stepID," + . "t2.type," + . "t2.parent as parentID," + . "t2.`desc`," + . "t2.expect"; + + $stepList = $this->dao->select($fields)->from(TABLE_CASE)->alias('t1') + ->leftJoin(TABLE_CASESTEP)->alias('t2')->on('t1.id = t2.`case` and t1.version = t2.version') + ->where('t1.deleted')->eq(0) + ->andWhere('t1.product')->eq($productID) + ->andWhere('t2.id')->ne('null') + ->beginIF($moduleID > 0)->andWhere('t1.module')->eq($moduleID)->fi() + ->fetchAll(); + + return $stepList; + } + + function getSceneByProductAndModule($productID, $moduleID) + { + $sceneList = $this->dao->select('id as sceneID, title as sceneName, path, parent as parentID, product as productID, module as moduleID') + ->from(TABLE_SCENE) + ->where('deleted')->eq(0) + ->andWhere('product')->eq($productID) + ->beginIF($moduleID > 0)->andWhere('module')->eq($moduleID)->fi() + ->fetchAll(); + + $sceneMaps = array(); + $topScenes = array(); + foreach($sceneList as $one) + { + if($one->parentID == 0) $topScenes[] = $one; + + $sceneMaps[$one->sceneID] = $one; + } + + return array('sceneMaps'=>$sceneMaps,'topScenes'=>$topScenes); + } + + function checkConfigValue($str) + { + return preg_match("/^[a-zA-Z]{1,10}$/",$str); + } + + function saveXmindConfig() + { + $configList = array(); + + $module = $this->post->module; + if(isset($module) && !empty($module)) + { + if(!$this->checkConfigValue($module)) return array('result' => 'fail', 'message' => '模块特征字符串只能是1-10个字母'); + $configList[] = array('key'=>'module','value'=>$module); + } + + $scene = $this->post->scene; + if(isset($scene) && !empty($scene)) + { + if(!$this->checkConfigValue($scene)) return array('result' => 'fail', 'message' => '场景特征字符串只能是1-10个字母'); + $configList[] = array('key'=>'scene','value'=>$scene); + } + + $case = $this->post->case; + if(isset($case) && !empty($case)) + { + if(!$this->checkConfigValue($case)) return array('result' => 'fail', 'message' => '测试用例特征字符串只能是1-10个字母'); + $configList[] = array('key'=>'case','value'=>$case); + } + + $pri = $this->post->pri; + if(isset($pri) && !empty($pri)) + { + if(!$this->checkConfigValue($pri)) return array('result' => 'fail', 'message' => '优先级特征字符串只能是1-10个字母'); + $configList[] = array('key'=>'pri','value'=>$pri); + } + + $group = $this->post->group; + if(isset($group) && !empty($group)) + { + if(!$this->checkConfigValue($group)) return array('result' => 'fail', 'message' => '步骤分组特征字符串只能是1-10个字母'); + $configList[] = array('key'=>'group','value'=>$group); + } + + $map = array(); + $map[strtolower($module)] = true; + $map[strtolower($scene)] = true; + $map[strtolower($case)] = true; + $map[strtolower($pri)] = true; + $map[strtolower($group)] = true; + + if(count($map) < 5) return array('result' => 'fail', 'message' => '特征字符串不能重复'); + + $this->dao->begin(); + + $this->dao->delete()->from(TABLE_CONFIG) + ->where('owner')->eq($this->app->user->account) + ->andWhere('module')->eq('testcase') + ->andWhere('section')->eq('xmind') + ->exec(); + + foreach($configList as $one) + { + $config = new stdclass(); + + $config->module = 'testcase'; + $config->section = 'xmind'; + $config->key = $one['key']; + $config->value = $one['value']; + $config->owner = $this->app->user->account; + + $this->dao->insert(TABLE_CONFIG)->data($config)->autoCheck()->exec(); + + if($this->dao->isError()) + { + $this->dao->rollBack(); + return array('result' => 'fail', 'message' => $this->dao->getError(true)); + } + } + + $this->dao->commit(); + + return array("result" => "success", "message" => 1); + } + + function getXmindConfig() + { + $configItems = $this->dao->select("`key`,value")->from(TABLE_CONFIG) + ->where('owner')->eq($this->app->user->account) + ->andWhere('module')->eq('testcase') + ->andWhere('section')->eq('xmind') + ->fetchAll(); + + $config = array(); + foreach($configItems as $item) $config[$item -> key] = $item -> value; + + if(!isset($config['module'])) $config['module'] = 'M'; + if(!isset($config['scene'])) $config['scene'] = 'S'; + if(!isset($config['case'])) $config['case'] = 'C'; + if(!isset($config['pri'])) $config['pri'] = 'P'; + if(!isset($config['group'])) $config['group'] = 'G'; + + return $config; + } + + function xmlToArray($xml, $options = array()) + { + $defaults = array( + 'namespaceRecursive' => false, // Get XML doc namespaces recursively + 'removeNamespace' => true, // Remove namespace from resulting keys + 'namespaceSeparator' => ':', // Change separator to something other than a colon + 'attributePrefix' => '', // Distinguish between attributes and nodes with the same name + 'alwaysArray' => [], // Array of XML tag names which should always become arrays + 'autoArray' => true, // Create arrays for tags which appear more than once + 'textContent' => 'text', // Key used for the text content of elements + 'autoText' => true, // Skip textContent key if node has no attributes or child nodes + 'keySearch' => false, // (Optional) search and replace on tag and attribute names + 'keyReplace' => false, // (Optional) replace values for above search values + ); + + $options = array_merge($defaults, $options); + $namespaces = $xml->getDocNamespaces($options['namespaceRecursive']); + $namespaces[''] = null; // Add empty base namespace + + /* Get attributes from all namespaces. */ + $attributesArray = []; + foreach($namespaces as $prefix => $namespace) + { + if($options['removeNamespace']) $prefix = ''; + + foreach($xml->attributes($namespace) as $attributeName => $attribute) + { + // (Optional) replace characters in attribute name + if($options['keySearch']) $attributeName = str_replace($options['keySearch'], $options['keyReplace'], $attributeName); + + $attributeKey = $options['attributePrefix'] . ($prefix ? $prefix . $options['namespaceSeparator'] : '') . $attributeName; + $attributesArray[$attributeKey] = (string) $attribute; + } + } + + // Get child nodes from all namespaces + $tagsArray = []; + foreach($namespaces as $prefix => $namespace) + { + if($options['removeNamespace']) $prefix = ''; + + foreach($xml->children($namespace) as $childXml) + { + // Recurse into child nodes + $childArray = $this->xmlToArray($childXml, $options); + $childTagName = key($childArray); + $childProperties = current($childArray); + + // Replace characters in tag name + if($options['keySearch']) $childTagName = str_replace($options['keySearch'], $options['keyReplace'], $childTagName); + + // Add namespace prefix, if any + if($prefix) $childTagName = $prefix . $options['namespaceSeparator'] . $childTagName; + + if(!isset($tagsArray[$childTagName])) + { + // Only entry with this key + // Test if tags of this type should always be arrays, no matter the element count + $tagsArray[$childTagName] = in_array($childTagName, $options['alwaysArray'], true) || !$options['autoArray'] ? [$childProperties] : $childProperties; + } + elseif(is_array($tagsArray[$childTagName]) && array_keys($tagsArray[$childTagName]) === range(0, count($tagsArray[$childTagName]) - 1)) + { + // Key already exists and is integer indexed array + $tagsArray[$childTagName][] = $childProperties; + } + else + { + // Key exists so convert to integer indexed array with previous value in position 0 + $tagsArray[$childTagName] = [$tagsArray[$childTagName], $childProperties]; + } + } + } + + // Get text content of node + $textContentArray = array(); + $plainText = trim((string) $xml); + if($plainText !== '') $textContentArray[$options['textContent']] = $plainText; + + // Stick it all together + $propertiesArray = !$options['autoText'] || $attributesArray || $tagsArray || $plainText === '' ? array_merge($attributesArray, $tagsArray, $textContentArray) : $plainText; + + // Return node as array + return array($xml->getName() => $propertiesArray); + } } diff --git a/module/testcase/view/caseheader.html.php b/module/testcase/view/caseheader.html.php index 5c31522a5b..4a7d8525c9 100644 --- a/module/testcase/view/caseheader.html.php +++ b/module/testcase/view/caseheader.html.php @@ -224,6 +224,11 @@ $misc = common::hasPriv('testcase', 'exportTemplate') ? "class='export'" : "class=disabled"; $link = common::hasPriv('testcase', 'exportTemplate') ? $this->createLink('testcase', 'exportTemplate', "productID=$productID") : '#'; echo "
  • " . html::a($link, $lang->testcase->exportTemplate, '', $misc . "data-app={$this->app->tab} data-width='65%'") . "
  • "; + + $class = common::hasPriv('testcase', 'exportXmind') ? '' : "class=disabled"; + $misc = common::hasPriv('testcase', 'exportXmind') ? "class='export'" : "class=disabled"; + $link = common::hasPriv('testcase', 'exportXmind') ? $this->createLink('testcase', 'exportXmind', "productID=$productID&moduleID=$moduleID&branch=$branch") : '#'; + echo "
  • " . html::a($link, $lang->testcase->xmindExport, '', $misc . "data-app={$this->app->tab}") . "
  • "; ?>
    @@ -238,6 +243,11 @@ $link = $this->createLink('testcase', 'importFromLib', "productID=$productID&branch=$branch&libID=0&orderBy=id_desc&browseType=&queryID=10&recTotal=0&recPerPage=20&pageID=1&projectID=$projectID"); if(common::hasPriv('testcase', 'importFromLib')) echo "
  • " . html::a($link, $lang->testcase->importFromLib, '', "data-app={$app->tab}") . "
  • "; + + $class = common::hasPriv('testcase', 'importXmind') ? '' : "class=disabled"; + $misc = common::hasPriv('testcase', 'importXmind') ? "class='export'" : "class=disabled"; + $link = common::hasPriv('testcase', 'importXmind') ? $this->createLink('testcase', 'importXmind', "productID=$productID&branch=$branch") : '#'; + echo "
  • " . html::a($link, $lang->testcase->xmindImport, '', $misc . "data-app={$this->app->tab}") . "
  • "; ?>
    diff --git a/module/testcase/view/exportxmind.html.php b/module/testcase/view/exportxmind.html.php new file mode 100644 index 0000000000..0084039e1a --- /dev/null +++ b/module/testcase/view/exportxmind.html.php @@ -0,0 +1,108 @@ +getModuleRoot() . 'common/view/header.lite.html.php';?> + + +
    +
    +

    testcase->xmindExport;?>

    +
    +
    + + + + + + + + + + + + + +
    + + testcase->xmindExportSetting;?> + +
    +
    +
    + testcase->settingModule;?> + +
    +
    +
    +
    + testcase->settingScene;?> + +
    +
    +
    +
    +
    +
    + testcase->settingCase;?> + +
    +
    +
    +
    + testcase->settingPri;?> + +
    +
    +
    +
    + testcase->settingGroup;?> + +
    +
    +
    +
    +
    +
    +
    +
    + testcase->product;?> + +
    +
    +
    +
    +
    +
    +
    +
    + testcase->module;?> + +
    +
    +
    +
    + +
    +
    +
    +getModuleRoot() . 'common/view/footer.lite.html.php';?> diff --git a/module/testcase/view/importxmind.html.php b/module/testcase/view/importxmind.html.php new file mode 100644 index 0000000000..0f43249f45 --- /dev/null +++ b/module/testcase/view/importxmind.html.php @@ -0,0 +1,72 @@ +getModuleRoot() . 'common/view/header.lite.html.php';?> + +
    +
    +
    +
    +

    testcase->xmindImport;?>

    +
    +
    + + + + + + + + + +
    + + testcase->xmindImportSetting;?> + +
    +
    +
    + testcase->settingModule;?> + +
    +
    +
    +
    + testcase->settingScene;?> + +
    +
    +
    +
    +
    +
    + testcase->settingCase;?> + +
    +
    +
    +
    + testcase->settingPri;?> + +
    +
    +
    +
    + testcase->settingGroup;?> + +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +getModuleRoot() . 'common/view/footer.lite.html.php';?> diff --git a/module/testcase/view/showxmindimport.html.php b/module/testcase/view/showxmindimport.html.php new file mode 100644 index 0000000000..fa4096e05f --- /dev/null +++ b/module/testcase/view/showxmindimport.html.php @@ -0,0 +1,96 @@ + + * @package case + * @version $Id: create.html.php 4904 2013-06-26 05:37:45Z wyd621@gmail.com $ + * @link http://www.zentao.net + */ +?> +getModuleRoot() . 'common/view/header.html.php';?> + + + + + + + + + + + +
    +
    +
    +

    testcase->xmindImportEdit;?>(name;?>)

    +
    + +
    +
    +
    + + + + + + + + + +
    +
    +
    + + goback, '', 'class="btn btn-wide"') : html::backButton();?> +
    +
    +
    +
    + + +getModuleRoot() . 'common/view/footer.html.php';?> From 3337ad0cd1c21d1a15cddcbae6beb5ff021a6268 Mon Sep 17 00:00:00 2001 From: zhaoke Date: Mon, 3 Apr 2023 13:15:52 +0800 Subject: [PATCH 03/14] + Add privilege menu. --- module/group/lang/resource.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index fb5efd4a1f..6ce645959e 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -1088,6 +1088,18 @@ $lang->resource->testcase->batchConfirmStoryChange = 'batchConfirmStoryChange'; $lang->resource->testcase->importToLib = 'importToLib'; $lang->resource->testcase->automation = 'automation'; +$lang->resource->testcase->createScene = 'createScene'; +$lang->resource->testcase->editScene = 'editScene'; +$lang->resource->testcase->deleteScene = 'deleteScene'; +$lang->resource->testcase->changeScene = 'changeScene'; +$lang->resource->testcase->batchChangeScene = 'batchChangeScene'; +$lang->resource->testcase->updateOrder = 'updateOrder'; + +$lang->resource->testcase->importXmind = 'importXmind'; +$lang->resource->testcase->exportXmind = 'exportXmind'; +$lang->resource->testcase->getXmindImport = 'getXmindImport'; +$lang->resource->testcase->showXMindImport = 'showXMindImport'; + $lang->testcase->methodOrder[0] = 'index'; $lang->testcase->methodOrder[5] = 'browse'; $lang->testcase->methodOrder[10] = 'groupCase'; @@ -1120,6 +1132,17 @@ $lang->testcase->methodOrder[140] = 'importToLib'; $lang->testcase->methodOrder[145] = 'automation'; $lang->testcase->methodOrder[150] = 'showScript'; +$lang->testcase->methodOrder[155] = 'createScene'; +$lang->testcase->methodOrder[160] = 'editScene'; +$lang->testcase->methodOrder[165] = 'deleteScene'; +$lang->testcase->methodOrder[170] = 'changeScene'; +$lang->testcase->methodOrder[175] = 'batchChangeScene'; +$lang->testcase->methodOrder[180] = 'updateOrder'; +$lang->testcase->methodOrder[185] = 'importXmind'; +$lang->testcase->methodOrder[190] = 'getXmindImport'; +$lang->testcase->methodOrder[195] = 'showXMindImport'; +$lang->testcase->methodOrder[200] = 'exportXmind'; + /* Test task. */ $lang->resource->testtask = new stdclass(); $lang->resource->testtask->create = 'create'; From 94e840b5cea4246cf5d4ade7663255b0c2f413e1 Mon Sep 17 00:00:00 2001 From: zhaoke Date: Mon, 3 Apr 2023 14:36:37 +0800 Subject: [PATCH 04/14] * Update testcase lang. --- module/testcase/lang/de.php | 7 ++--- module/testcase/lang/en.php | 7 ++--- module/testcase/lang/fr.php | 7 ++--- module/testcase/lang/vi.php | 57 +++++++++++++++++++++++++--------- module/testcase/lang/zh-cn.php | 2 -- module/testcase/lang/zh-tw.php | 36 +++++++++++++++------ 6 files changed, 79 insertions(+), 37 deletions(-) diff --git a/module/testcase/lang/de.php b/module/testcase/lang/de.php index 0ed86183c8..7bfb91eb7d 100644 --- a/module/testcase/lang/de.php +++ b/module/testcase/lang/de.php @@ -267,13 +267,11 @@ $lang->testcase->featureBar['browse']['zerocase'] = 'Zero Case Story'; $lang->testcase->featureBar['browse']['suite'] = 'Suite'; $lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; - $lang->testcase->importXmind = "Import XMIND"; $lang->testcase->exportXmind = "Export XMIND"; $lang->testcase->getXmindImport = "Get Mindmap"; $lang->testcase->showXMindImport = "Display Mindmap"; - $lang->testcase->xmindImport = "Imort XMIND"; $lang->testcase->xmindExport = "Export XMIND"; $lang->testcase->xmindImportEdit = "XMIND Edit"; @@ -315,7 +313,7 @@ $lang->testcase->createScene = "Add Scene"; $lang->testcase->changeScene = "Drag to change the scene which it belongs"; $lang->testcase->batchChangeScene = "Batch change scene"; $lang->testcase->updateOrder = "Drag Sort"; -$lang->testcase->errDiffProduct = "Different product"; +$lang->testcase->differentProduct = "Different product"; $lang->testcase->newScene = "Add Scene"; $lang->testcase->sceneTitle = 'Scene Title'; @@ -328,7 +326,6 @@ $lang->testcase->editScene = 'Edit Scene'; $lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; $lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; $lang->testcase->sceneb = "Scene"; -$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; $lang->testcase->onlyScene = 'Only Scene'; $lang->testcase->iScene = 'Scene'; $lang->testcase->generalTitle = 'Title'; @@ -343,5 +340,7 @@ $lang->testcase->dragModalMessage = '

    There are two possible situations fo $lang->testcase->dragModalChangeScene = 'Change its scene'; $lang->testcase->dragModalChangeOrder = 'Reorder'; +$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; + $lang->scene = new stdclass(); $lang->scene->title = 'Scene Title'; \ No newline at end of file diff --git a/module/testcase/lang/en.php b/module/testcase/lang/en.php index fc9c6b5f81..b2d3b6f32d 100644 --- a/module/testcase/lang/en.php +++ b/module/testcase/lang/en.php @@ -267,13 +267,11 @@ $lang->testcase->featureBar['browse']['zerocase'] = 'Zero Case Story'; $lang->testcase->featureBar['browse']['suite'] = 'Suite'; $lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; - $lang->testcase->importXmind = "Import XMIND"; $lang->testcase->exportXmind = "Export XMIND"; $lang->testcase->getXmindImport = "Get Mindmap"; $lang->testcase->showXMindImport = "Display Mindmap"; - $lang->testcase->xmindImport = "Imort XMIND"; $lang->testcase->xmindExport = "Export XMIND"; $lang->testcase->xmindImportEdit = "XMIND Edit"; @@ -315,7 +313,7 @@ $lang->testcase->createScene = "Add Scene"; $lang->testcase->changeScene = "Drag to change the scene which it belongs"; $lang->testcase->batchChangeScene = "Batch change scene"; $lang->testcase->updateOrder = "Drag Sort"; -$lang->testcase->errDiffProduct = "Different product"; +$lang->testcase->differentProduct = "Different product"; $lang->testcase->newScene = "Add Scene"; $lang->testcase->sceneTitle = 'Scene Title'; @@ -328,7 +326,6 @@ $lang->testcase->editScene = 'Edit Scene'; $lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; $lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; $lang->testcase->sceneb = "Scene"; -$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; $lang->testcase->onlyScene = 'Only Scene'; $lang->testcase->iScene = 'Scene'; $lang->testcase->generalTitle = 'Title'; @@ -343,5 +340,7 @@ $lang->testcase->dragModalMessage = '

    There are two possible situations fo $lang->testcase->dragModalChangeScene = 'Change its scene'; $lang->testcase->dragModalChangeOrder = 'Reorder'; +$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; + $lang->scene = new stdclass(); $lang->scene->title = 'Scene Title'; \ No newline at end of file diff --git a/module/testcase/lang/fr.php b/module/testcase/lang/fr.php index 7d88be8890..4d5ccad995 100644 --- a/module/testcase/lang/fr.php +++ b/module/testcase/lang/fr.php @@ -267,13 +267,11 @@ $lang->testcase->featureBar['browse']['zerocase'] = 'Zero Case Story'; $lang->testcase->featureBar['browse']['suite'] = 'Suite'; $lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; - $lang->testcase->importXmind = "Import XMIND"; $lang->testcase->exportXmind = "Export XMIND"; $lang->testcase->getXmindImport = "Get Mindmap"; $lang->testcase->showXMindImport = "Display Mindmap"; - $lang->testcase->xmindImport = "Imort XMIND"; $lang->testcase->xmindExport = "Export XMIND"; $lang->testcase->xmindImportEdit = "XMIND Edit"; @@ -315,7 +313,7 @@ $lang->testcase->createScene = "Add Scene"; $lang->testcase->changeScene = "Drag to change the scene which it belongs"; $lang->testcase->batchChangeScene = "Batch change scene"; $lang->testcase->updateOrder = "Drag Sort"; -$lang->testcase->errDiffProduct = "Different product"; +$lang->testcase->differentProduct = "Different product"; $lang->testcase->newScene = "Add Scene"; $lang->testcase->sceneTitle = 'Scene Title'; @@ -328,7 +326,6 @@ $lang->testcase->editScene = 'Edit Scene'; $lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; $lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; $lang->testcase->sceneb = "Scene"; -$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; $lang->testcase->onlyScene = 'Only Scene'; $lang->testcase->iScene = 'Scene'; $lang->testcase->generalTitle = 'Title'; @@ -343,5 +340,7 @@ $lang->testcase->dragModalMessage = '

    There are two possible situations fo $lang->testcase->dragModalChangeScene = 'Change its scene'; $lang->testcase->dragModalChangeOrder = 'Reorder'; +$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; + $lang->scene = new stdclass(); $lang->scene->title = 'Scene Title'; \ No newline at end of file diff --git a/module/testcase/lang/vi.php b/module/testcase/lang/vi.php index 6161998ca6..502b8c4c78 100644 --- a/module/testcase/lang/vi.php +++ b/module/testcase/lang/vi.php @@ -11,7 +11,15 @@ */ $lang->testcase->id = 'ID'; $lang->testcase->product = $lang->productCommon; +$lang->testcase->project = $lang->projectCommon; +$lang->testcase->execution = $lang->executionCommon; +$lang->testcase->linkStory = 'linkStory'; $lang->testcase->module = 'Module'; +$lang->testcase->auto = 'Test Automation Cases'; +$lang->testcase->frame = 'Test Automation Cramework'; +$lang->testcase->howRun = 'Testing Method'; +$lang->testcase->frequency = 'Frequency'; +$lang->testcase->path = 'Path'; $lang->testcase->lib = "Thư viện tình huống"; $lang->testcase->branch = "Branch/Platform"; $lang->testcase->moduleAB = 'Module'; @@ -24,9 +32,11 @@ $lang->testcase->precondition = 'Điều kiện bắt buộc'; $lang->testcase->pri = 'Ưu tiên'; $lang->testcase->type = 'Loại'; $lang->testcase->status = 'Tình trạng'; +$lang->testcase->statusAB = 'Status'; $lang->testcase->subStatus = 'Tình trạng con'; $lang->testcase->steps = 'Các bước'; $lang->testcase->openedBy = 'Người tạo'; +$lang->testcase->openedByAB = 'Reporter'; $lang->testcase->openedDate = 'Ngày tạo'; $lang->testcase->lastEditedBy = 'Người sửa'; $lang->testcase->result = 'Kết quả'; @@ -36,7 +46,14 @@ $lang->testcase->files = 'Files'; $lang->testcase->linkCase = 'Tình huống liên kết'; $lang->testcase->linkCases = 'Liên kết tình huống'; $lang->testcase->unlinkCase = 'Hủy liên kết tình huống'; +$lang->testcase->linkBug = 'Linked Bugs'; +$lang->testcase->linkBugs = 'Link Bug'; +$lang->testcase->unlinkBug = 'Unlink Bugs'; $lang->testcase->stage = 'Giai đoạn'; +$lang->testcase->scriptedBy = 'ScriptedBy'; +$lang->testcase->scriptedDate = 'ScriptedDate'; +$lang->testcase->scriptStatus = 'Script Status'; +$lang->testcase->scriptLocation = 'Script Location'; $lang->testcase->reviewedBy = 'Người duyệt'; $lang->testcase->reviewedDate = 'Ngày duyệt'; $lang->testcase->reviewResult = 'Duyệt kết quả'; @@ -54,6 +71,7 @@ $lang->testcase->assignedTo = 'Giao cho'; $lang->testcase->colorTag = 'Màu'; $lang->testcase->lastRunResult = 'Kết quả'; $lang->testcase->desc = 'Các bước'; +$lang->testcase->parent = 'Parent'; $lang->testcase->xml = 'XML'; $lang->testcase->expect = 'Kỳ vọng'; $lang->testcase->allProduct = "Tất cả {$lang->productCommon}"; @@ -73,11 +91,17 @@ $lang->testcase->sync = 'Đồng bộ tình huống'; $lang->testcase->ignore = 'Bỏ qua'; $lang->testcase->fromTesttask = 'Từ Yêu cầu Test'; $lang->testcase->fromCaselib = 'Từ thư viện tình huống'; +$lang->testcase->fromCaseID = 'From Case ID'; +$lang->testcase->fromCaseVersion = 'From Case Version'; +$lang->testcase->mailto = 'Mailto'; $lang->testcase->deleted = 'Đã xóa'; $lang->testcase->browseUnits = 'Unit Test'; +$lang->testcase->suite = 'Test Suite'; +$lang->testcase->executionStatus = 'executionStatus'; $lang->testcase->caseType = 'Case Type'; $lang->testcase->allType = 'All Types'; -$lang->testcase->showAutoCase = 'Automated Test Cases'; +$lang->testcase->showAutoCase = 'Automated'; +$lang->testcase->automation = 'Automation Test'; $lang->case = $lang->testcase; // For dao checking using. Because 'case' is a php keywords, so the module name is testcase, table name is still case. @@ -86,7 +110,6 @@ $lang->testcase->stepDesc = 'Bước'; $lang->testcase->stepExpect = 'Kỳ vọng'; $lang->testcase->stepVersion = 'Phiên bản'; -$lang->testcase->common = 'Tình huống'; $lang->testcase->index = "Trang tình huống"; $lang->testcase->create = "Thêm tình huống"; $lang->testcase->batchCreate = "Thêm hàng loạt"; @@ -108,6 +131,7 @@ $lang->testcase->batchConfirmStoryChange = "Xác nhận hàng loạt"; $lang->testcase->batchCaseTypeChange = "Thay đổi Loại hàng loạt"; $lang->testcase->browse = "Danh sách tình huống"; $lang->testcase->groupCase = "Xem theo Nhóm"; +$lang->testcase->zeroCase = "Stories without cases"; $lang->testcase->import = "Nhập"; $lang->testcase->importAction = "Nhập tình huống"; $lang->testcase->fileImport = "Nhập CSV"; @@ -126,7 +150,9 @@ $lang->testcase->groupName = 'Tên nhóm'; $lang->testcase->step = 'Các bước'; $lang->testcase->stepChild = 'Các bước con'; $lang->testcase->viewAll = 'Tất cả tình huống'; +$lang->testcase->importToLib = "Import To Library"; $lang->testcase->showScript = 'Show Script'; +$lang->testcase->autoScript = 'Script'; $lang->testcase->new = 'Mới'; @@ -156,11 +182,12 @@ $lang->testcase->legendLinkBugs = 'Bugs'; $lang->testcase->legendOpenAndEdit = 'Tạo/Sửa'; $lang->testcase->legendComment = 'Nhận xét'; -$lang->testcase->summary = "Tổng %s tình huống, và %s tình huống đang chạy."; -$lang->testcase->confirmDelete = 'Bạn có muốn xóa tình huống này?'; -$lang->testcase->confirmBatchDelete = 'Bạn có muốn xóa tình huống hàng loạt?'; -$lang->testcase->ditto = 'Như trên'; -$lang->testcase->dittoNotice = 'Tình huống này không liên kết tới sản phẩm bởi vì nó là cuối cùng!'; +$lang->testcase->summary = "Tổng %s tình huống, và %s tình huống đang chạy."; +$lang->testcase->confirmDelete = 'Bạn có muốn xóa tình huống này?'; +$lang->testcase->confirmBatchDelete = 'Bạn có muốn xóa tình huống hàng loạt?'; +$lang->testcase->ditto = 'Như trên'; +$lang->testcase->dittoNotice = 'Tình huống này không liên kết tới sản phẩm bởi vì nó là cuối cùng!'; +$lang->testcase->confirmUnlinkTesttask = 'The case [%s] is already associated in the testtask order of the previous branch/platform, after adjusting the branch/platform, it will be removed from the test list of the previous branch/platform, please confirm whether to continue to modify.'; $lang->testcase->reviewList[0] = 'KHÔNG'; $lang->testcase->reviewList[1] = 'CÓ'; @@ -207,16 +234,21 @@ $lang->testcase->resultList['blocked'] = 'Bị khóa'; $lang->testcase->buttonToList = 'Trở lại'; +$lang->testcase->whichLine = 'Line No.%s : '; +$lang->testcase->stepsEmpty = 'Step %s cannot be empty.'; $lang->testcase->errorEncode = 'Không có dữ liệu. Vui lòng chọn giải mã đúng và tải lên lại!'; $lang->testcase->noFunction = 'Iconv và mb_convert_encoding không được tìm thấy. Bạn không thể chuyển dữ liệu này thành mã hóa bạn muốn!'; $lang->testcase->noRequire = "Dòng %s có “%s ” là trường bắt buộc và nó nên để trống."; +$lang->testcase->noRequireTip = "“%s”is a required field and it should not be blank."; $lang->testcase->noLibrary = "Không có thư viện tồn tại. Vui lòng tạo một trước."; $lang->testcase->mustChooseResult = 'Kết quả xét duyệt là bắt buộc.'; $lang->testcase->noModule = '

    Chưa có Module.
    Quản lý ngay.
    '; $lang->testcase->noCase = 'Không có tình huống nào'; +$lang->testcase->importedCases = 'The case with ID%s has been imported in the same module and has been ignored.'; $lang->testcase->searchStories = 'Nhập nội dung cần tìm cho câu chuyện'; $lang->testcase->selectLib = 'Chọn thư viện'; +$lang->testcase->selectLibAB = 'Select Library'; $lang->testcase->action = new stdclass(); $lang->testcase->action->fromlib = array('main' => '$date, nhập bởi $actor từ $extra.'); @@ -231,19 +263,15 @@ $lang->testcase->featureBar['browse']['all'] = $lang->testcase->allCases $lang->testcase->featureBar['browse']['wait'] = 'Đang đợi'; $lang->testcase->featureBar['browse']['needconfirm'] = $lang->testcase->needConfirm; $lang->testcase->featureBar['browse']['group'] = 'Group View'; -$lang->testcase->featureBar['browse']['suite'] = 'Suite'; $lang->testcase->featureBar['browse']['zerocase'] = 'Zero Case Story'; -$lang->testcase->featureBar['browse']['browseunits'] = 'Unit Test'; +$lang->testcase->featureBar['browse']['suite'] = 'Suite'; $lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; -$lang->testcase->featureBar['groupcase'] = $lang->testcase->featureBar['browse']; - $lang->testcase->importXmind = "Import XMIND"; $lang->testcase->exportXmind = "Export XMIND"; $lang->testcase->getXmindImport = "Get Mindmap"; $lang->testcase->showXMindImport = "Display Mindmap"; - $lang->testcase->xmindImport = "Imort XMIND"; $lang->testcase->xmindExport = "Export XMIND"; $lang->testcase->xmindImportEdit = "XMIND Edit"; @@ -285,7 +313,7 @@ $lang->testcase->createScene = "Add Scene"; $lang->testcase->changeScene = "Drag to change the scene which it belongs"; $lang->testcase->batchChangeScene = "Batch change scene"; $lang->testcase->updateOrder = "Drag Sort"; -$lang->testcase->errDiffProduct = "Different product"; +$lang->testcase->differentProduct = "Different product"; $lang->testcase->newScene = "Add Scene"; $lang->testcase->sceneTitle = 'Scene Title'; @@ -298,7 +326,6 @@ $lang->testcase->editScene = 'Edit Scene'; $lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; $lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; $lang->testcase->sceneb = "Scene"; -$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; $lang->testcase->onlyScene = 'Only Scene'; $lang->testcase->iScene = 'Scene'; $lang->testcase->generalTitle = 'Title'; @@ -313,5 +340,7 @@ $lang->testcase->dragModalMessage = '

    There are two possible situations fo $lang->testcase->dragModalChangeScene = 'Change its scene'; $lang->testcase->dragModalChangeOrder = 'Reorder'; +$lang->testcase->confirmBatchDeleteSceneCase = 'Are you sure you want to delete these scene or test cases in batch?'; + $lang->scene = new stdclass(); $lang->scene->title = 'Scene Title'; \ No newline at end of file diff --git a/module/testcase/lang/zh-cn.php b/module/testcase/lang/zh-cn.php index 72972304cd..8809ac253f 100644 --- a/module/testcase/lang/zh-cn.php +++ b/module/testcase/lang/zh-cn.php @@ -267,13 +267,11 @@ $lang->testcase->featureBar['browse']['zerocase'] = "零用例{$lang->SRCommo $lang->testcase->featureBar['browse']['suite'] = '套件'; $lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; - $lang->testcase->importXmind = "导入XMIND"; $lang->testcase->exportXmind = "导出XMIND"; $lang->testcase->getXmindImport = "获取导图"; $lang->testcase->showXMindImport = "显示导图"; - $lang->testcase->xmindImport = "导入XMIND"; $lang->testcase->xmindExport = "导出XMIND"; $lang->testcase->xmindImportEdit = "XMIND 编辑"; diff --git a/module/testcase/lang/zh-tw.php b/module/testcase/lang/zh-tw.php index 973a0a7d35..1e4ab07e4f 100644 --- a/module/testcase/lang/zh-tw.php +++ b/module/testcase/lang/zh-tw.php @@ -11,8 +11,9 @@ */ $lang->testcase->id = '用例編號'; $lang->testcase->product = "所屬{$lang->productCommon}"; -$lang->testcase->project = '所屬項目'; -$lang->testcase->execution = '所屬執行'; +$lang->testcase->project = '所屬' . $lang->projectCommon; +$lang->testcase->execution = '所屬' . $lang->executionCommon; +$lang->testcase->linkStory = '关联需求'; $lang->testcase->module = '所屬模組'; $lang->testcase->auto = '自動化測試用例'; $lang->testcase->frame = '自動化測試框架'; @@ -20,7 +21,7 @@ $lang->testcase->howRun = '測試方式'; $lang->testcase->frequency = '使用頻率'; $lang->testcase->path = '路徑'; $lang->testcase->lib = "所屬庫"; -$lang->testcase->branch = "分支/平台"; +$lang->testcase->branch = "平台/分支"; $lang->testcase->moduleAB = '模組'; $lang->testcase->story = "相關{$lang->SRCommon}"; $lang->testcase->storyVersion = "{$lang->SRCommon}版本"; @@ -31,9 +32,11 @@ $lang->testcase->precondition = '前置條件'; $lang->testcase->pri = '優先順序'; $lang->testcase->type = '用例類型'; $lang->testcase->status = '用例狀態'; +$lang->testcase->statusAB = '状态'; $lang->testcase->subStatus = '子狀態'; $lang->testcase->steps = '用例步驟'; $lang->testcase->openedBy = '由誰創建'; +$lang->testcase->openedByAB = '创建者'; $lang->testcase->openedDate = '創建日期'; $lang->testcase->lastEditedBy = '最後修改者'; $lang->testcase->result = '測試結果'; @@ -43,6 +46,9 @@ $lang->testcase->files = '附件'; $lang->testcase->linkCase = '相關用例'; $lang->testcase->linkCases = '關聯相關用例'; $lang->testcase->unlinkCase = '移除相關用例'; +$lang->testcase->linkBug = '相关Bug'; +$lang->testcase->linkBugs = '关联相关Bug'; +$lang->testcase->unlinkBug = '移除相关Bug'; $lang->testcase->stage = '適用階段'; $lang->testcase->scriptedBy = '腳本由誰創建'; $lang->testcase->scriptedDate = '腳本創建日期'; @@ -91,6 +97,11 @@ $lang->testcase->mailto = '抄送給'; $lang->testcase->deleted = '是否刪除'; $lang->testcase->browseUnits = '單元測試'; $lang->testcase->suite = '套件'; +$lang->testcase->executionStatus = '执行状态'; +$lang->testcase->caseType = '用例类型'; +$lang->testcase->allType = '所有类型'; +$lang->testcase->showAutoCase = '自动化'; +$lang->testcase->automation = '自动化设置'; $lang->case = $lang->testcase; // 用於DAO檢查時使用。因為case是系統關鍵字,所以無法定義該模組為case,只能使用testcase,但表還是使用的case。 @@ -99,7 +110,6 @@ $lang->testcase->stepDesc = '步驟'; $lang->testcase->stepExpect = '預期'; $lang->testcase->stepVersion = '版本'; -$lang->testcase->common = '用例'; $lang->testcase->index = "用例管理首頁"; $lang->testcase->create = "建用例"; $lang->testcase->batchCreate = "批量建用例"; @@ -121,6 +131,7 @@ $lang->testcase->batchConfirmStoryChange = "批量確認變更"; $lang->testcase->batchCaseTypeChange = "批量修改類型"; $lang->testcase->browse = "用例列表"; $lang->testcase->groupCase = "分組瀏覽用例"; +$lang->testcase->zeroCase = "零用例{$lang->common->story}"; $lang->testcase->import = "導入"; $lang->testcase->importAction = "導入用例"; $lang->testcase->fileImport = "導入CSV"; @@ -139,6 +150,9 @@ $lang->testcase->groupName = '分組名稱'; $lang->testcase->step = '步驟'; $lang->testcase->stepChild = '子步驟'; $lang->testcase->viewAll = '查看所有'; +$lang->testcase->importToLib = '导入用例库'; +$lang->testcase->showScript = '查看自动化脚本'; +$lang->testcase->autoScript = '自动化脚本'; $lang->testcase->new = '新增'; @@ -173,6 +187,7 @@ $lang->testcase->confirmDelete = '您確認要刪除該測試用例嗎?'; $lang->testcase->confirmBatchDelete = '您確認要批量刪除這些測試用例嗎?'; $lang->testcase->ditto = '同上'; $lang->testcase->dittoNotice = '該用例與上一用例不屬於同一產品!'; +$lang->testcase->confirmUnlinkTesttask = '用例[%s]已关联在之前所属平台/分支的测试单中,调整平台/分支后,将从之前所属平台/分支的测试单中移除,请确认是否继续修改。'; $lang->testcase->reviewList[0] = '否'; $lang->testcase->reviewList[1] = '是'; @@ -219,16 +234,21 @@ $lang->testcase->resultList['blocked'] = '阻塞'; $lang->testcase->buttonToList = '返回'; +$lang->testcase->whichLine = '第%s行'; +$lang->testcase->stepsEmpty = '步骤%s不能为空'; $lang->testcase->errorEncode = '無數據,請選擇正確的編碼重新上傳!'; $lang->testcase->noFunction = '不存在iconv和mb_convert_encoding轉碼方法,不能將數據轉成想要的編碼!'; $lang->testcase->noRequire = "%s行的“%s”是必填欄位,不能為空"; +$lang->testcase->noRequireTip = "“%s”是必填字段,不能为空"; $lang->testcase->noLibrary = "現在還沒有用例庫,請先創建!"; $lang->testcase->mustChooseResult = '必須選擇評審結果'; $lang->testcase->noModule = '

    您現在還沒有模組信息
    請維護測試模組
    '; $lang->testcase->noCase = '暫時沒有用例。'; +$lang->testcase->importedCases = 'ID为 %s 的用例在相同模块已经导入,已忽略。'; $lang->testcase->searchStories = "鍵入來搜索{$lang->SRCommon}"; $lang->testcase->selectLib = '請選擇庫'; +$lang->testcase->selectLibAB = '选择用例库'; $lang->testcase->action = new stdclass(); $lang->testcase->action->fromlib = array('main' => '$date, 由 $actor 從用例庫 $extra導入。'); @@ -238,22 +258,20 @@ $lang->testcase->action->unlinkedfromproject = array('main' => '$date, 由 testcase->action->linked2execution = array('main' => '$date, 由 $actor 關聯到' . $lang->executionCommon . ' $extra。'); $lang->testcase->action->unlinkedfromexecution = array('main' => '$date, 由 $actor 從' . $lang->executionCommon . ' $extra 移除。'); +$lang->testcase->featureBar['browse']['casetype'] = $lang->testcase->caseType; $lang->testcase->featureBar['browse']['all'] = $lang->testcase->allCases; $lang->testcase->featureBar['browse']['wait'] = '待評審'; $lang->testcase->featureBar['browse']['needconfirm'] = $lang->testcase->needConfirm; $lang->testcase->featureBar['browse']['group'] = '分組查看'; -$lang->testcase->featureBar['browse']['suite'] = '套件'; $lang->testcase->featureBar['browse']['zerocase'] = "零用例{$lang->SRCommon}"; -$lang->testcase->featureBar['browse']['browseunits'] = '單元測試'; -$lang->testcase->featureBar['groupcase'] = $lang->testcase->featureBar['browse']; - +$lang->testcase->featureBar['browse']['suite'] = '套件'; +$lang->testcase->featureBar['browse']['autocase'] = $lang->testcase->showAutoCase; $lang->testcase->importXmind = "导入XMIND"; $lang->testcase->exportXmind = "导出XMIND"; $lang->testcase->getXmindImport = "获取导图"; $lang->testcase->showXMindImport = "显示导图"; - $lang->testcase->xmindImport = "导入XMIND"; $lang->testcase->xmindExport = "导出XMIND"; $lang->testcase->xmindImportEdit = "XMIND 编辑"; From 1cd8eda1a11417904c29cd66bc5fa24a60ed7a4c Mon Sep 17 00:00:00 2001 From: zhaoke Date: Fri, 7 Apr 2023 09:59:55 +0800 Subject: [PATCH 05/14] * Fix bug #33845. --- module/group/lang/resource.php | 2 ++ module/testcase/control.php | 2 +- module/testcase/lang/de.php | 1 + module/testcase/lang/en.php | 1 + module/testcase/lang/fr.php | 1 + module/testcase/lang/vi.php | 1 + module/testcase/lang/zh-cn.php | 1 + module/testcase/lang/zh-tw.php | 1 + 8 files changed, 9 insertions(+), 1 deletion(-) diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index 7709634e82..07ee1b9322 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -1097,6 +1097,7 @@ $lang->resource->testcase->batchChangeScene = 'batchChangeScene'; $lang->resource->testcase->updateOrder = 'updateOrder'; $lang->resource->testcase->importXmind = 'importXmind'; +$lang->resource->testcase->saveXmindImport = 'saveXmindImport'; $lang->resource->testcase->exportXmind = 'exportXmind'; $lang->resource->testcase->getXmindImport = 'getXmindImport'; $lang->resource->testcase->showXMindImport = 'showXMindImport'; @@ -1143,6 +1144,7 @@ $lang->testcase->methodOrder[185] = 'importXmind'; $lang->testcase->methodOrder[190] = 'getXmindImport'; $lang->testcase->methodOrder[195] = 'showXMindImport'; $lang->testcase->methodOrder[200] = 'exportXmind'; +$lang->testcase->methodOrder[205] = 'saveXmindImport'; /* Test task. */ $lang->resource->testtask = new stdclass(); diff --git a/module/testcase/control.php b/module/testcase/control.php index 912cf5d713..64b86435d8 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -2844,7 +2844,7 @@ class testcase extends control { $scene = $this->dao->select('*')->from(VIEW_SCENECASE)->where('id')->eq($sceneID)->andWhere('isCase')->eq(2)->fetch(); - if($confirm == 'no') return print(js::confirm(sprintf($this->lang->testcase->confirmDeleteScene,addslashes($scene->title)), $this->createLink('testcase', 'deleteScene', "sceneID=$sceneID&confirm=yes"))); + if($confirm == 'no') return print(js::confirm(sprintf(addslashes($this->lang->testcase->confirmDeleteScene),addslashes($scene->title)), $this->createLink('testcase', 'deleteScene', "sceneID=$sceneID&confirm=yes"))); $childrenCount = $this->dao->select('count(*) as count')->from(VIEW_SCENECASE)->where('parent')->eq($sceneID)->andWhere('deleted')->eq(0)->fetch('count'); if($childrenCount) diff --git a/module/testcase/lang/de.php b/module/testcase/lang/de.php index 7bfb91eb7d..624fe038a8 100644 --- a/module/testcase/lang/de.php +++ b/module/testcase/lang/de.php @@ -271,6 +271,7 @@ $lang->testcase->importXmind = "Import XMIND"; $lang->testcase->exportXmind = "Export XMIND"; $lang->testcase->getXmindImport = "Get Mindmap"; $lang->testcase->showXMindImport = "Display Mindmap"; +$lang->testcase->saveXmindImport = "Save Mindmap"; $lang->testcase->xmindImport = "Imort XMIND"; $lang->testcase->xmindExport = "Export XMIND"; diff --git a/module/testcase/lang/en.php b/module/testcase/lang/en.php index b2d3b6f32d..f6b49f9653 100644 --- a/module/testcase/lang/en.php +++ b/module/testcase/lang/en.php @@ -271,6 +271,7 @@ $lang->testcase->importXmind = "Import XMIND"; $lang->testcase->exportXmind = "Export XMIND"; $lang->testcase->getXmindImport = "Get Mindmap"; $lang->testcase->showXMindImport = "Display Mindmap"; +$lang->testcase->saveXmindImport = "Save Mindmap"; $lang->testcase->xmindImport = "Imort XMIND"; $lang->testcase->xmindExport = "Export XMIND"; diff --git a/module/testcase/lang/fr.php b/module/testcase/lang/fr.php index 4d5ccad995..927475a348 100644 --- a/module/testcase/lang/fr.php +++ b/module/testcase/lang/fr.php @@ -271,6 +271,7 @@ $lang->testcase->importXmind = "Import XMIND"; $lang->testcase->exportXmind = "Export XMIND"; $lang->testcase->getXmindImport = "Get Mindmap"; $lang->testcase->showXMindImport = "Display Mindmap"; +$lang->testcase->saveXmindImport = "Save Mindmap"; $lang->testcase->xmindImport = "Imort XMIND"; $lang->testcase->xmindExport = "Export XMIND"; diff --git a/module/testcase/lang/vi.php b/module/testcase/lang/vi.php index 502b8c4c78..c4f7ef958d 100644 --- a/module/testcase/lang/vi.php +++ b/module/testcase/lang/vi.php @@ -271,6 +271,7 @@ $lang->testcase->importXmind = "Import XMIND"; $lang->testcase->exportXmind = "Export XMIND"; $lang->testcase->getXmindImport = "Get Mindmap"; $lang->testcase->showXMindImport = "Display Mindmap"; +$lang->testcase->saveXmindImport = "Save Mindmap"; $lang->testcase->xmindImport = "Imort XMIND"; $lang->testcase->xmindExport = "Export XMIND"; diff --git a/module/testcase/lang/zh-cn.php b/module/testcase/lang/zh-cn.php index 8809ac253f..50acdc1af4 100644 --- a/module/testcase/lang/zh-cn.php +++ b/module/testcase/lang/zh-cn.php @@ -271,6 +271,7 @@ $lang->testcase->importXmind = "导入XMIND"; $lang->testcase->exportXmind = "导出XMIND"; $lang->testcase->getXmindImport = "获取导图"; $lang->testcase->showXMindImport = "显示导图"; +$lang->testcase->saveXmindImport = "保存导图"; $lang->testcase->xmindImport = "导入XMIND"; $lang->testcase->xmindExport = "导出XMIND"; diff --git a/module/testcase/lang/zh-tw.php b/module/testcase/lang/zh-tw.php index 1e4ab07e4f..db298380ef 100644 --- a/module/testcase/lang/zh-tw.php +++ b/module/testcase/lang/zh-tw.php @@ -271,6 +271,7 @@ $lang->testcase->importXmind = "导入XMIND"; $lang->testcase->exportXmind = "导出XMIND"; $lang->testcase->getXmindImport = "获取导图"; $lang->testcase->showXMindImport = "显示导图"; +$lang->testcase->saveXmindImport = "保存导图"; $lang->testcase->xmindImport = "导入XMIND"; $lang->testcase->xmindExport = "导出XMIND"; From 026897ad09c740e72d6c47455a09ac6f102263a4 Mon Sep 17 00:00:00 2001 From: zhaoke Date: Fri, 7 Apr 2023 10:42:29 +0800 Subject: [PATCH 06/14] * Fix php notice. --- module/testcase/control.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/testcase/control.php b/module/testcase/control.php index 64b86435d8..71c7fa21d8 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -3001,7 +3001,7 @@ class testcase extends control if(strpos($orderBy, 'sort') === false) return false; /* Get list of original scenes and cases, and sort with orderBy param. */ - $scenesMap = $this->dao->select('id,sort,isCase')->from(VIEW_SCENECASE)->where('id')->in($idList)->orderBy($orderBy)->fetchAll(id); + $scenesMap = $this->dao->select('id,sort,isCase')->from(VIEW_SCENECASE)->where('id')->in($idList)->orderBy($orderBy)->fetchAll('id'); foreach($scenesMap as $scene) { From 91afaf968d6b7c1fe8ac8ad8574c9999363e1aca Mon Sep 17 00:00:00 2001 From: zhaoke Date: Fri, 7 Apr 2023 11:38:54 +0800 Subject: [PATCH 07/14] * Fix table not exist. --- module/zanode/model.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/module/zanode/model.php b/module/zanode/model.php index 592c7b1ea8..ac4f038040 100644 --- a/module/zanode/model.php +++ b/module/zanode/model.php @@ -199,7 +199,7 @@ class zanodemodel extends model if(!empty($result) and $result->code == 'success') { - $this->dao->update(TABLE_HOST)->set('status')->eq(static::STATUS_CREATING_IMG)->where('id')->eq($node->id)->exec(); + $this->dao->update(TABLE_ZAHOST)->set('status')->eq(static::STATUS_CREATING_IMG)->where('id')->eq($node->id)->exec(); return $newID; } @@ -259,7 +259,7 @@ class zanodemodel extends model if(!empty($result) and $result->code == 'success') { $this->loadModel('action')->create('zanode', $zanodeID, 'createdSnapshot', '', $data->name); - $this->dao->update(TABLE_HOST)->set('status')->eq(static::STATUS_CREATING_SNAP)->where('id')->eq($node->id)->exec(); + $this->dao->update(TABLE_ZAHOST)->set('status')->eq(static::STATUS_CREATING_SNAP)->where('id')->eq($node->id)->exec(); return $newID; } @@ -309,7 +309,7 @@ class zanodemodel extends model if(!empty($result) and $result->code == 'success') { - $this->dao->update(TABLE_HOST)->set('status')->eq(static::STATUS_CREATING_SNAP)->where('id')->eq($node->id)->exec(); + $this->dao->update(TABLE_ZAHOST)->set('status')->eq(static::STATUS_CREATING_SNAP)->where('id')->eq($node->id)->exec(); return $newID; } @@ -383,7 +383,7 @@ class zanodemodel extends model if(!empty($result) and $result->code == 'success') { - $this->dao->update(TABLE_HOST)->set('status')->eq('restoring')->where('id')->eq($node->id)->exec(); + $this->dao->update(TABLE_ZAHOST)->set('status')->eq('restoring')->where('id')->eq($node->id)->exec(); $this->loadModel('action')->create('zanode', $zanodeID, 'restoredsnapshot', '', $snap->name); return true; } From 5d0cc84425c28df3da5fb6e307dae54731abfb16 Mon Sep 17 00:00:00 2001 From: zhaoke Date: Wed, 12 Apr 2023 17:28:31 +0800 Subject: [PATCH 08/14] * Fix case drag sort error. --- module/testcase/js/browse.js | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/module/testcase/js/browse.js b/module/testcase/js/browse.js index 88d96cf9ca..0e33858ee6 100644 --- a/module/testcase/js/browse.js +++ b/module/testcase/js/browse.js @@ -95,7 +95,7 @@ DtSort.Table = function(options) this.init(); - + var that = this; //解决表格展开折叠按钮单击导致checkbox自动选中功能,临时先放在这里 this.$container.find("tr").on("click",'.table-nest-toggle', function(e) { @@ -104,6 +104,7 @@ DtSort.Table = function(options) var $row = $(e.currentTarget).closest("tr"); var dataId = $row.attr("data-id"); table.toggleNestedRows(dataId,undefined,true); + that.measure(); }) } @@ -172,8 +173,18 @@ DtSort.Table.prototype.measure = function() } } +DtSort.Table.prototype.getNextEle = function(currentIndex) +{ + if(currentIndex+1 >= this.rowList.length) return -1; + for(var i=currentIndex+1; i 0) return i; + } +} + DtSort.Table.prototype.pick = function(pos) { + if(pos == undefined) return; if(this.rowList.length == 0) return undefined; var hitIndex = -1; @@ -185,14 +196,18 @@ DtSort.Table.prototype.pick = function(pos) { if(this.rowList[i].$dom.hasClass("table-nest-hide")) continue; - var boundary = {x:x, y:y, w:this.rowList[i].boundary.w, h:this.rowList[i].boundary.h}; - if(boundary.x <= pos.x && pos.x <= boundary.x+boundary.w && boundary.y <= pos.y && pos.y <= boundary.y+boundary.h) + var boundary = this.rowList[i].boundary; + var maxH = boundary.y+boundary.h; + var nextIndex = this.getNextEle(i); + if(nextIndex != -1) + { + maxH = this.rowList[nextIndex].boundary.y; + } + if(boundary.y <= pos.y && pos.y < maxH) { hitIndex = i; break; } - - y += boundary.h; } if(hitIndex < 0) return undefined; @@ -221,7 +236,8 @@ DtSort.Table.prototype.mouseDown = function(pos) DtSort.Table.prototype.mouseMove = function(pos) { - var result = this.pick(pos); + if(this.downPosition == undefined) return ; + var result = this.pick(this.sourceRow == undefined ? this.downPosition : pos); if(this.mousePress == false || DtSort.tools.distance(this.downPosition,pos)<=this.options.moveBuffer) { @@ -309,6 +325,7 @@ DtSort.Table.prototype.mouseUp = function(pos) this.sourceRow = undefined; this.targetRow = undefined; + this.downPosition = undefined; }; DtSort.Table.prototype.getRowPos = function(row) From a64bc7d5ab23f96970ca85418db3e1ab0de29fb2 Mon Sep 17 00:00:00 2001 From: zhaoke Date: Thu, 13 Apr 2023 13:51:34 +0800 Subject: [PATCH 09/14] * Move xmind functions to lib. --- config/filter.php | 2 +- db/update18.3.sql | 1 - lib/xmind/xmind.class.php | 213 ++++++++++++++++++++++++++ module/action/lang/zh-cn.php | 1 - module/action/view/trash.html.php | 107 ++++++------- module/common/view/mindmap.html.php | 1 - module/datatable/model.php | 1 - module/testcase/config.php | 1 - module/testcase/control.php | 225 ++-------------------------- module/testcase/lang/de.php | 2 +- module/testcase/lang/en.php | 2 +- module/testcase/lang/fr.php | 2 +- module/testcase/lang/vi.php | 2 +- module/testcase/lang/zh-cn.php | 2 +- 14 files changed, 281 insertions(+), 281 deletions(-) create mode 100644 lib/xmind/xmind.class.php diff --git a/config/filter.php b/config/filter.php index ad76a9f679..0ff898303e 100755 --- a/config/filter.php +++ b/config/filter.php @@ -360,7 +360,7 @@ $filter->testtask->default->cookie['preProductID'] = 'int'; $filter->testcase->browse->cookie['onlyScene'] = 'code'; if(empty($filter->project->testcase)) $filter->project->testcase = new stdclass(); -$filter->project->testcase->cookie= array('onlyScene' => 'code'); +$filter->project->testcase->cookie = array('onlyScene' => 'code'); $filter->todo->export->cookie['checkedItem'] = 'reg::checked'; diff --git a/db/update18.3.sql b/db/update18.3.sql index c7e72600f1..5d85834e83 100644 --- a/db/update18.3.sql +++ b/db/update18.3.sql @@ -169,7 +169,6 @@ UNION 2 AS `isCase` FROM `zt_scene`; -UPDATE `zt_screen` SET `name` = '燃尽图大屏' WHERE `id` = '5'; CREATE TABLE `zt_chart_back` SELECT * FROM `zt_chart`; ALTER TABLE `zt_chart` MODIFY `fields` mediumtext NULL; diff --git a/lib/xmind/xmind.class.php b/lib/xmind/xmind.class.php new file mode 100644 index 0000000000..a89ad6a888 --- /dev/null +++ b/lib/xmind/xmind.class.php @@ -0,0 +1,213 @@ + + * @package Xmind + * @version $Id$ + * @link http://www.zentao.net + */ +class xmind{ + function createModuleNode($xmlDoc, $context, $productNode, &$moduleNodes) + { + $config = $context['config']; + $moduleList = $context['moduleList']; + + foreach($moduleList as $key => $name) + { + $suffix = $config['module'].':'.$key; + $moduleNode = $this->createNode($xmlDoc, $name, $suffix, array('nodeType' => 'module')); + $productNode->appendChild($moduleNode); + + $moduleNodes[$key] = $moduleNode; + } + } + + function createSceneNode($xmlDoc, $context, $productNode, &$moduleNodes, &$sceneNodes) + { + $sceneMaps = $context['sceneMaps']; + $config = $context['config']; + + $topScenes = $context['topScenes']; + + foreach($topScenes as $scene) + { + $suffix = $config['scene'].':'.$scene->sceneID; + $sceneNode = $this->createNode($xmlDoc, $scene->sceneName, $suffix, array('nodeType' => 'scene')); + + $this->createNextChildScenesNode($scene, $sceneNode, $xmlDoc, $context, $moduleNodes, $sceneNodes); + + if(isset($moduleNodes[$scene->moduleID])) + { + $moduleNode = $moduleNodes[$scene->moduleID]; + $moduleNode->appendChild($sceneNode); + } + else + { + $productNode->appendChild($sceneNode); + } + + $sceneNodes[$scene->sceneID] = $sceneNode; + } + } + + function createNextChildScenesNode($parentScene,$parentNode, $xmlDoc, $context, &$moduleNodes, &$sceneNodes) + { + $sceneMaps = $context['sceneMaps']; + $config = $context['config']; + + foreach($sceneMaps as $key => $scene) + { + if($scene->parentID != $parentScene->sceneID + CHANGEVALUE) continue; + + $suffix = $config['scene'].':'.$scene->sceneID; + $sceneNode = $this->createNode($xmlDoc, $scene->sceneName, $suffix, array('nodeType'=>'scene')); + + $this->createNextChildScenesNode($scene, $sceneNode, $xmlDoc, $context, $moduleNodes, $sceneNodes); + + $parentNode->appendChild($sceneNode); + $sceneNodes[$scene->sceneID] = $sceneNode; + } + } + + function createTestcaseNode($xmlDoc, $context, $productNode, &$moduleNodes, &$sceneNodes) + { + $caseList = $context['caseList']; + + foreach($caseList as $case) + { + if(empty($case->testcaseID)) continue; + + $parentNode = $sceneNodes[$case->sceneID]; + if(!isset($parentNode)) $parentNode = $moduleNodes[$case->moduleID]; + if(!isset($parentNode)) $parentNode = $productNode; + + $this->createTestcaseNodeImpl($case, $xmlDoc, $context, $parentNode); + } + } + + function createTestcaseNodeImpl($case, $xmlDoc, $context, $parentNode) + { + $caseList = $context['caseList']; + $stepList = $context['stepList']; + $config = $context['config']; + $suffix = $config['case'].':'.$case->testcaseID.','.$config['pri'].':'.$case->pri; + $caseNode = $this->createNode($xmlDoc, $case->name, $suffix, array('nodeType'=>'testcase')); + + $parentNode->appendChild($caseNode); + + $topStepList = $this->findTopStepListByCase($case, $stepList); + + foreach($topStepList as $step) + { + $subStepList = $this->findSubStepListByStep($step,$stepList); + + $suffix = count($subStepList) > 0 ? $config['group'] : ''; + $stepNode = $this->createNode($xmlDoc, $step->desc, $suffix, array('nodeType' => 'step')); + $caseNode->appendChild($stepNode); + + if(count($subStepList)) + { + foreach($subStepList as $sub) + { + $subNode = $this->createNode($xmlDoc, $sub->desc, '', array('nodeType'=>'substep')); + $stepNode->appendChild($subNode); + + if(!empty($sub->expect)) + { + $expectNode = $this->createNode($xmlDoc, $sub->expect, '', array('nodeType'=>'expect')); + $subNode->appendChild($expectNode); + } + } + } + + if(count($subStepList) == 0 && !empty($step->expect)) + { + $expectNode = $this->createNode($xmlDoc, $step->expect, '', array('nodeType'=>'expect')); + $stepNode->appendChild($expectNode); + } + } + } + + function findSubStepListByStep($step,$stepList) + { + $subList = array(); + foreach($stepList as $one) + { + if($one->parentID == $step->stepID) + { + $subList[] = $one; + } + } + + return $subList; + } + + public function findTopStepListByCase($case,$stepList) + { + $topList = array(); + foreach($stepList as $step) + { + if($step->parentID == '0' && $step->testcaseID == $case->testcaseID) + { + $topList[] = $step; + } + } + + return $topList; + } + + public function createNode($xmlDoc, $text, $suffix = '', $attrs=array()) + { + $node = $xmlDoc->createElement('node'); + + $textAttr = $xmlDoc->createAttribute('TEXT'); + $textAttrValue = $xmlDoc->createTextNode($this->toText($text,$suffix)); + + $textAttr->appendChild($textAttrValue); + $node->appendChild($textAttr); + + $positionAttr = $xmlDoc->createAttribute("POSITION"); + $positionAttrValue = $xmlDoc->createTextNode('right'); + + $positionAttr->appendChild($positionAttrValue); + $node->appendChild($positionAttr); + + foreach($attrs as $key => $value) + { + $attr = $xmlDoc->createAttribute($key); + $attrValue = $xmlDoc->createTextNode($value); + + $attr->appendChild($attrValue); + $node->appendChild($attr); + } + + return $node; + } + + public function toText($str, $suffix) + { + if(empty($suffix)) return $str; + return $str . '['.$suffix.']'; + } + + function getBetween($kw1, $mark1, $mark2) + { + $kw = $kw1; + $kw = '123' . $kw . '123'; + $st = strripos($kw, $mark1); + $ed = strripos($kw, $mark2); + + if(($st == false || $ed == false) || $st >= $ed) return 0; + + $kw = substr($kw, ($st + 1), ($ed - $st - 1)); + return $kw; + } + + function endsWith($haystack, $needle) + { + return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0; + } +} \ No newline at end of file diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index a0fc6d64f0..1065120926 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -810,7 +810,6 @@ $lang->action->search->objectTypeList['testsuite'] = '套件'; $lang->action->search->objectTypeList['caselib'] = '公共库'; $lang->action->search->objectTypeList['testreport'] = '报告'; - /* 用来在动态显示中显示动作 */ $lang->action->search->label[''] = ''; $lang->action->search->label['created'] = $lang->action->label->created; diff --git a/module/action/view/trash.html.php b/module/action/view/trash.html.php index 909571d76e..48510b55e4 100755 --- a/module/action/view/trash.html.php +++ b/module/action/view/trash.html.php @@ -101,66 +101,57 @@ objectType == 'case' ? 'testcase' : $action->objectType;?> action->objectTypes, $action->objectType, '');?> + objectID;?> objectType == 'scene'){ ?> - objectID,1); - //$showid = preg_replace('/^0+/', '', $showid); + + objectName;?> + + + + objectType == 'user' ? "account={$action->objectName}" : "id={$action->objectID}"; + $methodName = 'view'; + if($module == 'caselib') + { + $methodName = 'view'; + $module = 'caselib'; + } + if($module == 'basicmeas') + { + $module = 'measurement'; + $methodName = 'setSQL'; + $params = "id={$action->objectID}"; + } + if($action->objectType == 'api') + { + $params = "libID=0&moduelID=0&apiID={$action->objectID}"; + $methodName = 'index'; + } + if(strpos('traincourse,traincontents', $module) !== false) + { + $methodName = $module == 'traincourse' ? 'viewcourse' : 'viewchapter'; + $module = 'traincourse'; + } + if(isset($config->action->customFlows[$action->objectType])) + { + $flow = $config->action->customFlows[$action->objectType]; + $module = $flow->module; + } + if(strpos($this->config->action->noLinkModules, ",{$module},") !== false) + { + echo $action->objectName; + } + else + { + $tab = ''; + $canView = common::hasPriv($module, $methodName); + if($action->objectType == 'meeting') $tab = $action->project ? "data-app='project'" : "data-app='my'"; + if($module == 'requirement') $module = 'story'; + echo $canView ? html::a($this->createLink($module, $methodName, $params), $action->objectName, '_self', "title='{$action->objectName}' $tab") : "$action->objectName"; + } ?> - objectID;?> - - objectID;?> - - - objectType == 'scene'){ ?> - - objectName;?> - - - - objectType == 'user' ? "account={$action->objectName}" : "id={$action->objectID}"; - $methodName = 'view'; - if($module == 'caselib') - { - $methodName = 'view'; - $module = 'caselib'; - } - if($module == 'basicmeas') - { - $module = 'measurement'; - $methodName = 'setSQL'; - $params = "id={$action->objectID}"; - } - if($action->objectType == 'api') - { - $params = "libID=0&moduelID=0&apiID={$action->objectID}"; - $methodName = 'index'; - } - if(strpos('traincourse,traincontents', $module) !== false) - { - $methodName = $module == 'traincourse' ? 'viewcourse' : 'viewchapter'; - $module = 'traincourse'; - } - if(isset($config->action->customFlows[$action->objectType])) - { - $flow = $config->action->customFlows[$action->objectType]; - $module = $flow->module; - } - if(strpos($this->config->action->noLinkModules, ",{$module},") !== false) - { - echo $action->objectName; - } - else - { - $tab = ''; - $canView = common::hasPriv($module, $methodName); - if($action->objectType == 'meeting') $tab = $action->project ? "data-app='project'" : "data-app='my'"; - if($module == 'requirement') $module = 'story'; - echo $canView ? html::a($this->createLink($module, $methodName, $params), $action->objectName, '_self', "title='{$action->objectName}' $tab") : "$action->objectName"; - } - ?> - - + + project]->name;?> diff --git a/module/common/view/mindmap.html.php b/module/common/view/mindmap.html.php index 9df19feacd..f33807f168 100644 --- a/module/common/view/mindmap.html.php +++ b/module/common/view/mindmap.html.php @@ -1,4 +1,3 @@ -getExtViewFile(__FILE__)){include $extView; return helper::cd();}?> config->datatable->moduleAlias, "$module-$method", $module); if(!isset($this->config->$module)) $this->loadModel($module); - $setting = ""; if(isset($this->config->datatable->$datatableId->$key)) { if($datatableId == 'testcaseBrowse' && $key == 'tablecols' && $this->cookie->onlyScene) diff --git a/module/testcase/config.php b/module/testcase/config.php index 226489ecde..e0c751dd96 100644 --- a/module/testcase/config.php +++ b/module/testcase/config.php @@ -236,7 +236,6 @@ $config->testcase->datatable->fieldList['actions']['width'] = '180'; $config->testcase->datatable->fieldList['actions']['required'] = 'yes'; $config->testcase->datatable->fieldList['actions']['sort'] = 'no'; - $config->testcase->search['module'] = 'testcase'; $config->testcase->search['fields']['scene'] = $lang->testcase->iScene; $config->testcase->search['params']['scene'] = array('operator' => 'belong', 'control' => 'select', 'values' => ''); diff --git a/module/testcase/control.php b/module/testcase/control.php index a6c469d5db..445ae73cba 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -147,6 +147,7 @@ class testcase extends control $this->app->loadClass('pager', $static = true); $pager = new pager($recTotal, $recPerPage, $pageID); $sort = common::appendOrder($orderBy); + /* Get test cases. */ $cases = $this->testcase->getTestCases($productID, $branch, $browseType, $browseType == 'bysearch' ? $queryID : $suiteID, $moduleID, $caseType, $sort, null); $caseIdList = array(); @@ -257,7 +258,6 @@ class testcase extends control $this->view->moduleName = $moduleID ? $tree->name : $this->lang->tree->all; $this->view->moduleID = $moduleID; $this->view->projectType = !empty($projectID) ? $this->dao->select('model')->from(TABLE_PROJECT)->where('id')->eq($projectID)->fetch('model') : ''; - // $this->view->summary = $this->testcase->summary($cases); $this->view->summary = $summary; $this->view->pager = $pager; $this->view->users = $this->user->getPairs('noletter'); @@ -586,7 +586,7 @@ class testcase extends control } $currentModuleID = $moduleID ? (int)$moduleID : (int)$this->cookie->lastCaseModule; - $currentSceneID = (int)$this->cookie->lastCaseScene; + $currentSceneID = (int)$this->cookie->lastCaseScene; if($testcaseID > 0) $currentSceneID = $scene; /* Get the status of stories are not closed. */ $modules = array(); @@ -2846,7 +2846,7 @@ class testcase extends control { $scene = $this->dao->select('*')->from(VIEW_SCENECASE)->where('id')->eq($sceneID)->andWhere('isCase')->eq(2)->fetch(); - if($confirm == 'no') return print(js::confirm(sprintf(addslashes($this->lang->testcase->confirmDeleteScene),addslashes($scene->title)), $this->createLink('testcase', 'deleteScene', "sceneID=$sceneID&confirm=yes"))); + if($confirm == 'no') return print(js::confirm(sprintf($this->lang->testcase->confirmDeleteScene,addslashes($scene->title)), $this->createLink('testcase', 'deleteScene', "sceneID=$sceneID&confirm=yes"))); $childrenCount = $this->dao->select('count(*) as count')->from(VIEW_SCENECASE)->where('parent')->eq($sceneID)->andWhere('deleted')->eq(0)->fetch('count'); if($childrenCount) @@ -3036,6 +3036,7 @@ class testcase extends control { if($_POST) { + $this->classXmind = $this->app->loadClass('xmind'); if (isset($_POST['imodule'])) $imoduleID = $_POST['imodule']; $configResult = $this->testcase->saveXmindConfig(); @@ -3067,7 +3068,7 @@ class testcase extends control $productNode = $xmlDoc->createElement('node'); $textAttr = $xmlDoc->createAttribute('TEXT'); - $textAttrValue = $xmlDoc->createTextNode($this->toText("$productName", $productID)); + $textAttrValue = $xmlDoc->createTextNode($this->classXmind->toText("$productName", $productID)); $textAttr->appendChild($textAttrValue); $productNode->appendChild($textAttr); @@ -3076,9 +3077,9 @@ class testcase extends control $sceneNodes = array(); $moduleNodes = array(); - $this->createModuleNode($xmlDoc, $context, $productNode, $moduleNodes); - $this->createSceneNode($xmlDoc, $context, $productNode, $moduleNodes, $sceneNodes); - $this->createTestcaseNode($xmlDoc, $context, $productNode, $moduleNodes, $sceneNodes); + $this->classXmind->createModuleNode($xmlDoc, $context, $productNode, $moduleNodes); + $this->classXmind->createSceneNode($xmlDoc, $context, $productNode, $moduleNodes, $sceneNodes); + $this->classXmind->createTestcaseNode($xmlDoc, $context, $productNode, $moduleNodes, $sceneNodes); $xmlStr = $xmlDoc->saveXML(); $this->fetch('file', 'sendDownHeader', array('fileName' => $productName, 'mm', $xmlStr)); @@ -3097,189 +3098,6 @@ class testcase extends control $this->display(); } - function createModuleNode($xmlDoc, $context, $productNode, &$moduleNodes) - { - $config = $context['config']; - $moduleList = $context['moduleList']; - - foreach($moduleList as $key => $name) - { - $suffix = $config['module'].':'.$key; - $moduleNode = $this->createNode($xmlDoc, $name, $suffix, array('nodeType' => 'module')); - $productNode->appendChild($moduleNode); - - $moduleNodes[$key] = $moduleNode; - } - } - - function createSceneNode($xmlDoc, $context, $productNode, &$moduleNodes, &$sceneNodes) - { - $sceneMaps = $context['sceneMaps']; - $config = $context['config']; - - $topScenes = $context['topScenes']; - - foreach($topScenes as $scene) - { - $suffix = $config['scene'].':'.$scene->sceneID; - $sceneNode = $this->createNode($xmlDoc, $scene->sceneName, $suffix, array('nodeType' => 'scene')); - - $this->createNextChildScenesNode($scene, $sceneNode, $xmlDoc, $context, $moduleNodes, $sceneNodes); - - if(isset($moduleNodes[$scene->moduleID])) - { - $moduleNode = $moduleNodes[$scene->moduleID]; - $moduleNode->appendChild($sceneNode); - } - else - { - $productNode->appendChild($sceneNode); - } - - $sceneNodes[$scene->sceneID] = $sceneNode; - } - } - - function createNextChildScenesNode($parentScene,$parentNode, $xmlDoc, $context, &$moduleNodes, &$sceneNodes) - { - $sceneMaps = $context['sceneMaps']; - $config = $context['config']; - - foreach($sceneMaps as $key => $scene) - { - if($scene->parentID != $parentScene->sceneID + CHANGEVALUE) continue; - - $suffix = $config['scene'].':'.$scene->sceneID; - $sceneNode = $this->createNode($xmlDoc, $scene->sceneName, $suffix, array('nodeType'=>'scene')); - - $this->createNextChildScenesNode($scene, $sceneNode, $xmlDoc, $context, $moduleNodes, $sceneNodes); - - $parentNode->appendChild($sceneNode); - $sceneNodes[$scene->sceneID] = $sceneNode; - } - } - - function createTestcaseNode($xmlDoc, $context, $productNode, &$moduleNodes, &$sceneNodes) - { - $caseList = $context['caseList']; - - foreach($caseList as $case) - { - if(empty($case->testcaseID)) continue; - - $parentNode = $sceneNodes[$case->sceneID]; - if(!isset($parentNode)) $parentNode = $moduleNodes[$case->moduleID]; - if(!isset($parentNode)) $parentNode = $productNode; - - $this->createTestcaseNodeImpl($case, $xmlDoc, $context, $parentNode); - } - } - - function createTestcaseNodeImpl($case, $xmlDoc, $context, $parentNode) - { - $caseList = $context['caseList']; - $stepList = $context['stepList']; - $config = $context['config']; - $suffix = $config['case'].':'.$case->testcaseID.','.$config['pri'].':'.$case->pri; - $caseNode = $this->createNode($xmlDoc, $case->name, $suffix, array('nodeType'=>'testcase')); - - $parentNode->appendChild($caseNode); - - $topStepList = $this->findTopStepListByCase($case, $stepList); - - foreach($topStepList as $step) - { - $subStepList = $this->findSubStepListByStep($step,$stepList); - - $suffix = count($subStepList) > 0 ? $config['group'] : ''; - $stepNode = $this->createNode($xmlDoc, $step->desc, $suffix, array('nodeType' => 'step')); - $caseNode->appendChild($stepNode); - - if(count($subStepList)) - { - foreach($subStepList as $sub) - { - $subNode = $this->createNode($xmlDoc, $sub->desc, '', array('nodeType'=>'substep')); - $stepNode->appendChild($subNode); - - if(!empty($sub->expect)) - { - $expectNode = $this->createNode($xmlDoc, $sub->expect, '', array('nodeType'=>'expect')); - $subNode->appendChild($expectNode); - } - } - } - - if(count($subStepList) == 0 && !empty($step->expect)) - { - $expectNode = $this->createNode($xmlDoc, $step->expect, '', array('nodeType'=>'expect')); - $stepNode->appendChild($expectNode); - } - } - } - - function findSubStepListByStep($step,$stepList) - { - $subList = array(); - foreach($stepList as $one) - { - if($one->parentID == $step->stepID) - { - $subList[] = $one; - } - } - - return $subList; - } - - public function findTopStepListByCase($case,$stepList) - { - $topList = array(); - foreach($stepList as $step) - { - if($step->parentID == '0' && $step->testcaseID == $case->testcaseID) - { - $topList[] = $step; - } - } - - return $topList; - } - - public function createNode($xmlDoc, $text, $suffix = '', $attrs=array()) - { - $node = $xmlDoc->createElement('node'); - - $textAttr = $xmlDoc->createAttribute('TEXT'); - $textAttrValue = $xmlDoc->createTextNode($this->toText($text,$suffix)); - - $textAttr->appendChild($textAttrValue); - $node->appendChild($textAttr); - - $positionAttr = $xmlDoc->createAttribute("POSITION"); - $positionAttrValue = $xmlDoc->createTextNode('right'); - - $positionAttr->appendChild($positionAttrValue); - $node->appendChild($positionAttr); - - foreach($attrs as $key => $value) - { - $attr = $xmlDoc->createAttribute($key); - $attrValue = $xmlDoc->createTextNode($value); - - $attr->appendChild($attrValue); - $node->appendChild($attr); - } - - return $node; - } - - public function toText($str, $suffix) - { - if(empty($suffix)) return $str; - return $str . '['.$suffix.']'; - } - public function getXmindConfig() { $result = $this->testcase->getXmindConfig(); @@ -3311,6 +3129,7 @@ class testcase extends control { if($_FILES) { + $this->classXmind = $this->app->loadClass('xmind'); if($_FILES['file']['size'] == 0) return print(js::alert($this->lang->testcase->errorFileNotEmpty)); $configResult = $this->testcase->saveXmindConfig(); @@ -3386,9 +3205,9 @@ class testcase extends control } $pId = $productID; - if($this->endsWith($title,"]") == true) + if($this->classXmind->endsWith($title,"]") == true) { - $tmpId = $this->getBetween($title,"[","]"); + $tmpId = $this->classXmind->getBetween($title,"[","]"); if(empty($tmpId) == false) { $projectCount = $this->dao->select('count(*) as count') @@ -3419,9 +3238,9 @@ class testcase extends control } $pId = $productID; - if($this->endsWith($title,"]") == true) + if($this->classXmind->endsWith($title,"]") == true) { - $tmpId = $this->getBetween($title,"[","]"); + $tmpId = $this->classXmind->getBetween($title,"[","]"); if(empty($tmpId) == false) { $projectCount = $this->dao->select('count(*) as count') @@ -3440,24 +3259,6 @@ class testcase extends control return array('result'=>'success','pId'=>$pId,'type'=>'json'); } - function getBetween($kw1, $mark1, $mark2) - { - $kw = $kw1; - $kw = '123' . $kw . '123'; - $st = strripos($kw, $mark1); - $ed = strripos($kw, $mark2); - - if(($st == false || $ed == false) || $st >= $ed) return 0; - - $kw = substr($kw, ($st + 1), ($ed - $st - 1)); - return $kw; - } - - function endsWith($haystack, $needle) - { - return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0; - } - public function saveXmindConfig() { $result = $this->testcase->saveXmindConfig(); diff --git a/module/testcase/lang/de.php b/module/testcase/lang/de.php index 624fe038a8..d52449dfbe 100644 --- a/module/testcase/lang/de.php +++ b/module/testcase/lang/de.php @@ -325,7 +325,7 @@ $lang->testcase->summaryScene = 'Total %d Top Scene.'; $lang->testcase->deleteScene = 'Delete Scene'; $lang->testcase->editScene = 'Edit Scene'; $lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; -$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; +$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: \"%s\"?'; $lang->testcase->sceneb = "Scene"; $lang->testcase->onlyScene = 'Only Scene'; $lang->testcase->iScene = 'Scene'; diff --git a/module/testcase/lang/en.php b/module/testcase/lang/en.php index f6b49f9653..5d85d79b93 100644 --- a/module/testcase/lang/en.php +++ b/module/testcase/lang/en.php @@ -325,7 +325,7 @@ $lang->testcase->summaryScene = 'Total %d Top Scene.'; $lang->testcase->deleteScene = 'Delete Scene'; $lang->testcase->editScene = 'Edit Scene'; $lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; -$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; +$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: \"%s\"?'; $lang->testcase->sceneb = "Scene"; $lang->testcase->onlyScene = 'Only Scene'; $lang->testcase->iScene = 'Scene'; diff --git a/module/testcase/lang/fr.php b/module/testcase/lang/fr.php index 927475a348..e85d1bef92 100644 --- a/module/testcase/lang/fr.php +++ b/module/testcase/lang/fr.php @@ -325,7 +325,7 @@ $lang->testcase->summaryScene = 'Total %d Top Scene.'; $lang->testcase->deleteScene = 'Delete Scene'; $lang->testcase->editScene = 'Edit Scene'; $lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; -$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; +$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: \"%s\"?'; $lang->testcase->sceneb = "Scene"; $lang->testcase->onlyScene = 'Only Scene'; $lang->testcase->iScene = 'Scene'; diff --git a/module/testcase/lang/vi.php b/module/testcase/lang/vi.php index c4f7ef958d..7aa8ce7c8f 100644 --- a/module/testcase/lang/vi.php +++ b/module/testcase/lang/vi.php @@ -325,7 +325,7 @@ $lang->testcase->summaryScene = 'Total %d Top Scene.'; $lang->testcase->deleteScene = 'Delete Scene'; $lang->testcase->editScene = 'Edit Scene'; $lang->testcase->hasChildren = 'This scene has sub scene or test cases. Do you want to delete them all?'; -$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: "%s"?'; +$lang->testcase->confirmDeleteScene = 'Are you sure you want to delete the scene: \"%s\"?'; $lang->testcase->sceneb = "Scene"; $lang->testcase->onlyScene = 'Only Scene'; $lang->testcase->iScene = 'Scene'; diff --git a/module/testcase/lang/zh-cn.php b/module/testcase/lang/zh-cn.php index 50acdc1af4..83f23c1950 100644 --- a/module/testcase/lang/zh-cn.php +++ b/module/testcase/lang/zh-cn.php @@ -325,7 +325,7 @@ $lang->testcase->summaryScene = '本页共 %d 个顶级场景。'; $lang->testcase->deleteScene = '删除场景'; $lang->testcase->editScene = '编辑场景'; $lang->testcase->hasChildren = '该场景有子场景或测试用例存在,要全部删除吗?'; -$lang->testcase->confirmDeleteScene = '您确定要删除场景:\“%s\”吗?'; +$lang->testcase->confirmDeleteScene = '您确定要删除场景:“%s”吗?'; $lang->testcase->sceneb = "场景"; $lang->testcase->onlyScene = '仅场景'; $lang->testcase->iScene = '所属场景'; From 9a7ade5b640d69b26b672ec01869991de9760120 Mon Sep 17 00:00:00 2001 From: zhaoke Date: Thu, 13 Apr 2023 15:22:20 +0800 Subject: [PATCH 10/14] * Fix formatting issues. --- lib/xmind/xmind.class.php | 135 +++++++++++++++++++++--- module/action/view/trash.html.php | 6 +- module/testcase/control.php | 148 +++++++++++++++++++++++++++ module/testcase/view/browse.html.php | 6 +- www/js/mindmap/css/zui.mindmap.css | 144 +++++++++++++++++--------- 5 files changed, 370 insertions(+), 69 deletions(-) diff --git a/lib/xmind/xmind.class.php b/lib/xmind/xmind.class.php index a89ad6a888..4b8260c57a 100644 --- a/lib/xmind/xmind.class.php +++ b/lib/xmind/xmind.class.php @@ -9,7 +9,18 @@ * @version $Id$ * @link http://www.zentao.net */ -class xmind{ +class xmind +{ + /** + * Create Module Node. + * + * @param DOMDocument $xmlDoc + * @param array $context + * @param DOMElement $productNode + * @param array $moduleNodes + * @access public + * @return void + */ function createModuleNode($xmlDoc, $context, $productNode, &$moduleNodes) { $config = $context['config']; @@ -17,25 +28,36 @@ class xmind{ foreach($moduleList as $key => $name) { - $suffix = $config['module'].':'.$key; - $moduleNode = $this->createNode($xmlDoc, $name, $suffix, array('nodeType' => 'module')); + $suffix = $config['module'].':'.$key; + $moduleNode = $this->createNode($xmlDoc, $name, $suffix, array('nodeType' => 'module')); $productNode->appendChild($moduleNode); $moduleNodes[$key] = $moduleNode; } } + /** + * Create Scene Node. + * + * @param DOMDocument $xmlDoc + * @param array $context + * @param DOMElement $productNode + * @param array $moduleNodes + * @param array $sceneNodes + * @access public + * @return void + */ function createSceneNode($xmlDoc, $context, $productNode, &$moduleNodes, &$sceneNodes) { - $sceneMaps = $context['sceneMaps']; - $config = $context['config']; + $sceneMaps = $context['sceneMaps']; + $config = $context['config']; - $topScenes = $context['topScenes']; + $topScenes = $context['topScenes']; foreach($topScenes as $scene) { $suffix = $config['scene'].':'.$scene->sceneID; - $sceneNode = $this->createNode($xmlDoc, $scene->sceneName, $suffix, array('nodeType' => 'scene')); + $sceneNode = $this->createNode($xmlDoc, $scene->sceneName, $suffix, array('nodeType' => 'scene')); $this->createNextChildScenesNode($scene, $sceneNode, $xmlDoc, $context, $moduleNodes, $sceneNodes); @@ -53,10 +75,22 @@ class xmind{ } } + /** + * Create Next Child Scene Node. + * + * @param object $parentScene + * @param object $parentNode + * @param DOMDocument $xmlDoc + * @param array $context + * @param array $moduleNodes + * @param array $sceneNodes + * @access public + * @return void + */ function createNextChildScenesNode($parentScene,$parentNode, $xmlDoc, $context, &$moduleNodes, &$sceneNodes) { - $sceneMaps = $context['sceneMaps']; - $config = $context['config']; + $sceneMaps = $context['sceneMaps']; + $config = $context['config']; foreach($sceneMaps as $key => $scene) { @@ -72,6 +106,17 @@ class xmind{ } } + /** + * Create Test Case Node. + * + * @param DOMDocument $xmlDoc + * @param array $context + * @param object $productNode + * @param array $moduleNodes + * @param array $sceneNodes + * @access public + * @return void + */ function createTestcaseNode($xmlDoc, $context, $productNode, &$moduleNodes, &$sceneNodes) { $caseList = $context['caseList']; @@ -84,11 +129,21 @@ class xmind{ if(!isset($parentNode)) $parentNode = $moduleNodes[$case->moduleID]; if(!isset($parentNode)) $parentNode = $productNode; - $this->createTestcaseNodeImpl($case, $xmlDoc, $context, $parentNode); + $this->createOneTestcaseNode($case, $xmlDoc, $context, $parentNode); } } - function createTestcaseNodeImpl($case, $xmlDoc, $context, $parentNode) + /** + * Create One Test Case Node. + * + * @param object $case + * @param DOMDocument $xmlDoc + * @param array $context + * @param object $parentNode + * @access public + * @return void + */ + function createOneTestcaseNode($case, $xmlDoc, $context, $parentNode) { $caseList = $context['caseList']; $stepList = $context['stepList']; @@ -131,6 +186,14 @@ class xmind{ } } + /** + * Find Substep List By Step. + * + * @param object $step + * @param array $stepList + * @access public + * @return array + */ function findSubStepListByStep($step,$stepList) { $subList = array(); @@ -145,6 +208,14 @@ class xmind{ return $subList; } + /** + * Find Top Step List By Case. + * + * @param object $case + * @param array $stepList + * @access public + * @return array + */ public function findTopStepListByCase($case,$stepList) { $topList = array(); @@ -159,18 +230,28 @@ class xmind{ return $topList; } + /** + * Create Xmind Node. + * + * @param DOMDocument $xmlDoc + * @param string $text + * @param string $suffix + * @param array $attrs + * @access public + * @return object + */ public function createNode($xmlDoc, $text, $suffix = '', $attrs=array()) { $node = $xmlDoc->createElement('node'); - $textAttr = $xmlDoc->createAttribute('TEXT'); - $textAttrValue = $xmlDoc->createTextNode($this->toText($text,$suffix)); + $textAttr = $xmlDoc->createAttribute('TEXT'); + $textAttrValue = $xmlDoc->createTextNode($this->toText($text,$suffix)); $textAttr->appendChild($textAttrValue); $node->appendChild($textAttr); - $positionAttr = $xmlDoc->createAttribute("POSITION"); - $positionAttrValue = $xmlDoc->createTextNode('right'); + $positionAttr = $xmlDoc->createAttribute("POSITION"); + $positionAttrValue = $xmlDoc->createTextNode('right'); $positionAttr->appendChild($positionAttrValue); $node->appendChild($positionAttr); @@ -187,12 +268,28 @@ class xmind{ return $node; } + /** + * Add suffix before string. + * + * @param string $str + * @param string $suffix + * @access public + * @return string + */ public function toText($str, $suffix) { if(empty($suffix)) return $str; return $str . '['.$suffix.']'; } + /** + * Get Substring Between Mark1 And Mark2 From Kw. + * + * @param string $str + * @param string $suffix + * @access public + * @return string + */ function getBetween($kw1, $mark1, $mark2) { $kw = $kw1; @@ -206,6 +303,14 @@ class xmind{ return $kw; } + /** + * Judgment ends with a string. + * + * @param string $haystack + * @param string $needle + * @access public + * @return string + */ function endsWith($haystack, $needle) { return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0; diff --git a/module/action/view/trash.html.php b/module/action/view/trash.html.php index 48510b55e4..0ba4a19e05 100755 --- a/module/action/view/trash.html.php +++ b/module/action/view/trash.html.php @@ -102,11 +102,11 @@ action->objectTypes, $action->objectType, '');?> objectID;?> - objectType == 'scene'){ ?> + objectType == 'scene'): ?> objectName;?> - + objectType == 'user' ? "account={$action->objectName}" : "id={$action->objectID}"; @@ -151,7 +151,7 @@ } ?> - + project]->name;?> diff --git a/module/testcase/control.php b/module/testcase/control.php index 445ae73cba..7cf023d88c 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -2641,6 +2641,24 @@ class testcase extends control $this->display(); } + /** + * Ajax Get module scenes. + * + * @param int $productID + * @param int $branch + * @param int $moduleID + * @param int $stype + * @param int $storyID + * @param bool $onlyOption + * @param string $status + * @param string $limit + * @param string $type + * @param int $hasParent + * @param string $number + * @param int $currentScene + * @access public + * @return void + */ public function ajaxGetModuleScenes($productID, $branch = 0, $moduleID = 0, $stype = 1, $storyID = 0, $onlyOption = 'false', $status = '', $limit = 0, $type = 'full', $hasParent = 1, $number = '', $currentScene = 0) { $optionMenu = $this->testcase->getSceneMenu($productID, $moduleID, 'case', 0, $branch, $currentScene); @@ -2649,6 +2667,21 @@ class testcase extends control die($output); } + /** + * Ajax Get Option Menu. + * + * @param int $rootID + * @param string $viewType + * @param int $branch + * @param int $rootModuleID + * @param string $returnType + * @param string $fieldID + * @param bool $needManage + * @param string $extra + * @param int $currentModuleID + * @access public + * @return void + */ public function ajaxGetOptionMenu($rootID, $viewType = 'story', $branch = 0, $rootModuleID = 0, $returnType = 'html', $fieldID = '', $needManage = false, $extra = '', $currentModuleID = 0) { $this->loadModel('tree'); @@ -2708,6 +2741,24 @@ class testcase extends control if($returnType == 'json') die(json_encode($optionMenu)); } + /** + * Ajax Get scenes. + * + * @param int $productID + * @param int $branch + * @param int $moduleID + * @param int $stype + * @param int $storyID + * @param bool $onlyOption + * @param string $status + * @param string $limit + * @param string $type + * @param int $hasParent + * @param string $number + * @param int $currentScene + * @access public + * @return void + */ public function ajaxGetScenesForBC($productID, $branch = 0, $moduleID = 0, $stype = 1, $storyID = 0, $onlyOption = 'false', $status = '', $limit = 0, $type = 'full', $hasParent = 1, $number = '', $currentScene = 0) { $optionMenu = $this->testcase->getSceneMenu($productID, $moduleID, 'case', 0, $branch, $currentScene); @@ -2725,6 +2776,13 @@ class testcase extends control die($output); } + /** + * Batch Change Scene. + * + * @param int $sceneId + * @access public + * @return void + */ public function batchChangeScene($sceneId) { if($this->post->caseIDList) @@ -2757,6 +2815,12 @@ class testcase extends control echo js::locate($this->session->caseList, 'parent'); } + /** + * Change Scene. + * + * @access public + * @return void + */ public function changeScene() { $now = helper::now(); @@ -2842,6 +2906,14 @@ class testcase extends control } } + /** + * Delete Scene. + * + * @param int $sceneId + * @param string $confirm + * @access public + * @return void + */ public function deleteScene($sceneID, $confirm = 'no') { $scene = $this->dao->select('*')->from(VIEW_SCENECASE)->where('id')->eq($sceneID)->andWhere('isCase')->eq(2)->fetch(); @@ -2875,6 +2947,14 @@ class testcase extends control echo js::reload('parent'); } + /** + * Edit Scene. + * + * @param int $sceneId + * @param int $executionID + * @access public + * @return void + */ public function editScene($sceneID, $executionID = 0) { $this->loadModel('story'); @@ -3032,6 +3112,16 @@ class testcase extends control ->exec(); } } + + /** + * Export Xmind. + * + * @param int $productID + * @param int $moduleID + * @param int $branch + * @access public + * @return void + */ public function exportXMind($productID, $moduleID, $branch) { if($_POST) @@ -3098,12 +3188,24 @@ class testcase extends control $this->display(); } + /** + * Get Xmind Config. + * + * @access public + * @return void + */ public function getXmindConfig() { $result = $this->testcase->getXmindConfig(); $this->send($result); } + /** + * Get Xmind Content. + * + * @access public + * @return void + */ public function getXmindImport() { $folder = $this->session->xmindImport; @@ -3125,6 +3227,14 @@ class testcase extends control } } + /** + * Import Xmind. + * + * @param int $productID + * @param int $branch + * @access public + * @return void + */ public function importXMind($productID, $branch) { if($_FILES) @@ -3194,6 +3304,15 @@ class testcase extends control $this->display(); } + /** + * Fetch By Xml. + * + * @param string $extractFolder + * @param int $productID + * @param int $branch + * @access public + * @return void + */ function fetchByXML($extractFolder, $productID, $branch) { $filePath = $extractFolder."/content.xml"; @@ -3226,6 +3345,15 @@ class testcase extends control return array('result'=>'success','pId'=>$pId, 'type'=>'xml'); } + /** + * Fetch By Json. + * + * @param string $extractFolder + * @param int $productID + * @param int $branch + * @access public + * @return void + */ function fetchByJSON($extractFolder, $productID, $branch) { $filePath = $extractFolder."/content.json"; @@ -3259,12 +3387,24 @@ class testcase extends control return array('result'=>'success','pId'=>$pId,'type'=>'json'); } + /** + * Save Xmind Config. + * + * @access public + * @return void + */ public function saveXmindConfig() { $result = $this->testcase->saveXmindConfig(); $this->send($result); } + /** + * Save Imported Xmind. + * + * @access public + * @return void + */ public function saveXmindImport() { if(!empty($_POST)) @@ -3276,6 +3416,14 @@ class testcase extends control $this->send(array('result' => 'fail', 'message' => $this->lang->errorSaveXmind)); } + /** + * Show Imported Xmind. + * + * @param int $productID + * @param int $branch + * @access public + * @return void + */ public function showXMindImport($productID,$branch) { $product = $this->product->getById($productID); diff --git a/module/testcase/view/browse.html.php b/module/testcase/view/browse.html.php index 7dece08a85..511f8c8c15 100644 --- a/module/testcase/view/browse.html.php +++ b/module/testcase/view/browse.html.php @@ -62,13 +62,13 @@ js::set('langNormal', $lang->testcase->normal);

    - cookie->onlyScene){ ?> + cookie->onlyScene): ?> testcase->noScene;?> createLink('testcase', 'createScene', "productID=$productID&branch=$branch&moduleID=$initModule"), " " . $lang->testcase->newScene, '', "class='btn btn-info' data-app='{$this->app->tab}'");?> - + testcase->noCase;?> @@ -78,7 +78,7 @@ js::set('langNormal', $lang->testcase->normal); createLink('testsuite', 'linkCase', "suiteID=$param"), " " . $lang->testsuite->linkCase, '', "class='btn btn-info' data-app='{$this->app->tab}'");?> - +

    diff --git a/www/js/mindmap/css/zui.mindmap.css b/www/js/mindmap/css/zui.mindmap.css index 8504439d5b..887ca29f6c 100644 --- a/www/js/mindmap/css/zui.mindmap.css +++ b/www/js/mindmap/css/zui.mindmap.css @@ -5,12 +5,14 @@ * Copyright (c) 2022 cnezsoft.com; Licensed MIT */ -.mindmap { +.mindmap + { position: relative; z-index: 0; overflow: hidden; } -.mindmap-container { +.mindmap-container + { position: absolute; top: 0; left: 0; @@ -20,17 +22,20 @@ background: #fff; border: 1px solid #e5e5e5; } -.mindmap-container.dragging { +.mindmap-container.dragging + { cursor: pointer; } -.mindmap-bg { +.mindmap-bg + { position: absolute; top: 0; left: 0; z-index: 5; max-width: inherit; } -.mindmap-desktop { +.mindmap-desktop + { position: absolute; top: 0; left: 0; @@ -41,11 +46,13 @@ -webkit-user-select: none; -moz-user-select: none; } -.mindmap-node { +.mindmap-node + { position: absolute; border-radius: 24px; } -.mindmap-node > .wrapper { +.mindmap-node > .wrapper + { min-height: 28px; background: #ebf2f9; border: 4px solid #aaa; @@ -54,22 +61,27 @@ -o-transition: all .2s cubic-bezier(.175, .885, .32, 1); transition: all .2s cubic-bezier(.175, .885, .32, 1); } -.mindmap-node > .wrapper > .text { +.mindmap-node > .wrapper > .text + { padding: 8px 12px; font-size: 14px; font-weight: bold; cursor: default; } -.mindmap-node > .wrapper > .text * { +.mindmap-node > .wrapper > .text * + { display: inline; } -.mindmap-node > .wrapper > .text br { +.mindmap-node > .wrapper > .text br + { display: none; } -.mindmap-node > .wrapper > .text:focus { +.mindmap-node > .wrapper > .text:focus + { outline: none; } -.mindmap-node > .wrapper > .text:empty:not(:focus) { +.mindmap-node > .wrapper > .text:empty:not(:focus) + { position: relative; top: 7px; min-width: 10px; @@ -77,97 +89,117 @@ background: #508dee; border-radius: 50%; } -.mindmap-node > .wrapper > .caption { +.mindmap-node > .wrapper > .caption + { display: none; } -.mindmap-node:hover { +.mindmap-node:hover + { -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .4); box-shadow: 0 0 5px rgba(0, 0, 0, .4); } -.mindmap-node:hover > .wrapper { +.mindmap-node:hover > .wrapper + { border-color: #999; } -.mindmap-node[data-type="root"] > .wrapper { +.mindmap-node[data-type="root"] > .wrapper + { min-width: 45px; min-height: 45px; } -.mindmap-node[data-type="root"] > .wrapper > .text { +.mindmap-node[data-type="root"] > .wrapper > .text + { text-align: center; } -.mindmap-node[data-type="root"] > .wrapper > .text:empty:not(:focus) { +.mindmap-node[data-type="root"] > .wrapper > .text:empty:not(:focus) + { top: 0; min-width: 37px; min-height: 37px; background: none; border-radius: 50%; } -.mindmap-node[data-type="sub"] { +.mindmap-node[data-type="sub"] + { border-radius: 6px; } -.mindmap-node[data-type="sub"] > .wrapper { +.mindmap-node[data-type="sub"] > .wrapper + { background: #fff; border-width: 2px; border-radius: 5px; } -.mindmap-node[data-type="sub"] > .wrapper > .text { +.mindmap-node[data-type="sub"] > .wrapper > .text + { padding: 3px 5px; font-size: 13px; font-weight: normal; } -.mindmap-node[data-type="sub"] > .wrapper > .text:empty:not(:focus) { +.mindmap-node[data-type="sub"] > .wrapper > .text:empty:not(:focus) + { background: none; } -.mindmap-node[data-type="node"] { +.mindmap-node[data-type="node"] + { border-radius: 4px; } -.mindmap-node[data-type="node"] > .wrapper { +.mindmap-node[data-type="node"] > .wrapper + { background: transparent; border: 2px solid transparent; border-radius: 3px; } -.mindmap-node[data-type="node"] > .wrapper > .text { +.mindmap-node[data-type="node"] > .wrapper > .text + { padding: 3px 3px; font-size: 13px; font-weight: normal; } -.mindmap-node[data-type="node"]:hover > .wrapper { +.mindmap-node[data-type="node"]:hover > .wrapper + { color: #353535; background: #fff; border-color: #999; } .mindmap-node.active, .mindmap-node.active:hover, -.mindmap-node.drag-shadow { +.mindmap-node.drag-shadow + { z-index: 900; -webkit-box-shadow: none; box-shadow: none; } .mindmap-node.active > .wrapper, .mindmap-node.active:hover > .wrapper, -.mindmap-node.drag-shadow > .wrapper { +.mindmap-node.drag-shadow > .wrapper + { color: #fff; background: #508dee; border-color: #2a74ea; } .mindmap-node.focus, .mindmap-node.focus:hover, -.mindmap-node.drop-to { +.mindmap-node.drop-to + { -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(20, 92, 205, .6); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(20, 92, 205, .6); } .mindmap-node.focus > .wrapper, .mindmap-node.focus:hover > .wrapper, -.mindmap-node.drop-to > .wrapper { +.mindmap-node.drop-to > .wrapper + { color: #353535; background: #fff; border-color: #145ccd; } .mindmap-node.focus > .wrapper > .text, .mindmap-node.focus:hover > .wrapper > .text, -.mindmap-node.drop-to > .wrapper > .text { +.mindmap-node.drop-to > .wrapper > .text + { cursor: text; } -.mindmap-node .btn-toggle { +.mindmap-node .btn-toggle + { position: absolute; top: 50%; right: -14px; @@ -194,37 +226,46 @@ transition-property: visibility, opacity, border-color, box-shadow; transition-property: visibility, opacity, border-color, box-shadow, -webkit-box-shadow; } -.mindmap-node .btn-toggle:hover { +.mindmap-node .btn-toggle:hover + { background: #3280fc; border-color: rgba(0, 0, 0, .1); -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .4); box-shadow: 0 0 5px rgba(0, 0, 0, .4); } -.mindmap-node .btn-toggle:before { +.mindmap-node .btn-toggle:before + { content: '-'; } -.mindmap-node.mindmap-side-left .btn-toggle { +.mindmap-node.mindmap-side-left .btn-toggle + { right: auto; left: -14px; } -.mindmap-node.mindmap-collapesed .btn-toggle:before { +.mindmap-node.mindmap-collapesed .btn-toggle:before + { content: '+'; } -.mindmap-node[data-type="root"] .btn-toggle { +.mindmap-node[data-type="root"] .btn-toggle + { display: none!important; } -.mindmap-show-toggle-btn .mindmap-has-child .btn-toggle { +.mindmap-show-toggle-btn .mindmap-has-child .btn-toggle + { display: block; } -.mindmap-show-toggle-btn .mindmap-node.focus .btn-toggle { +.mindmap-show-toggle-btn .mindmap-node.focus .btn-toggle + { display: none; } .mindmap-show-toggle-btn .mindmap-node.mindmap-collapesed .btn-toggle, -.mindmap-show-toggle-btn .mindmap-node:hover .btn-toggle { +.mindmap-show-toggle-btn .mindmap-node:hover .btn-toggle + { visibility: visible; opacity: 1; } -.mindmap-shadow { +.mindmap-shadow + { position: absolute; z-index: 1000; width: 100%; @@ -234,31 +275,38 @@ transition: all .4s cubic-bezier(.175, .885, .32, 1); } .mindmap-shadow.shadow-left, -.mindmap-shadow.shadow-right { +.mindmap-shadow.shadow-right + { top: 0; width: 20px; } -.mindmap-shadow.shadow-left { +.mindmap-shadow.shadow-left + { left: -20px; } -.mindmap-shadow.shadow-right { +.mindmap-shadow.shadow-right + { right: -20px; } .mindmap-shadow.shadow-top, -.mindmap-shadow.shadow-bottom { +.mindmap-shadow.shadow-bottom + { left: 0; height: 20px; } -.mindmap-shadow.shadow-top { +.mindmap-shadow.shadow-top + { top: -20px; } -.mindmap-shadow.shadow-bottom { +.mindmap-shadow.shadow-bottom + { bottom: -20px; } .shadow-left > .shadow-left, .shadow-right > .shadow-right, .shadow-top > .shadow-top, -.shadow-bottom > .shadow-bottom { +.shadow-bottom > .shadow-bottom + { -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, .8); box-shadow: 0 0 8px rgba(0, 0, 0, .8); } From d94b0041d79009db553598b631d09d1514d9bc6f Mon Sep 17 00:00:00 2001 From: zhaoke Date: Thu, 13 Apr 2023 15:34:13 +0800 Subject: [PATCH 11/14] * Format css file. --- module/testcase/css/showxmindimport.css | 51 ++++++++++++++++--------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/module/testcase/css/showxmindimport.css b/module/testcase/css/showxmindimport.css index 717d1e32e2..23bed92212 100644 --- a/module/testcase/css/showxmindimport.css +++ b/module/testcase/css/showxmindimport.css @@ -1,13 +1,16 @@ -.mindmap-container { +.mindmap-container +{ border: none !important; } -.mindmap-node { +.mindmap-node +{ display: flex; align-items: center; } -.pri-level,.testcase-pri-root a { +.pri-level,.testcase-pri-root a +{ border-width: 2px; border-style: solid; border-radius: 50%; @@ -18,35 +21,42 @@ display: block; } -.pri-1 { +.pri-1 +{ color: #d50000; border-color: #d50000; } -.pri-2 { +.pri-2 +{ color: #ff9800; border-color: #ff9800; } -.pri-3 { +.pri-3 +{ color: #2098ee; border-color: #2098ee; } -.pri-4 { +.pri-4 +{ color: #009688; border-color: #009688; } -.pri-empty { +.pri-empty +{ background-color: lightgray; } -.testcase-pri-root a { +.testcase-pri-root a +{ margin: 8px; } -.testcase-pri-root { +.testcase-pri-root +{ display: flex; position: absolute; padding-left: 10px; @@ -54,39 +64,46 @@ z-index: 20; } -.effect{ +.effect +{ z-index: 15; box-shadow:0px 1px 4px rgba(0,0,0,0.8),0px 0px 40px rgba(0,0,0,0.1) inset; background-color: white; } -.effect a { +.effect a +{ width: 24px; height: 24px; line-height: 20px; } -.effect:before,.effect:after{ +.effect:before,.effect:after +{ content:""; border-radius:100px/10px; box-shadow:0 0px 20px rgba(0,0,0,0.8); } -.scene-indicator { +.scene-indicator +{ margin-left: 5px; color: lightgray; } -.scene-indicator-yes { +.scene-indicator-yes +{ color: darkgreen; } -.suffix { +.suffix +{ color: darkred; display: none; } -#productName { +#productName +{ height: 32px; width: 100%; padding-left: 10px; From 3f3e644e70e99032972d14698adfccb07bc38c55 Mon Sep 17 00:00:00 2001 From: zhaoke Date: Thu, 13 Apr 2023 16:18:42 +0800 Subject: [PATCH 12/14] * Add commit to testcase model. --- module/testcase/js/browse.js | 4 +- module/testcase/model.php | 236 ++++++++++++++++++++++++++++++++++- 2 files changed, 237 insertions(+), 3 deletions(-) diff --git a/module/testcase/js/browse.js b/module/testcase/js/browse.js index 0e33858ee6..84aa8406b6 100644 --- a/module/testcase/js/browse.js +++ b/module/testcase/js/browse.js @@ -576,7 +576,7 @@ $(function() $('#caseTableList').on('click', '.row-case', function(e) { - var $row = $(this); + var $row = $(this); $row.toggleClass('checked'); updateChildrenCheckboxes($row); @@ -632,7 +632,7 @@ $(function() var selectedCaseNum = 0; for(var i=0; iget(); } + /** + * Build Operate Browse Scene Menu. + * + * @param object $scene + * @access public + * @return string + */ public function buildOperateBrowseSceneMenu($scene) { $canBeChanged = common::canBeChanged('case', $scene); @@ -3162,6 +3188,18 @@ class testcaseModel extends model return $menu; } + /** + * Search Form Add Scene. + * + * @param int $productID + * @param array $products + * @param int $queryID + * @param string $actionURL + * @param int $projectID + * @param int $moduleID + * @access public + * @return void + */ public function buildSearchFormAddScene($productID, $products, $queryID, $actionURL, $projectID = 0,$moduleID = 0) { $product = ($this->app->tab == 'project' and empty($productID)) ? $products : array($productID => $products[$productID]) + array('all' => $this->lang->testcase->allProduct); @@ -3200,6 +3238,16 @@ class testcaseModel extends model $this->loadModel('search')->setSearchParams($this->config->testcase->search); } + /** + * Build Tree Array. + * + * @param array $treeMenu + * @param array $scenes + * @param object $scene + * @param string $sceneName + * @access public + * @return void + */ public function buildTreeArray(& $treeMenu, $scenes, $scene, $sceneName = '/') { $parentScenes = explode(',', $scene->path); @@ -3243,7 +3291,7 @@ class testcaseModel extends model * Create scene. * * @access public - * @return void + * @return array */ public function createScene() { @@ -3300,6 +3348,13 @@ class testcaseModel extends model } } + /** + * Get All Children Id. + * + * @param int $sceneID + * @access public + * @return object + */ public function getAllChildId($sceneID) { if($sceneID == 0) return array(); @@ -3313,6 +3368,18 @@ class testcaseModel extends model ->fetchPairs(); } + /** + * Search Cases Has Scene. + * + * @param int $productID + * @param int $queryID + * @param string $orderBy + * @param object $pager + * @param int $branch + * @param string $auto + * @access public + * @return object + */ public function getBySearchHasScene($productID, $queryID, $orderBy, $pager = null, $branch = 0, $auto = 'no') { if($queryID) @@ -3459,6 +3526,13 @@ class testcaseModel extends model return $queryFunction($modules, $type, $objectIdList, $branch)->orderBy($orderBy)->page($pager)->fetchAll('id'); } + /** + * Search Cases Has Scene. + * + * @param int $viewID + * @access public + * @return string + */ public function getParents($viewID) { if($viewID == 0) return '/'; @@ -3478,6 +3552,19 @@ class testcaseModel extends model return substr($scenePath,34); } + /** + * Get Scene Menu. + * + * @param int $rootID + * @param int $moduleID + * @param string $type + * @param int $startScene + * @param int $branch + * @param int $currentScene + * @param bool $emptyMenu + * @access public + * @return array + */ public function getSceneMenu($rootID, $moduleID, $type = '', $startScene = 0, $branch = 0, $currentScene = 0, $emptyMenu = false) { if(empty($branch)) $branch = 0; @@ -3543,6 +3630,15 @@ class testcaseModel extends model return $lastMenu; } + /** + * Get Scene Name. + * + * @param array $moduleIdList + * @param bool $allPath + * @param bool $branchPath + * @access public + * @return array + */ public function getScenesName($moduleIdList, $allPath = true, $branchPath = false) { if(!$allPath) return $this->dao->select('id, title')->from(VIEW_SCENECASE)->where('id')->in($moduleIdList)->andWhere('deleted')->eq(0)->fetchPairs('id', 'title'); @@ -3577,6 +3673,20 @@ class testcaseModel extends model return $modulePairs; } + /** + * Get Cases Which Has Scene. + * + * @param int $productID + * @param int $branch + * @param string $browseType + * @param int $queryID + * @param int $moduleID + * @param string $sort + * @param object $pager + * @param string $auto + * @access public + * @return array + */ public function getTestCaseHasScene($productID, $branch, $browseType, $queryID, $moduleID, $sort, $pager, $auto = 'no') { /* Set modules and browse type. */ @@ -3629,10 +3739,29 @@ class testcaseModel extends model return $cases; } + /** + * Substr string. + * + * @param string $text + * @param int $length + * @access public + * @return string + */ public function istrcut($text, $length) { return (mb_strlen($text, 'utf8') > $length) ? mb_substr($text, 0, $length, 'utf8').'...' : $text; } + + /** + * Print Table Head. + * + * @param object $col + * @param string $orderBy + * @param string $vars + * @param bool $checkBox + * @access public + * @return string + */ public function printHead($col, $orderBy, $vars, $checkBox = true) { $id = $col->id; @@ -3695,6 +3824,13 @@ class testcaseModel extends model } } + /** + * Update Scene. + * + * @param int $sceneID + * @access public + * @return string + */ public function updateScene($sceneID) { /* Get original data. */ @@ -3808,6 +3944,13 @@ class testcaseModel extends model return array('status' => 'updated', 'id' => $sceneID); } + /** + * Get Xmind File Content. + * + * @param string $fileName + * @access public + * @return string + */ public function getXmindImport($fileName) { $xmlNode = simplexml_load_file($fileName); @@ -3816,6 +3959,12 @@ class testcaseModel extends model return json_encode($testData); } + /** + * Save Xmind File Content To Database. + * + * @access public + * @return array + */ public function saveXmindImport() { $this->dao->begin(); @@ -3859,6 +4008,14 @@ class testcaseModel extends model return array("result"=>"success","message"=>1); } + /** + * Save Test Case. + * + * @param array $testcaseData + * @param array $sceneIds + * @access public + * @return array + */ public function saveTestcase($testcaseData, $sceneIds) { $tmpPId = $testcaseData["tmpPId"]; @@ -3992,6 +4149,14 @@ class testcaseModel extends model return array('result' => 'success', 'message' => 1,'testcaseID'=>$testcaseID); } + /** + * Save Scene. + * + * @param array $sceneData + * @param array $sceneIds + * @access public + * @return array + */ public function saveScene($sceneData, $sceneIds) { $id = isset($sceneData["id"]) ? $sceneData["id"] : -1; @@ -4066,6 +4231,15 @@ class testcaseModel extends model return array('result' => 'success', 'message' => 1,"sceneID"=>$sceneID); } + /** + * Get Export Data. + * + * @param int $productID + * @param int $moduleID + * @param int $branch + * @access public + * @return array + */ public function getXmindExport($productID, $moduleID, $branch) { $caseList = $this->getCaseByProductAndModule($productID, $moduleID); @@ -4085,6 +4259,15 @@ class testcaseModel extends model ); } + /** + * Get Module By Product. + * + * @param int $productID + * @param int $moduleID + * @param int $branch + * @access public + * @return array + */ function getModuleByProductAndModel($productID, $moduleID, $branch) { $moduleList = array(); @@ -4105,6 +4288,14 @@ class testcaseModel extends model return $moduleList; } + /** + * Get Case By Product And Module. + * + * @param int $productID + * @param int $moduleID + * @access public + * @return array + */ function getCaseByProductAndModule($productID, $moduleID) { $fields = "t2.id as productID," @@ -4129,6 +4320,14 @@ class testcaseModel extends model return $caseList; } + /** + * Get Step By Product And Module. + * + * @param int $productID + * @param int $moduleID + * @access public + * @return array + */ function getStepByProductAndModule($productID, $moduleID) { $fields = "t1.id as testcaseID," @@ -4149,6 +4348,14 @@ class testcaseModel extends model return $stepList; } + /** + * Get Scene By Product And Module. + * + * @param int $productID + * @param int $moduleID + * @access public + * @return array + */ function getSceneByProductAndModule($productID, $moduleID) { $sceneList = $this->dao->select('id as sceneID, title as sceneName, path, parent as parentID, product as productID, module as moduleID') @@ -4170,11 +4377,24 @@ class testcaseModel extends model return array('sceneMaps'=>$sceneMaps,'topScenes'=>$topScenes); } + /** + * Check Config. + * + * @param string $str + * @access public + * @return bool + */ function checkConfigValue($str) { return preg_match("/^[a-zA-Z]{1,10}$/",$str); } + /** + * Save Xmind Config. + * + * @access public + * @return array + */ function saveXmindConfig() { $configList = array(); @@ -4255,6 +4475,12 @@ class testcaseModel extends model return array("result" => "success", "message" => 1); } + /** + * Get Xmind Config. + * + * @access public + * @return array + */ function getXmindConfig() { $configItems = $this->dao->select("`key`,value")->from(TABLE_CONFIG) @@ -4275,6 +4501,14 @@ class testcaseModel extends model return $config; } + /** + * Convert Xml To Array. + * + * @param object $xml + * @param array $options + * @access public + * @return array + */ function xmlToArray($xml, $options = array()) { $defaults = array( From e6faec7ba6f2465bdda07a2280aa241ab3e4b145 Mon Sep 17 00:00:00 2001 From: zhaoke Date: Thu, 13 Apr 2023 16:34:05 +0800 Subject: [PATCH 13/14] * Update commits. --- lib/xmind/xmind.class.php | 18 ++++++------- module/testcase/control.php | 30 ++++++++++----------- module/testcase/model.php | 52 ++++++++++++++++++------------------- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/lib/xmind/xmind.class.php b/lib/xmind/xmind.class.php index 4b8260c57a..e967310d67 100644 --- a/lib/xmind/xmind.class.php +++ b/lib/xmind/xmind.class.php @@ -12,7 +12,7 @@ class xmind { /** - * Create Module Node. + * Create module node. * * @param DOMDocument $xmlDoc * @param array $context @@ -37,7 +37,7 @@ class xmind } /** - * Create Scene Node. + * Create scene node. * * @param DOMDocument $xmlDoc * @param array $context @@ -76,7 +76,7 @@ class xmind } /** - * Create Next Child Scene Node. + * Create next child scene node. * * @param object $parentScene * @param object $parentNode @@ -107,7 +107,7 @@ class xmind } /** - * Create Test Case Node. + * Create test case node. * * @param DOMDocument $xmlDoc * @param array $context @@ -134,7 +134,7 @@ class xmind } /** - * Create One Test Case Node. + * Create one test case node. * * @param object $case * @param DOMDocument $xmlDoc @@ -187,7 +187,7 @@ class xmind } /** - * Find Substep List By Step. + * Find substep list by step. * * @param object $step * @param array $stepList @@ -209,7 +209,7 @@ class xmind } /** - * Find Top Step List By Case. + * Find top step list by case. * * @param object $case * @param array $stepList @@ -231,7 +231,7 @@ class xmind } /** - * Create Xmind Node. + * Create xmind node. * * @param DOMDocument $xmlDoc * @param string $text @@ -283,7 +283,7 @@ class xmind } /** - * Get Substring Between Mark1 And Mark2 From Kw. + * Get substring between mark1 and mark2 from kw. * * @param string $str * @param string $suffix diff --git a/module/testcase/control.php b/module/testcase/control.php index 7cf023d88c..fbabd18da0 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -2668,7 +2668,7 @@ class testcase extends control } /** - * Ajax Get Option Menu. + * Ajax get option menu. * * @param int $rootID * @param string $viewType @@ -2742,7 +2742,7 @@ class testcase extends control } /** - * Ajax Get scenes. + * Ajax get scenes. * * @param int $productID * @param int $branch @@ -2777,7 +2777,7 @@ class testcase extends control } /** - * Batch Change Scene. + * Batch change scene. * * @param int $sceneId * @access public @@ -2816,7 +2816,7 @@ class testcase extends control } /** - * Change Scene. + * Change scene. * * @access public * @return void @@ -2907,7 +2907,7 @@ class testcase extends control } /** - * Delete Scene. + * Delete scene. * * @param int $sceneId * @param string $confirm @@ -2948,7 +2948,7 @@ class testcase extends control } /** - * Edit Scene. + * Edit scene. * * @param int $sceneId * @param int $executionID @@ -3114,7 +3114,7 @@ class testcase extends control } /** - * Export Xmind. + * Export xmind. * * @param int $productID * @param int $moduleID @@ -3189,7 +3189,7 @@ class testcase extends control } /** - * Get Xmind Config. + * Get xmind config. * * @access public * @return void @@ -3201,7 +3201,7 @@ class testcase extends control } /** - * Get Xmind Content. + * Get xmind content. * * @access public * @return void @@ -3228,7 +3228,7 @@ class testcase extends control } /** - * Import Xmind. + * Import xmind. * * @param int $productID * @param int $branch @@ -3305,7 +3305,7 @@ class testcase extends control } /** - * Fetch By Xml. + * Fetch by xml. * * @param string $extractFolder * @param int $productID @@ -3346,7 +3346,7 @@ class testcase extends control } /** - * Fetch By Json. + * Fetch by json. * * @param string $extractFolder * @param int $productID @@ -3388,7 +3388,7 @@ class testcase extends control } /** - * Save Xmind Config. + * Save xmind config. * * @access public * @return void @@ -3400,7 +3400,7 @@ class testcase extends control } /** - * Save Imported Xmind. + * Save imported xmind. * * @access public * @return void @@ -3417,7 +3417,7 @@ class testcase extends control } /** - * Show Imported Xmind. + * Show imported xmind. * * @param int $productID * @param int $branch diff --git a/module/testcase/model.php b/module/testcase/model.php index 902a96fa93..d90f8d37fb 100644 --- a/module/testcase/model.php +++ b/module/testcase/model.php @@ -2871,7 +2871,7 @@ class testcaseModel extends model } /** - * Batch Change Scene. + * Batch change scene. * * @param array $caseIDList * @param int $sceneId @@ -3132,7 +3132,7 @@ class testcaseModel extends model } /** - * Build Menu Query. + * Build menu query. * * @param int $rootID * @param int $moduleID @@ -3164,7 +3164,7 @@ class testcaseModel extends model } /** - * Build Operate Browse Scene Menu. + * Build operate browse scene menu. * * @param object $scene * @access public @@ -3189,7 +3189,7 @@ class testcaseModel extends model } /** - * Search Form Add Scene. + * Search form add scene. * * @param int $productID * @param array $products @@ -3239,7 +3239,7 @@ class testcaseModel extends model } /** - * Build Tree Array. + * Build tree array. * * @param array $treeMenu * @param array $scenes @@ -3349,7 +3349,7 @@ class testcaseModel extends model } /** - * Get All Children Id. + * Get all children id. * * @param int $sceneID * @access public @@ -3369,7 +3369,7 @@ class testcaseModel extends model } /** - * Search Cases Has Scene. + * Search cases has scene. * * @param int $productID * @param int $queryID @@ -3527,7 +3527,7 @@ class testcaseModel extends model } /** - * Search Cases Has Scene. + * Search cases has scene. * * @param int $viewID * @access public @@ -3553,7 +3553,7 @@ class testcaseModel extends model } /** - * Get Scene Menu. + * Get scene menu. * * @param int $rootID * @param int $moduleID @@ -3631,7 +3631,7 @@ class testcaseModel extends model } /** - * Get Scene Name. + * Get scene name. * * @param array $moduleIdList * @param bool $allPath @@ -3674,7 +3674,7 @@ class testcaseModel extends model } /** - * Get Cases Which Has Scene. + * Get cases which has scene. * * @param int $productID * @param int $branch @@ -3753,7 +3753,7 @@ class testcaseModel extends model } /** - * Print Table Head. + * Print table head. * * @param object $col * @param string $orderBy @@ -3825,7 +3825,7 @@ class testcaseModel extends model } /** - * Update Scene. + * Update scene. * * @param int $sceneID * @access public @@ -3945,7 +3945,7 @@ class testcaseModel extends model } /** - * Get Xmind File Content. + * Get xmind file content. * * @param string $fileName * @access public @@ -3960,7 +3960,7 @@ class testcaseModel extends model } /** - * Save Xmind File Content To Database. + * Save xmind file content to database. * * @access public * @return array @@ -4009,7 +4009,7 @@ class testcaseModel extends model } /** - * Save Test Case. + * Save test case. * * @param array $testcaseData * @param array $sceneIds @@ -4150,7 +4150,7 @@ class testcaseModel extends model } /** - * Save Scene. + * Save scene. * * @param array $sceneData * @param array $sceneIds @@ -4232,7 +4232,7 @@ class testcaseModel extends model } /** - * Get Export Data. + * Get export data. * * @param int $productID * @param int $moduleID @@ -4260,7 +4260,7 @@ class testcaseModel extends model } /** - * Get Module By Product. + * Get module by product. * * @param int $productID * @param int $moduleID @@ -4289,7 +4289,7 @@ class testcaseModel extends model } /** - * Get Case By Product And Module. + * Get case by product and module. * * @param int $productID * @param int $moduleID @@ -4321,7 +4321,7 @@ class testcaseModel extends model } /** - * Get Step By Product And Module. + * Get step by product and module. * * @param int $productID * @param int $moduleID @@ -4349,7 +4349,7 @@ class testcaseModel extends model } /** - * Get Scene By Product And Module. + * Get scene by product and module. * * @param int $productID * @param int $moduleID @@ -4378,7 +4378,7 @@ class testcaseModel extends model } /** - * Check Config. + * Check config. * * @param string $str * @access public @@ -4390,7 +4390,7 @@ class testcaseModel extends model } /** - * Save Xmind Config. + * Save xmind config. * * @access public * @return array @@ -4476,7 +4476,7 @@ class testcaseModel extends model } /** - * Get Xmind Config. + * Get xmind config. * * @access public * @return array @@ -4502,7 +4502,7 @@ class testcaseModel extends model } /** - * Convert Xml To Array. + * Convert xml to array. * * @param object $xml * @param array $options From 4bcd11acd32a5feca41f53cc6ca6afb10eb156b5 Mon Sep 17 00:00:00 2001 From: zhaoke Date: Thu, 13 Apr 2023 16:38:59 +0800 Subject: [PATCH 14/14] * Update commits. --- module/testcase/control.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/testcase/control.php b/module/testcase/control.php index fbabd18da0..98abfc8003 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -2556,7 +2556,7 @@ class testcase extends control } /** - * Create Scene. + * Create scene. * * @param int $productID * @param int $branch @@ -2642,7 +2642,7 @@ class testcase extends control } /** - * Ajax Get module scenes. + * Ajax get module scenes. * * @param int $productID * @param int $branch