From 078520ee2ef71efcb65e113d5ec9aa3b6f0f4fa4 Mon Sep 17 00:00:00 2001 From: Catouse Date: Tue, 2 May 2017 17:14:48 +0800 Subject: [PATCH 01/16] * finish task #2989. --- module/testcase/js/common.js | 12 ++++++----- module/testcase/view/create.html.php | 16 +++++++------- module/testcase/view/edit.html.php | 32 ++++++++-------------------- 3 files changed, 24 insertions(+), 36 deletions(-) diff --git a/module/testcase/js/common.js b/module/testcase/js/common.js index 4c32979869..2730b9645d 100644 --- a/module/testcase/js/common.js +++ b/module/testcase/js/common.js @@ -145,20 +145,21 @@ function initSteps(selector) var insertStepRow = function($row, count, type, notFocus) { if(count === undefined) count = 1; + var $step; for(var i = 0; i < count; ++i) { - var $step = $stepTemplate.clone(); + $step = $stepTemplate.clone(); if($row) $row.after($step); else $steps.append($step); $step.addClass('step-new'); - if(type) $step.find('step-type').val(type); - if(!notFocus) setTimeout(function(){$step.find('.step-steps').focus();}, 10); + if(type) $step.find('.step-type').val(type); } + if(!notFocus && $step) setTimeout(function(){$step.find('.step-steps').focus();}, 10); }; var updateStepType = function($step, type) { var targetIsGroup = type =='group'; - $step.attr('data-type', type).find('.step-steps').toggleClass('autosize', !targetIsGroup).attr('placeholder', targetIsGroup ? groupNameText : null).focus(); + $step.attr('data-type', type).find('.step-steps').toggleClass('autosize', !targetIsGroup).attr('placeholder', targetIsGroup ? groupNameText : null); }; var getStepsElements = function() { @@ -277,7 +278,8 @@ function initSteps(selector) var $step = $control.closest('.step'); if($step.data('index') === getStepsElements().length) { - insertStepRow($step, 1, 'step', false); + insertStepRow($step, 1, 'step', true); + if($step.is('.step-item,.step-group')) insertStepRow($step, 1, 'item', true); refreshSteps(); } } diff --git a/module/testcase/view/create.html.php b/module/testcase/view/create.html.php index 7cb8a42c9e..a5f07f7178 100644 --- a/module/testcase/view/create.html.php +++ b/module/testcase/view/create.html.php @@ -147,16 +147,16 @@ - +
- - - + + +
@@ -170,16 +170,16 @@ type)) $step->type = 'step';?> - + expect, "rows='1' class='form-control autosize step-expects'") ?>
- - - + + +
diff --git a/module/testcase/view/edit.html.php b/module/testcase/view/edit.html.php index f51f03b73e..fcc930ee1c 100644 --- a/module/testcase/view/edit.html.php +++ b/module/testcase/view/edit.html.php @@ -60,23 +60,16 @@ - +
- - - + + +
@@ -89,24 +82,17 @@ desc, "rows='1' class='form-control autosize step-steps'") ?> type)) $step->type = 'step';?> - - + + expect, "rows='1' class='form-control autosize step-expects'") ?>
- - - + + +
From f0cbb00254e5014475ff6f40a3e4d40805aa9160 Mon Sep 17 00:00:00 2001 From: Catouse Date: Tue, 2 May 2017 17:15:20 +0800 Subject: [PATCH 02/16] * finish task #3023, and fix bug #1000. --- www/js/my.full.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/www/js/my.full.js b/www/js/my.full.js index 73ac117120..e808766847 100644 --- a/www/js/my.full.js +++ b/www/js/my.full.js @@ -543,7 +543,29 @@ function setFormAction(actionLink, hiddenwin, obj) if(hiddenwin) $form.attr('target', hiddenwin); else $form.removeAttr('target'); - $form.attr('action', actionLink).submit(); + $form.attr('action', actionLink); + + // Check safari is for bug #1000, see http://pms.zentao.net/bug-view-1000.html + var isSafari = navigator.userAgent.indexOf('AppleWebKit') > -1; + if(isSafari) + { + var idPreffix = 'checkbox-fix-' + $.zui.uuid(); + $form.find('input[type="checkbox"]').each(function() + { + var $checkbox = $(this); + var checkboxId = idPreffix + $checkbox.val(); + $checkbox.attr('data-fix-checkbox', checkboxId).after('
').appendTo($form); + }); + } + $form.submit(); + if(isSafari) + { + $form.find('[data-fix-checkbox]').each(function() + { + var $checkbox = $(this); + $('#' + $checkbox.data('fixCheckbox')).after($checkbox).remove(); + }); + } } /** From 22fb4f59fc925b854782e26cf001c4e879717c33 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Wed, 3 May 2017 10:33:50 +0800 Subject: [PATCH 03/16] =?UTF-8?q?Finish=20task#2982=20=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=E5=BA=93=E5=A2=9E=E5=8A=A0=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E5=92=8C=E6=89=B9=E9=87=8F=E6=B7=BB=E5=8A=A0=E7=9A=84=E5=8A=9F?= =?UTF-8?q?=E8=83=BD,cost:5=20left:0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/testsuite/control.php | 340 +++++++++++++++++- module/testsuite/css/batchcreatecase.css | 2 + module/testsuite/css/library.css | 2 + module/testsuite/css/showimport.css | 2 + module/testsuite/lang/zh-cn.php | 2 + module/testsuite/model.php | 249 +++++++++++++ .../testsuite/view/batchcreatecase.html.php | 93 +++++ module/testsuite/view/exporttemplet.html.php | 49 +++ module/testsuite/view/import.html.php | 19 + module/testsuite/view/library.html.php | 19 + module/testsuite/view/showimport.html.php | 121 +++++++ 11 files changed, 897 insertions(+), 1 deletion(-) create mode 100644 module/testsuite/css/batchcreatecase.css create mode 100644 module/testsuite/css/showimport.css create mode 100644 module/testsuite/view/batchcreatecase.html.php create mode 100644 module/testsuite/view/exporttemplet.html.php create mode 100644 module/testsuite/view/import.html.php create mode 100644 module/testsuite/view/showimport.html.php diff --git a/module/testsuite/control.php b/module/testsuite/control.php index de4113f002..5aa421adc3 100644 --- a/module/testsuite/control.php +++ b/module/testsuite/control.php @@ -432,7 +432,7 @@ class testsuite extends control $this->view->orderBy = $orderBy; $this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter'); $this->view->modules = $this->tree->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); - $this->view->moduleTree = $this->tree->getTreeMenu($libID, $viewType = 'caselib', $startModuleID = 0, array('treeModel', 'createCaseLibLink'), ''); + $this->view->moduleTree = $this->tree->getTreeMenu($libID, $viewType = 'caselib', $startModuleID = 0, array('treeModel', 'createCaseLibLink')); $this->view->pager = $pager; $this->view->browseType = $browseType; $this->view->moduleID = $moduleID; @@ -554,6 +554,47 @@ class testsuite extends control $this->display(); } + /** + * Batch create case. + * + * @param int $libID + * @param int $moduleID + * @access public + * @return void + */ + public function batchCreateCase($libID, $moduleID = 0) + { + $this->loadModel('testcase'); + if(!empty($_POST)) + { + $caseID = $this->testsuite->batchCreateCase($libID); + if(dao::isError()) die(js::error(dao::getError())); + if(isonlybody()) die(js::closeModal('parent.parent', 'this')); + die(js::locate($this->createLink('testsuite', 'library', "libID=$libID&browseType=byModule¶m=$moduleID"), 'parent')); + } + + $libraries = $this->testsuite->getLibraries(); + if(empty($libraries)) $this->locate(inlink('createLib')); + + /* Set lib menu. */ + $this->testsuite->setLibMenu($libraries, $libID); + + $currentModuleID = (int)$moduleID; + + /* Set module option menu. */ + $moduleOptionMenu = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); + $moduleOptionMenu['ditto'] = $this->lang->testcase->ditto; + + $this->view->title = $libraries[$libID] . $this->lang->colon . $this->lang->testcase->batchCreate; + $this->view->position[] = html::a($this->createLink('testsuite', 'library', "libID=$libID"), $libraries[$libID]); + $this->view->position[] = $this->lang->testcase->batchCreate; + $this->view->libID = $libID; + $this->view->moduleOptionMenu = $moduleOptionMenu; + $this->view->currentModuleID = $currentModuleID; + + $this->display(); + } + /** * View library * @@ -623,4 +664,301 @@ class testsuite extends control $this->view->keywords = $keywords; $this->display(); } + + /** + * Export templet. + * + * @param int $libID + * @access public + * @return void + */ + public function exportTemplet($libID) + { + $this->loadModel('testcase'); + if($_POST) + { + if(!$this->config->testcase->needReview) unset($this->lang->testcase->statusList['wait']); + + $fields['module'] = $this->lang->testcase->module; + $fields['title'] = $this->lang->testcase->title; + $fields['stepDesc'] = $this->lang->testcase->stepDesc; + $fields['stepExpect'] = $this->lang->testcase->stepExpect; + $fields['keywords'] = $this->lang->testcase->keywords; + $fields['type'] = $this->lang->testcase->type; + $fields['pri'] = $this->lang->testcase->pri; + $fields['status'] = $this->lang->testcase->status; + $fields['stage'] = $this->lang->testcase->stage; + $fields['precondition'] = $this->lang->testcase->precondition; + + $fields[''] = ''; + $fields['typeValue'] = $this->lang->testcase->lblTypeValue; + $fields['stageValue'] = $this->lang->testcase->lblStageValue; + $fields['statusValue'] = $this->lang->testcase->lblStatusValue; + + $modules = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); + $rows = array(); + foreach($modules as $moduleID => $module) + { + $row = new stdclass(); + $row->module = $module . "(#$moduleID)"; + $row->stepDesc = "1. \n2. \n3."; + $row->stepExpect = "1. \n2. \n3."; + + if(empty($rows)) + { + $row->typeValue = join("\n", $this->lang->testcase->typeList); + $row->stageValue = join("\n", $this->lang->testcase->stageList); + $row->statusValue = join("\n", $this->lang->testcase->statusList); + } + $rows[] = $row; + } + + $this->post->set('fields', $fields); + $this->post->set('kind', 'testcase'); + $this->post->set('rows', $rows); + $this->post->set('extraNum', $this->post->num); + $this->post->set('fileName', 'templet'); + $this->fetch('file', 'export2csv', $_POST); + } + + $this->display(); + } + + /** + * Import case csv file. + * + * @param int $libID + * @access public + * @return void + */ + public function import($libID) + { + $this->loadModel('testcase'); + if($_FILES) + { + $file = $this->loadModel('file')->getUpload('file'); + $file = $file[0]; + + move_uploaded_file($file['tmpname'], $this->file->savePath . $file['pathname']); + + $fileName = $this->file->savePath . $file['pathname']; + $rows = $this->file->parseCSV($fileName); + $fields = $this->testcase->getImportFields($productID); + $fields = array_flip($fields); + $header = array(); + foreach($rows[0] as $i => $rowValue) + { + if(empty($rowValue)) break; + $header[$i] = $rowValue; + } + unset($rows[0]); + + $columnKey = array(); + foreach($header as $title) + { + if(!isset($fields[$title])) continue; + $columnKey[] = $fields[$title]; + } + + if(count($columnKey) != count($header) or $this->post->encode != 'utf-8') + { + $fc = file_get_contents($fileName); + $encode = $this->post->encode != "utf-8" ? $this->post->encode : 'gbk'; + $fc = helper::convertEncoding($fc, $encode, 'utf-8'); + file_put_contents($fileName, $fc); + + $rows = $this->file->parseCSV($fileName); + $columnKey = array(); + $header = array(); + foreach($rows[0] as $i => $rowValue) + { + if(empty($rowValue)) break; + $header[$i] = $rowValue; + } + unset($rows[0]); + foreach($header as $title) + { + if(!isset($fields[$title])) continue; + $columnKey[] = $fields[$title]; + } + if(count($columnKey) != count($header)) die(js::alert($this->lang->testcase->errorEncode)); + } + + $this->session->set('importFile', $fileName); + + die(js::locate(inlink('showImport', "libID=$libID"), 'parent.parent')); + } + $this->display(); + } + + /** + * Show import case. + * + * @param int $libID + * @access public + * @return void + */ + public function showImport($libID) + { + $this->loadModel('testcase'); + if($_POST) + { + $this->testsuite->createFromImport($libID); + die(js::locate(inlink('library', "libID=$libID"), 'parent')); + } + + $libraries = $this->testsuite->getLibraries(); + if(empty($libraries)) $this->locate(inlink('createLib')); + + $this->testsuite->setMenu($libraries, $libID); + + $file = $this->session->importFile; + $caseLang = $this->lang->testcase; + $caseConfig = $this->config->testcase; + $modules = $this->loadModel('tree')->getOptionMenu($libID, $viewType = 'caselib', $startModuleID = 0); + + $fields = explode(',', $caseConfig->exportFields); + foreach($fields as $key => $fieldName) + { + $fieldName = trim($fieldName); + $fields[$fieldName] = isset($caseLang->$fieldName) ? $caseLang->$fieldName : $fieldName; + unset($fields[$key]); + } + + $fields = array_flip($fields); + $rows = $this->loadModel('file')->parseCSV($file); + $header = array(); + foreach($rows[0] as $i => $rowValue) + { + if(empty($rowValue)) break; + $header[$i] = $rowValue; + } + unset($rows[0]); + + foreach($header as $title) + { + if(!isset($fields[$title])) continue; + $columnKey[] = $fields[$title]; + } + + $endField = end($fields); + $caseData = array(); + $stepData = array(); + $stepVars = 0; + foreach($rows as $row => $data) + { + $case = new stdclass(); + foreach($columnKey as $key => $field) + { + if(!isset($data[$key])) continue; + $cellValue = $data[$key]; + if($field == 'module') + { + $case->$field = 0; + if(strrpos($cellValue, '(#') !== false) + { + $id = trim(substr($cellValue, strrpos($cellValue,'(#') + 2), ')'); + $case->$field = $id; + } + } + elseif(in_array($field, $caseConfig->export->listFields)) + { + if($field == 'stage') + { + $stages = explode("\n", $cellValue); + foreach($stages as $stage) $case->stage[] = array_search($stage, $caseLang->{$field . 'List'}); + $case->stage = join(',', $case->stage); + } + else + { + $case->$field = array_search($cellValue, $caseLang->{$field . 'List'}); + } + } + elseif($field != 'stepDesc' and $field != 'stepExpect') + { + $case->$field = $cellValue; + } + else + { + $steps = explode("\n", $cellValue); + $stepKey = str_replace('step', '', strtolower($field)); + $caseStep = array(); + + foreach($steps as $step) + { + $step = trim($step); + if(empty($step)) continue; + if(preg_match('/^(([0-9]+)\.[0-9]+)([.、]{1})/U', $step, $out)) + { + $num = $out[1]; + $parent = $out[2]; + $sign = $out[3]; + $signbit = $sign == '.' ? 1 : 3; + $step = trim(substr($step, strlen($num) + $signbit)); + if(!empty($step)) $caseStep[$num]['content'] = $step; + $caseStep[$num]['type'] = 'item'; + $caseStep[$parent]['type'] = 'group'; + } + elseif(preg_match('/^([0-9]+)([.、]{1})/U', $step, $out)) + { + $num = $out[1]; + $sign = $out[2]; + $signbit = $sign == '.' ? 1 : 3; + $step = trim(substr($step, strlen($num) + $signbit)); + if(!empty($step)) $caseStep[$num]['content'] = $step; + $caseStep[$num]['type'] = 'step'; + } + elseif(isset($num)) + { + $caseStep[$num]['content'] .= "\n" . $step; + } + else + { + if($field == 'stepDesc') + { + $num = 1; + $caseStep[$num]['content'] = $step; + $caseStep[$num]['type'] = 'step'; + } + if($field == 'stepExpect' and isset($stepData[$row]['desc'])) + { + end($stepData[$row]['desc']); + $num = key($stepData[$row]['desc']); + $caseStep[$num]['content'] = $step; + } + } + } + unset($num); + unset($sign); + $stepVars += count($caseStep, COUNT_RECURSIVE) - count($caseStep); + $stepData[$row][$stepKey] = $caseStep; + } + } + + $caseData[$row] = $case; + unset($case); + } + + if(empty($caseData)) + { + echo js::alert($this->lang->error->noData); + die(js::locate($this->createLink('testsuite', 'library', "libID=$libID"))); + } + if(!$this->config->testcase->needReview) unset($this->lang->testcase->statusList['wait']); + + /* Judge whether the editedTasks is too large and set session. */ + $countInputVars = count($caseData) * 9 + $stepVars; + $showSuhosinInfo = common::judgeSuhosinSetting($countInputVars); + if($showSuhosinInfo) $this->view->suhosinInfo = extension_loaded('suhosin') ? sprintf($this->lang->suhosinInfo, $countInputVars) : sprintf($this->lang->maxVarsInfo, $countInputVars); + + $this->view->title = $this->lang->caselib->common . $this->lang->colon . $this->lang->testcase->showImport; + $this->view->position[] = $this->lang->testcase->showImport; + + $this->view->modules = $modules; + $this->view->cases = $this->dao->select('id,module,stage,status,pri,type')->from(TABLE_CASE)->where('lib')->eq($libID)->andWhere('deleted')->eq(0)->andWhere('product')->eq(0)->fetchAll('id'); + $this->view->caseData = $caseData; + $this->view->stepData = $stepData; + $this->view->libID = $libID; + $this->display(); + } } diff --git a/module/testsuite/css/batchcreatecase.css b/module/testsuite/css/batchcreatecase.css new file mode 100644 index 0000000000..97107bc0d1 --- /dev/null +++ b/module/testsuite/css/batchcreatecase.css @@ -0,0 +1,2 @@ +.w-180px{width:180px} +.chosen-container[id^="module"] .chosen-drop {min-width: 400px; border-top: 1px solid #ddd!important} diff --git a/module/testsuite/css/library.css b/module/testsuite/css/library.css index 2c188e386f..6e9a99e6c1 100644 --- a/module/testsuite/css/library.css +++ b/module/testsuite/css/library.css @@ -1,5 +1,7 @@ .w-220px{width:220px} +#createActionMenu .dropdown-menu {width: 100%;} + .dropdown-menu.with-search {padding: 0; min-width: 150px; overflow: hidden; max-height: 302px;} .dropdown-menu > .menu-search .input-group {width:100%;} .dropdown-menu > .menu-search .input-group-addon {position: absolute; right: 10px; top: 0; z-index: 10; background: none; border: none; color: #666} diff --git a/module/testsuite/css/showimport.css b/module/testsuite/css/showimport.css new file mode 100644 index 0000000000..496c95dba6 --- /dev/null +++ b/module/testsuite/css/showimport.css @@ -0,0 +1,2 @@ +.table table td,.outer .table table tbody > tr:last-child td {padding: 2px 0 2px 5px!important; border: none!important;} +.chosen-container-single .chosen-drop {min-width: 300px; border-top: 1px solid #ddd!important} diff --git a/module/testsuite/lang/zh-cn.php b/module/testsuite/lang/zh-cn.php index be6e650cce..2f4f981bd5 100644 --- a/module/testsuite/lang/zh-cn.php +++ b/module/testsuite/lang/zh-cn.php @@ -20,6 +20,8 @@ $lang->testsuite->linkVersion = "版本"; $lang->testsuite->unlinkCase = "移除"; $lang->testsuite->batchUnlinkCases = "批量移除用例"; $lang->testsuite->deleted = '已删除'; +$lang->testsuite->exportTemplet = '导出模板'; +$lang->testsuite->importFile = '导入CSV'; $lang->testsuite->common = '套件'; $lang->testsuite->product = '所属' . $lang->productCommon; diff --git a/module/testsuite/model.php b/module/testsuite/model.php index 174bec7dfe..f709bf6f8d 100644 --- a/module/testsuite/model.php +++ b/module/testsuite/model.php @@ -509,4 +509,253 @@ class testsuiteModel extends model } return $link; } + + /** + * Create from import. + * + * @param int $libID + * @access public + * @return void + */ + public function createFromImport($libID) + { + $this->loadModel('action'); + $this->loadModel('testcase'); + $this->loadModel('file'); + $now = helper::now(); + $data = fixer::input('post')->get(); + + if(!empty($_POST['id'])) + { + $oldSteps = $this->dao->select('t2.*')->from(TABLE_CASE)->alias('t1') + ->leftJoin(TABLE_CASESTEP)->alias('t2')->on('t1.id = t2.case') + ->where('t1.id')->in(($_POST['id'])) + ->andWhere('t1.version=t2.version') + ->orderBy('t2.id') + ->fetchGroup('case'); + $oldCases = $this->dao->select('*')->from(TABLE_CASE)->where('id')->in($_POST['id'])->fetchAll('id'); + } + + $cases = array(); + foreach($data->lib as $key => $lib) + { + $caseData = new stdclass(); + + $caseData->lib = $lib; + $caseData->module = $data->module[$key]; + $caseData->title = $data->title[$key]; + $caseData->pri = (int)$data->pri[$key]; + $caseData->type = $data->type[$key]; + $caseData->status = $data->status[$key]; + $caseData->stage = join(',', $data->stage[$key]); + $caseData->keywords = $data->keywords[$key]; + $caseData->frequency = 1; + $caseData->precondition = $data->precondition[$key]; + + if(isset($this->config->testcase->create->requiredFields)) + { + $requiredFields = explode(',', $this->config->testcase->create->requiredFields); + foreach($requiredFields as $requiredField) + { + $requiredField = trim($requiredField); + if(empty($caseData->$requiredField)) die(js::alert(sprintf($this->lang->testcase->noRequire, $key, $this->lang->testcase->$requiredField))); + } + } + + $cases[$key] = $caseData; + } + + $forceReview = $this->testcase->forceReview(); + foreach($cases as $key => $caseData) + { + if(!empty($_POST['id'][$key]) and empty($_POST['insert'])) + { + $caseID = $data->id[$key]; + $stepChanged = false; + $steps = array(); + $oldStep = isset($oldSteps[$caseID]) ? $oldSteps[$caseID] : array(); + $oldCase = $oldCases[$caseID]; + + /* Remove the empty setps in post. */ + $steps = array(); + if(isset($_POST['desc'][$key])) + { + foreach($data->desc[$key] as $id => $desc) + { + $desc = trim($desc); + if(empty($desc))continue; + $step = new stdclass(); + $step->type = $data->stepType[$key][$id]; + $step->desc = $desc; + $step->expect = trim($data->expect[$key][$id]); + + $steps[] = $step; + } + } + + /* If step count changed, case changed. */ + if((!$oldStep != !$steps) or (count($oldStep) != count($steps))) + { + $stepChanged = true; + } + else + { + /* Compare every step. */ + foreach($oldStep as $id => $oldStep) + { + if(trim($oldStep->desc) != trim($steps[$id]->desc) or trim($oldStep->expect) != $steps[$id]->expect) + { + $stepChanged = true; + break; + } + } + } + + $version = $stepChanged ? $oldCase->version + 1 : $oldCase->version; + $caseData->version = $version; + $changes = common::createChanges($oldCase, $caseData); + if(!$changes and !$stepChanged) continue; + + if($changes or $stepChanged) + { + $caseData->lastEditedBy = $this->app->user->account; + $caseData->lastEditedDate = $now; + if($stepChanged and $forceReview) $caseData->status = 'wait'; + $this->dao->update(TABLE_CASE)->data($caseData)->where('id')->eq($caseID)->autoCheck()->exec(); + if($stepChanged) + { + $parentStepID = 0; + foreach($steps as $id => $step) + { + $step = (array)$step; + if(empty($step['desc'])) continue; + $stepData = new stdclass(); + $stepData->type = ($step['type'] == 'item' and $parentStepID == 0) ? 'step' : $step['type']; + $stepData->parent = ($stepData->type == 'item') ? $parentStepID : 0; + $stepData->case = $caseID; + $stepData->version = $version; + $stepData->desc = htmlspecialchars($step['desc']); + $stepData->expect = htmlspecialchars($step['expect']); + $this->dao->insert(TABLE_CASESTEP)->data($stepData)->autoCheck()->exec(); + if($stepData->type == 'group') $parentStepID = $this->dao->lastInsertID(); + if($stepData->type == 'step') $parentStepID = 0; + } + } + $oldCase->steps = $this->joinStep($oldStep); + $caseData->steps = $this->joinStep($steps); + $changes = common::createChanges($oldCase, $caseData); + $actionID = $this->action->create('case', $caseID, 'Edited'); + $this->action->logHistory($actionID, $changes); + } + } + else + { + $caseData->version = 1; + $caseData->openedBy = $this->app->user->account; + $caseData->openedDate = $now; + $caseData->branch = isset($data->branch[$key]) ? $data->branch[$key] : $branch; + if($forceReview) $caseData->status = 'wait'; + $this->dao->insert(TABLE_CASE)->data($caseData)->autoCheck()->exec(); + + if(!dao::isError()) + { + $caseID = $this->dao->lastInsertID(); + $parentStepID = 0; + foreach($data->desc[$key] as $id => $desc) + { + $desc = trim($desc); + if(empty($desc)) continue; + $stepData = new stdclass(); + $stepData->type = ($data->stepType[$key][$id] == 'item' and $parentStepID == 0) ? 'step' : $data->stepType[$key][$id]; + $stepData->parent = ($stepData->type == 'item') ? $parentStepID : 0; + $stepData->case = $caseID; + $stepData->version = 1; + $stepData->desc = htmlspecialchars($desc); + $stepData->expect = htmlspecialchars($data->expect[$key][$id]); + $this->dao->insert(TABLE_CASESTEP)->data($stepData)->autoCheck()->exec(); + if($stepData->type == 'group') $parentStepID = $this->dao->lastInsertID(); + if($stepData->type == 'step') $parentStepID = 0; + } + $this->action->create('case', $caseID, 'Opened'); + } + } + } + + unlink($this->session->importFile); + unset($_SESSION['importFile']); + } + + /** + * Batch create case for lib. + * + * @param int $libID + * @access public + * @return void + */ + public function batchCreateCase($libID) + { + $this->loadModel('testcase'); + $this->loadModel('action'); + + $now = helper::now(); + $cases = fixer::input('post')->get(); + $batchNum = count(reset($cases)); + + $result = $this->loadModel('common')->removeDuplicate('case', $cases, "lib={$libID}"); + $cases = $result['data']; + + for($i = 0; $i < $batchNum; $i++) + { + if(!empty($cases->title[$i]) and empty($cases->type[$i])) die(js::alert(sprintf($this->lang->error->notempty, $this->lang->testcase->type))); + } + + $module = 0; + $type = ''; + $pri = 3; + for($i = 0; $i < $batchNum; $i++) + { + $module = $cases->module[$i] == 'ditto' ? $module : $cases->module[$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->type[$i] = $type; + $cases->pri[$i] = $pri; + } + + $forceReview = $this->testcase->forceReview(); + for($i = 0; $i < $batchNum; $i++) + { + if($cases->type[$i] != '' and $cases->title[$i] != '') + { + $data[$i] = new stdclass(); + $data[$i]->lib = $libID; + $data[$i]->module = $cases->module[$i]; + $data[$i]->type = $cases->type[$i]; + $data[$i]->pri = $cases->pri[$i]; + $data[$i]->stage = empty($cases->stage[$i]) ? '' : implode(',', $cases->stage[$i]); + $data[$i]->color = $cases->color[$i]; + $data[$i]->title = $cases->title[$i]; + $data[$i]->precondition = $cases->precondition[$i]; + $data[$i]->keywords = $cases->keywords[$i]; + $data[$i]->openedBy = $this->app->user->account; + $data[$i]->openedDate = $now; + $data[$i]->status = $forceReview ? 'wait' : 'normal'; + $data[$i]->version = 1; + + $this->dao->insert(TABLE_CASE)->data($data[$i]) + ->autoCheck() + ->batchCheck($this->config->testcase->create->requiredFields, 'notempty') + ->exec(); + + if(dao::isError()) + { + echo js::error(dao::getError()); + die(js::reload('parent')); + } + + $caseID = $this->dao->lastInsertID(); + $actionID = $this->action->create('case', $caseID, 'Opened'); + } + } + } } diff --git a/module/testsuite/view/batchcreatecase.html.php b/module/testsuite/view/batchcreatecase.html.php new file mode 100644 index 0000000000..1c089d2ea9 --- /dev/null +++ b/module/testsuite/view/batchcreatecase.html.php @@ -0,0 +1,93 @@ + + * @package testsuite + * @version $Id$ + * @link http://www.zentao.net + */ +?> + +testcase->batchCreate);?> + +
+
+ icons['testcase']);?> + + icons['batchCreate']);?> + testcase->batchCreate;?> + +
+ pasteText, "data-toggle='myModal' ")?> +
+
+
+
+ + + + + + + + + + + + + + + testcase->typeList['']);?> + testcase->batchCreate; $i++):?> + testcase->typeList['ditto'] = $lang->testcase->ditto; + if($i != 0) $lang->testcase->priList['ditto'] = $lang->testcase->ditto; + $type = $i == 0 ? 'feature' : 'ditto'; + $pri = $i == 0 ? 3 : 'ditto'; + ?> + + + + + + + + + + + + + + +
idAB;?>testcase->module;?>testcase->title;?> testcase->type;?> testcase->pri;?>testcase->precondition;?>testcase->keywords;?>testcase->stage;?>
+
+ testcase->colorTag}' data-update-text='#title\\[{$i}\\]'");?> + +
+
testcase->typeList, $type, "class=form-control");?>testcase->priList, $pri, "class=form-control");?>testcase->stageList, '', "class='form-control chosen' multiple");?>
+
+ + + + + + + + + + + + + + + +
%s +
+ testcase->colorTag}' data-update-text='#title\\[%s\\]'");?> +
testcase->typeList, $type, "class=form-control");?>testcase->priList, $pri, "class=form-control");?>testcase->stageList, '', "class='form-control' multiple");?>
+ + diff --git a/module/testsuite/view/exporttemplet.html.php b/module/testsuite/view/exporttemplet.html.php new file mode 100644 index 0000000000..80e5e9e311 --- /dev/null +++ b/module/testsuite/view/exporttemplet.html.php @@ -0,0 +1,49 @@ + + +
+
+ icons['export']);?> + testcase->exportTemplet;?> +
+
+ +
+ + + + + + +
+
+ testcase->num;?> + +
+
+ charsets[$this->cookie->lang], 'utf-8', "class='form-control'");?> + + +
+
+ diff --git a/module/testsuite/view/import.html.php b/module/testsuite/view/import.html.php new file mode 100644 index 0000000000..00c7543f75 --- /dev/null +++ b/module/testsuite/view/import.html.php @@ -0,0 +1,19 @@ + +
+
+ + + + + + +
+ + + charsets[$this->cookie->lang], 'utf-8', "class='form-control'");?> + + +
+
+
+ diff --git a/module/testsuite/view/library.html.php b/module/testsuite/view/library.html.php index fa1a57be4e..15b4db4b76 100644 --- a/module/testsuite/view/library.html.php +++ b/module/testsuite/view/library.html.php @@ -45,6 +45,15 @@ js::set('batchDelete', $lang->testcase->confirmBatchDelete); ?>
+
+ createLink('testsuite', 'exportTemplet', "libID=$libID") : '#'; + if(common::hasPriv('testsuite', 'exportTemplet')) echo html::a($link, " " . $lang->testsuite->exportTemplet, '', "class='btn export'"); + + $link = common::hasPriv('testsuite', 'import') ? $this->createLink('testsuite', 'import', "libID=$libID") : '#'; + if(common::hasPriv('testsuite', 'import')) echo html::a($link, " " . $lang->testsuite->importFile, '', "class='btn export'"); + ?> +
testcase->confirmBatchDelete); $link = common::hasPriv('testsuite', 'createCase') ? $this->createLink('testsuite', 'createCase', "libID=$libID&moduleID=" . (isset($moduleID) ? $moduleID : 0)) : '#'; echo html::a($link, "" . $lang->testcase->create, '', $misc); ?> + +
diff --git a/module/testsuite/view/showimport.html.php b/module/testsuite/view/showimport.html.php new file mode 100644 index 0000000000..79f04a1ae3 --- /dev/null +++ b/module/testsuite/view/showimport.html.php @@ -0,0 +1,121 @@ + + + +
+ +
+ + + + + + + + + + + + + + + + + + $case):?> + title)) continue;?> + id) and !isset($cases[$case->id])) $case->id = 0;?> + + + + + + + + + + + + + + + + + + + +
testcase->id?>testcase->title?>testcase->module?>testcase->pri?>testcase->type?>testcase->status?>testcase->stage?>testcase->keywords?>testcase->precondition?> + + + + + +
testcase->stepDesc?>testcase->stepExpect?>
+
+ id)) + { + echo $case->id . html::hidden("id[$key]", $case->id); + $insert = false; + } + else + { + echo $key . " {$lang->testcase->new}"; + } + echo html::hidden("lib[$key]", $libID); + ?> + title, "class='form-control' style='margin-top:2px' autocomplete='off'")?>module) ? $case->module : (!empty($case->id) ? $cases[$case->id]->module : ''), "class='form-control chosen'")?>testcase->priList, isset($case->pri) ? $case->pri : (!empty($case->id) ? $cases[$case->id]->pri : ''), "class='form-control'")?>testcase->typeList, isset($case->type) ? $case->type : (!empty($case->id) ? $cases[$case->id]->type : ''), "class='form-control'")?>testcase->statusList, isset($case->status) ? $case->status : (!empty($case->id) ? $cases[$case->id]->status : 'normal'), "class='form-control'")?>testcase->stageList, !empty($case->stage) ? $case->stage : (!empty($case->id) ? $cases[$case->id]->stage : ''), "multiple='multiple' class='form-control chosen'")?>keywords) ? $case->keywords : "", "class='form-control' autocomplete='off'")?>precondition) ? htmlspecialchars($case->precondition) : "", "class='form-control'")?> + + + $desc):?> + + + + + + + +
+ +
+ {$lang->save}"; + } + else + { + echo html::submitButton(); + } + echo '   ' . html::backButton() + ?> +
+
+ + + From 457ffb3f61f98671596307bee9719eecc109253b Mon Sep 17 00:00:00 2001 From: wangyidong Date: Wed, 3 May 2017 10:34:49 +0800 Subject: [PATCH 04/16] =?UTF-8?q?Finish=20task#3015=20=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=A8=A1=E5=9D=97=E6=89=80=E5=B1=9E=E7=9A=84?= =?UTF-8?q?=E4=BA=A7=E5=93=81=E6=97=B6=EF=BC=8C=E5=88=97=E5=87=BA=E7=9A=84?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=88=97=E8=A1=A8=E6=9C=89=E9=87=8D=E5=A4=8D?= =?UTF-8?q?,cost:1=20left:0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/tree/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/tree/model.php b/module/tree/model.php index 46521384ce..05a27ff1f8 100644 --- a/module/tree/model.php +++ b/module/tree/model.php @@ -319,7 +319,7 @@ class treeModel extends model } else { - $branches = array($branch => ''); + $branches = array(); } $manage = $userFunc[1] == 'createManageLink' ? true : false; From 6b6883c2c95f6dcb1a71fc7d5d2c042cacfa889b Mon Sep 17 00:00:00 2001 From: wangyidong Date: Wed, 3 May 2017 10:45:49 +0800 Subject: [PATCH 05/16] * fix for browse task by product. --- module/project/model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/project/model.php b/module/project/model.php index e976114413..32d7a71b4c 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -825,7 +825,7 @@ class projectModel extends model unset($queryStatus['closed']); $queryStatus = array_keys($queryStatus); } - $tasks = $this->task->getProjectTasks($projectID, $productID, $queryStatus, $modules, $sort, $pager); + $tasks = $this->task->getProjectTasks($projectID, 0, $queryStatus, $modules, $sort, $pager); } else { From b4f0a9c031cb159fbe90772e89c283f1b0176e28 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Wed, 3 May 2017 10:46:23 +0800 Subject: [PATCH 06/16] * fix bug for batch update story. --- module/story/model.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/module/story/model.php b/module/story/model.php index a9d1a13304..77c8f4ff90 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -503,7 +503,6 @@ class storyModel extends model $allChanges = array(); $now = helper::now(); $data = fixer::input('post')->get(); - a($data);exit; $storyIDList = $this->post->storyIDList ? $this->post->storyIDList : array(); /* Init $stories. */ @@ -575,7 +574,6 @@ class storyModel extends model $this->dao->update(TABLE_STORY)->data($story) ->autoCheck() - ->batchCheck($this->config->story->edit->requiredFields, 'notempty') ->checkIF($story->closedBy, 'closedReason', 'notempty') ->checkIF($story->closedReason == 'done', 'stage', 'notempty') ->checkIF($story->closedReason == 'duplicate', 'duplicateStory', 'notempty') From 9aad8360a388fa7bbbfe65880a053137733b8886 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Wed, 3 May 2017 10:46:59 +0800 Subject: [PATCH 07/16] * adjust code for show import case. --- module/testcase/view/showimport.html.php | 1 + 1 file changed, 1 insertion(+) diff --git a/module/testcase/view/showimport.html.php b/module/testcase/view/showimport.html.php index ee1663487c..9d0bbef51a 100644 --- a/module/testcase/view/showimport.html.php +++ b/module/testcase/view/showimport.html.php @@ -67,6 +67,7 @@ $desc):?> + From 93ab2fce42ca579870ce9853e22085958618148a Mon Sep 17 00:00:00 2001 From: wangyidong Date: Wed, 3 May 2017 10:50:13 +0800 Subject: [PATCH 08/16] =?UTF-8?q?Finish=20task#3016=20=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=A7=86=E5=9B=BE=E7=89=88=E6=9C=AC=E9=80=89=E6=8B=A9=E9=87=8C?= =?UTF-8?q?=E9=9D=A2=E7=9A=84=E5=85=A8=E9=83=A8=E4=BA=A7=E5=93=81=E5=BA=94?= =?UTF-8?q?=E5=BD=93=E6=A0=B9=E6=8D=AE=E6=B5=81=E7=A8=8B=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E4=BF=AE=E6=94=B9,cost:1=20left:0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/testtask/lang/en.php | 2 +- module/testtask/lang/zh-cn.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/module/testtask/lang/en.php b/module/testtask/lang/en.php index b0e4ac7159..289a08add0 100644 --- a/module/testtask/lang/en.php +++ b/module/testtask/lang/en.php @@ -39,7 +39,7 @@ $lang->testtask->testing = "Testing Build"; $lang->testtask->blocked = "Blocked Build"; $lang->testtask->done = "Tested Build"; $lang->testtask->totalStatus = "All"; -$lang->testtask->all = "All Product"; +$lang->testtask->all = "All " . $lang->productCommon; $lang->testtask->id = 'ID'; $lang->testtask->common = 'Test build'; diff --git a/module/testtask/lang/zh-cn.php b/module/testtask/lang/zh-cn.php index 86e6d80ac5..9ad559d43e 100644 --- a/module/testtask/lang/zh-cn.php +++ b/module/testtask/lang/zh-cn.php @@ -39,7 +39,7 @@ $lang->testtask->testing = "测试中版本"; $lang->testtask->blocked = "被阻塞版本"; $lang->testtask->done = "已测版本"; $lang->testtask->totalStatus = "全部"; -$lang->testtask->all = "全部产品"; +$lang->testtask->all = "全部" . $lang->productCommon; $lang->testtask->id = '编号'; $lang->testtask->common = '测试版本'; From 4463151d96126e120dfa43d49cde2cb6f9cc6c42 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Wed, 3 May 2017 11:21:11 +0800 Subject: [PATCH 09/16] =?UTF-8?q?Finish=20task#2992=20=E7=87=83=E5=B0=BD?= =?UTF-8?q?=E5=9B=BE=E4=BF=AE=E6=94=B9=E9=A6=96=E5=A4=A9=E5=B7=A5=E6=97=B6?= =?UTF-8?q?=E6=97=B6=E5=B0=86=E9=82=A3=E4=B8=80=E5=A4=A9=E7=9A=84=E5=89=A9?= =?UTF-8?q?=E4=BD=99=E5=B7=A5=E6=97=B6=E4=B9=9F=E4=BF=AE=E6=94=B9=E4=BA=86?= =?UTF-8?q?,cost:1=20left:0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/project/control.php | 4 ++-- module/project/lang/en.php | 1 + module/project/lang/zh-cn.php | 1 + module/project/model.php | 4 ++-- module/project/view/fixfirst.html.php | 12 ++++++++++++ 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/module/project/control.php b/module/project/control.php index ea77051622..e44053fa34 100644 --- a/module/project/control.php +++ b/module/project/control.php @@ -810,11 +810,11 @@ class project extends control * @access public * @return void */ - public function fixFirst($projectID) + public function fixFirst($projectID, $withLeft = false) { if($_POST) { - $this->project->fixFirst($projectID); + $this->project->fixFirst($projectID, $withLeft); die(js::reload('parent.parent')); } diff --git a/module/project/lang/en.php b/module/project/lang/en.php index 9df8d39a8c..2fc63a3887 100644 --- a/module/project/lang/en.php +++ b/module/project/lang/en.php @@ -207,6 +207,7 @@ $lang->project->goback = "Go Back"; $lang->project->noweekend = 'Without Weekend'; $lang->project->withweekend = 'With Weekend'; $lang->project->interval = 'Intervals'; +$lang->project->fixFirstWithLeft = 'Do you want to set the left of the day to this value?'; /* 统计。*/ $lang->project->charts = new stdclass(); diff --git a/module/project/lang/zh-cn.php b/module/project/lang/zh-cn.php index 68502ff6f6..686d59382c 100644 --- a/module/project/lang/zh-cn.php +++ b/module/project/lang/zh-cn.php @@ -207,6 +207,7 @@ $lang->project->goback = "返回任务列表"; $lang->project->noweekend = '去除周末'; $lang->project->withweekend = '显示周末'; $lang->project->interval = '间隔'; +$lang->project->fixFirstWithLeft = '是否将该天剩余工时也设置为该值?'; /* 统计。*/ $lang->project->charts = new stdclass(); diff --git a/module/project/model.php b/module/project/model.php index 32d7a71b4c..d6948b0c87 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -1553,7 +1553,7 @@ class projectModel extends model * @access public * @return void */ - public function fixFirst($projectID) + public function fixFirst($projectID, $withLeft = false) { $project = $this->getById($projectID); $burn = $this->dao->select('*')->from(TABLE_BURN)->where('project')->eq($projectID)->andWhere('date')->eq($project->begin)->fetch(); @@ -1561,7 +1561,7 @@ class projectModel extends model $data = fixer::input('post') ->add('project', $projectID) ->add('date', $project->begin) - ->add('left', empty($burn) ? $this->post->estimate : $burn->left) + ->add('left', (empty($burn) or $withLeft) ? $this->post->estimate : $burn->left) ->add('consumed', empty($burn) ? 0 : $burn->consumed) ->get(); if(!is_numeric($data->estimate)) return false; diff --git a/module/project/view/fixfirst.html.php b/module/project/view/fixfirst.html.php index 5761599a62..e9404b196f 100644 --- a/module/project/view/fixfirst.html.php +++ b/module/project/view/fixfirst.html.php @@ -25,4 +25,16 @@
+ From 099bd94ebbe922c7b580190ebae7aba68bd2bf02 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Wed, 3 May 2017 14:39:38 +0800 Subject: [PATCH 10/16] =?UTF-8?q?Finish=20task#2994=20=E7=A6=85=E9=81=93?= =?UTF-8?q?=E5=A6=82=E6=9E=9C=E5=92=8C=E7=84=B6=E4=B9=8B=E9=9B=86=E6=88=90?= =?UTF-8?q?=E5=90=8E=EF=BC=8C=E6=A0=B7=E5=BC=8F=E5=81=9A=E8=B0=83=E6=95=B4?= =?UTF-8?q?,cost:3=20left:0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/common/view/footer.html.php | 18 ++++++++++++++---- module/common/view/header.html.php | 10 ++++++++++ www/js/bindranzhi.js | 22 ++++++++++++++++++++++ www/theme/default/bindranzhi.css | 6 ++++++ 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 www/js/bindranzhi.js create mode 100644 www/theme/default/bindranzhi.css diff --git a/module/common/view/footer.html.php b/module/common/view/footer.html.php index fe5cde58b7..05c609dfdd 100755 --- a/module/common/view/footer.html.php +++ b/module/common/view/footer.html.php @@ -17,23 +17,33 @@ - -global->browserNotice)):?> + global->novice) and $this->loadModel('tutorial')->checkNovice()):?> - + diff --git a/module/common/view/header.html.php b/module/common/view/header.html.php index 291bb398e1..69f53686b6 100755 --- a/module/common/view/header.html.php +++ b/module/common/view/header.html.php @@ -5,13 +5,23 @@ include 'chosen.html.php'; //include 'validation.html.php'; ?> +app->loadConfig('sso');?>