zen.php 132 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399
  1. <?php
  2. /**
  3. * The zen file of testcase module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Mengyi Liu <liumengyi@easycorp.ltd>
  8. * @package testcase
  9. * @link https://www.zentao.net
  10. */
  11. class testcaseZen extends testcase
  12. {
  13. /**
  14. * 检查是否有产品,如果没有则跳转到创建产品的页面。
  15. * Check products.
  16. *
  17. * @access protected
  18. * @return void
  19. */
  20. protected function checkProducts()
  21. {
  22. $objectID = 0;
  23. $tab = $this->app->tab == 'project' || $this->app->tab == 'execution' ? $this->app->tab : 'qa';
  24. if($this->app->tab == 'project')
  25. {
  26. $objectID = $this->session->project;
  27. }
  28. elseif($this->app->tab == 'execution')
  29. {
  30. $objectID = $this->session->execution;
  31. }
  32. if(empty($this->products) && (helper::isAjaxRequest('zin') || helper::isAjaxRequest('fetch'))) $this->locate($this->createLink('product', 'showErrorNone', "moduleName=$tab&activeMenu=testcase&objectID=$objectID"));
  33. }
  34. /**
  35. * 设置列表页面的 cookie。
  36. * Set browse cookie.
  37. *
  38. * @param int $productID
  39. * @param string|bool $branch
  40. * @param string $browseType
  41. * @param string $param
  42. * @access protected
  43. * @return void
  44. */
  45. protected function setBrowseCookie($productID, $branch, $browseType = '', $param = '')
  46. {
  47. helper::setcookie('preProductID', $productID);
  48. helper::setcookie('preBranch', $branch);
  49. $productChanged = $this->cookie->preProductID != $productID;
  50. $branchChanged = $this->cookie->preBranch != $branch;
  51. if($productChanged || $branchChanged || $browseType == 'bysearch')
  52. {
  53. $_COOKIE['caseModule'] = 0;
  54. helper::setcookie('caseModule', '0');
  55. }
  56. if($browseType == 'bymodule') helper::setcookie('caseModule', $param);
  57. if($browseType == 'bysuite') helper::setcookie('caseSuite', $param);
  58. }
  59. /**
  60. * 获取列表页面的 branch 参数。
  61. * Get browse branch param.
  62. *
  63. * @param string $branch
  64. * @access protected
  65. * @return string
  66. */
  67. protected function getBrowseBranch($branch)
  68. {
  69. if($branch === '') $branch = $this->cookie->preBranch;
  70. if($branch === '' || $branch === false) $branch = '0';
  71. return $branch;
  72. }
  73. /**
  74. * 设置列表页面的 session。
  75. * Set Browse session.
  76. *
  77. * @param int $productID
  78. * @param string|bool $branch
  79. * @param int $moduleID
  80. * @param string $browseType
  81. * @param string $orderBy
  82. * @access protected
  83. * @return void
  84. */
  85. protected function setBrowseSession($productID, $branch, $moduleID, $browseType = '', $orderBy = '')
  86. {
  87. if($browseType != 'bymodule') $this->session->set('caseBrowseType', $browseType);
  88. $this->session->set('caseList', $this->app->getURI(true), $this->app->tab);
  89. $this->session->set('productID', $productID);
  90. $this->session->set('branch', $branch, 'qa');
  91. $this->session->set('moduleID', $moduleID);
  92. $this->session->set('browseType', $browseType);
  93. $this->session->set('orderBy', $orderBy);
  94. $this->session->set('testcaseOrderBy', '`sort` asc', $this->app->tab);
  95. $this->session->set('testcaseOrderBy', '`sort` asc');
  96. }
  97. /**
  98. * 设置列表页面的导航。
  99. * Set menu in browse.
  100. *
  101. * @param int $productID
  102. * @param string|bool $branch
  103. * @param int $projectID
  104. * @access protected
  105. * @return array
  106. */
  107. protected function setBrowseMenu($productID, $branch, $projectID = 0)
  108. {
  109. /* 在不同的应用中,设置不同的导航。 */
  110. /* Set menu, save session. */
  111. if($this->app->tab == 'project')
  112. {
  113. if(empty($projectID)) $projectID = (int)$this->session->project;
  114. if(empty($projectID)) return array($productID, $branch);
  115. $linkedProducts = $this->product->getProducts($projectID, 'all', '', false);
  116. $this->products = count($linkedProducts) > 1 ? array('0' => $this->lang->product->all) + $linkedProducts : $linkedProducts;
  117. $productID = count($linkedProducts) > 1 ? $productID : key($linkedProducts);
  118. $hasProduct = $this->dao->findById($projectID)->from(TABLE_PROJECT)->fetch('hasProduct');
  119. if(!$hasProduct) unset($this->config->testcase->search['fields']['product']);
  120. $branch = intval($branch) > 0 ? $branch : 'all';
  121. $this->loadModel('project')->setMenu($projectID);
  122. $this->view->products = $this->products;
  123. $this->view->projectID = $projectID;
  124. $this->view->hasProduct = $hasProduct;
  125. }
  126. else
  127. {
  128. $this->loadModel('qa')->setMenu($productID, $branch);
  129. }
  130. return array($productID, $branch);
  131. }
  132. /**
  133. * 设置菜单。
  134. * Set menu.
  135. *
  136. * @param int $projectID
  137. * @param int $executionID
  138. * @param int $productID
  139. * @param string|int $branch
  140. * @access protected
  141. * @return void
  142. */
  143. protected function setMenu($projectID = 0, $executionID = 0, $productID = 0, $branch = '')
  144. {
  145. if($this->app->tab == 'project') $this->loadModel('project')->setMenu($projectID);
  146. if($this->app->tab == 'execution') $this->loadModel('execution')->setMenu($executionID);
  147. if($this->app->tab == 'qa') $this->testcase->setMenu($productID, $branch);
  148. $this->view->projectID = $projectID;
  149. $this->view->executionID = $executionID;
  150. }
  151. /**
  152. * 构建搜索表单。
  153. * Build the search form.
  154. *
  155. * @param int $productID
  156. * @param int $queryID
  157. * @param int $projectID
  158. * @access protected
  159. * @return void
  160. * @param string $actionURL
  161. */
  162. protected function buildBrowseSearchForm($productID, $queryID, $projectID, $actionURL)
  163. {
  164. if($this->app->rawModule == 'testcase') $this->config->testcase->search['onMenuBar'] = 'yes';
  165. $searchProducts = $this->product->getPairs('', 0, '', 'all');
  166. $this->testcase->buildSearchForm($productID, $searchProducts, $queryID, $actionURL, $projectID);
  167. }
  168. /**
  169. * 展示从用例库导入的用例。
  170. * Assign the cases imported from the library.
  171. *
  172. * @param int $productID
  173. * @param string $branch
  174. * @param int $libID
  175. * @param string $orderBy
  176. * @param int $queryID
  177. * @param array $libraries
  178. * @param array $cases
  179. * @access protected
  180. * @return void
  181. * @param int $projectID
  182. */
  183. protected function assignForImportFromLib($productID, $branch, $libID, $orderBy, $queryID, $libraries, $projectID, $cases)
  184. {
  185. $product = $this->loadModel('product')->getById($productID);
  186. $branches = array();
  187. if($product->type != 'normal')
  188. {
  189. $this->loadModel('branch');
  190. $branches = array(BRANCH_MAIN => $this->lang->branch->main) + $this->branch->getPairs($productID, 'active', $projectID);
  191. }
  192. foreach($branches as $branchID => $branchName) $canImportModules[$branchID] = $this->testcase->getCanImportedModules($productID, $libID, $branchID, 'items', $cases);
  193. if(empty($branches)) $canImportModules[0] = $this->testcase->getCanImportedModules($productID, $libID, 0, 'items', $cases);
  194. /* Build the search form. */
  195. $actionURL = $this->createLink('testcase', 'importFromLib', "productID={$productID}&branch={$branch}&libID={$libID}&orderBy={$orderBy}&browseType=bySearch&queryID=myQueryID");
  196. $this->config->testcase->search['module'] = 'testsuite';
  197. $this->config->testcase->search['onMenuBar'] = 'no';
  198. $this->config->testcase->search['actionURL'] = $actionURL;
  199. $this->config->testcase->search['queryID'] = $queryID;
  200. $this->config->testcase->search['fields']['lib'] = $this->lang->testcase->lib;
  201. $this->config->testcase->search['params']['lib'] = array('operator' => '=', 'control' => 'select', 'values' => array('' => '', $libID => $libraries[$libID], 'all' => $this->lang->caselib->all));
  202. $this->config->testcase->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($libID, 'caselib');
  203. if(!$this->config->testcase->needReview) unset($this->config->testcase->search['params']['status']['values']['wait']);
  204. unset($this->config->testcase->search['fields']['product']);
  205. unset($this->config->testcase->search['fields']['branch']);
  206. $this->loadModel('search')->setSearchParams($this->config->testcase->search);
  207. $this->view->productID = $productID;
  208. $this->view->branch = $branch;
  209. $this->view->libID = $libID;
  210. $this->view->orderBy = $orderBy;
  211. $this->view->queryID = $queryID;
  212. $this->view->product = $product;
  213. $this->view->branches = $branches;
  214. $this->view->canImportModules = $canImportModules;
  215. }
  216. /**
  217. * 展示创建 testcase 的相关变量。
  218. * Show the variables associated with the creation testcase.
  219. *
  220. * @param int $productID
  221. * @param string $branch
  222. * @param int $moduleID
  223. * @param string $from
  224. * @param int $param
  225. * @param int $storyID
  226. * @access protected
  227. * @return void
  228. */
  229. protected function assignCreateVars($productID, $branch = '', $moduleID = 0, $from = '', $param = 0, $storyID = 0)
  230. {
  231. $product = $this->product->getById($productID);
  232. if(!isset($this->products[$productID])) $this->products[$productID] = $product->name;
  233. $projectID = $from == 'project' ? $param : $this->session->project;
  234. $executionID = $from == 'execution' ? $param : 0;
  235. $testcaseID = $from && strpos('testcase|work|contribute', $from) !== false ? $param : 0;
  236. /* 设置分支。 */
  237. /* Set branches. */
  238. if($this->app->tab == 'project' || $this->app->tab == 'execution')
  239. {
  240. $objectID = $this->app->tab == 'project' ? $this->session->project : $executionID;
  241. $productBranches = isset($product->type) && $product->type != 'normal' ? $this->loadModel('execution')->getBranchByProduct(array($productID), $objectID, 'noclosed|withMain') : array();
  242. $branches = isset($productBranches[$productID]) ? $productBranches[$productID] : array('');
  243. $branch = empty($branch) ? (string)key($branches) : $branch;
  244. }
  245. else
  246. {
  247. $branches = isset($product->type) && $product->type != 'normal' ? $this->loadModel('branch')->getPairs($productID, 'active') : array();
  248. }
  249. /* 设置菜单。 */
  250. /* Set menu. */
  251. $executionID = $executionID ? $executionID : (int)$this->session->execution;
  252. $this->setMenu((int)$this->session->project, $executionID, $productID, $branch);
  253. /* 初始化用例数据。 */
  254. /* Initialize the testcase. */
  255. $case = $this->initTestcase($storyID, $testcaseID, $from == 'bug' ? $param : 0);
  256. if($case->story && $this->loadModel('story')->fetchByID($case->story)->product != $productID) $case->story = 0;
  257. /* 设置模块和需求。 */
  258. /* Set modules. */
  259. $this->assignModulesForCreate($productID, $moduleID, $branch, $case->story, $branches);
  260. $this->view->product = $product;
  261. $this->view->projectID = isset($projectID) ? $projectID : 0;
  262. $this->view->currentSceneID = $testcaseID > 0 ? $case->scene : (int)$this->cookie->lastCaseScene;
  263. $this->view->case = $case;
  264. $this->view->executionID = $executionID;
  265. $this->view->branch = $branch;
  266. $this->view->branches = $branches;
  267. $this->view->from = $from;
  268. $this->view->param = $param;
  269. }
  270. /**
  271. * 展示创建场景的相关变量。
  272. * Show the variables associated with the creation scene.
  273. *
  274. * @param int $productID
  275. * @param string $branch
  276. * @param int $moduleID
  277. * @access protected
  278. * @return void
  279. */
  280. protected function assignCreateSceneVars($productID, $branch = '', $moduleID = 0)
  281. {
  282. $product = $this->product->getById($productID);
  283. if(!isset($this->products[$productID])) $this->products[$productID] = $product->name;
  284. /* 获取分支键值对及当前分支。*/
  285. /* Get branch pairs and set current branch. */
  286. if($this->app->tab == 'project' || $this->app->tab == 'execution')
  287. {
  288. /* 在项目或执行中显示时只显示项目或执行关联的分支键值对。*/
  289. /* When showing in a project or execution, only the branch key-value pairs associated with the project or execution are shown. */
  290. $objectID = $this->app->tab == 'project' ? $this->session->project : 0;
  291. $productBranches = isset($product->type) && $product->type != 'normal' ? $this->loadModel('execution')->getBranchByProduct(array($productID), $objectID, 'noclosed|withMain') : array();
  292. $branches = isset($productBranches[$productID]) ? $productBranches[$productID] : array();
  293. if(!empty($branches)) $branch = (string)key($branches);
  294. }
  295. else
  296. {
  297. $branches = isset($product->type) && $product->type != 'normal' ? $this->loadModel('branch')->getPairs($productID, 'active') : array();
  298. }
  299. /* 设置菜单。 */
  300. /* Set menu. */
  301. $this->setMenu((int)$this->session->project, (int)$this->session->execution, $productID, $branch);
  302. $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->testcase->newScene;
  303. $this->view->modules = $this->tree->getOptionMenu($productID, 'case', 0, ($branch === 'all' || !isset($branches[$branch])) ? 'all' : (string)$branch);
  304. $this->view->scenes = $this->testcase->getSceneMenu($productID, $moduleID, ($branch === 'all' || !isset($branches[$branch])) ? 'all' : (string)$branch);
  305. $this->view->moduleID = $moduleID ? (int)$moduleID : (int)$this->cookie->lastCaseModule;
  306. $this->view->parent = (int)$this->cookie->lastCaseScene;
  307. $this->view->product = $product;
  308. $this->view->branch = $branch;
  309. $this->view->branches = $branches;
  310. }
  311. /**
  312. * 处理编辑场景相关的变量。
  313. * Assign variables of edit a scene.
  314. *
  315. * @param object $oldScene
  316. * @access protected
  317. * @return void
  318. */
  319. protected function assignEditSceneVars($oldScene)
  320. {
  321. $productID = $oldScene->product;
  322. $branchID = (string)$oldScene->branch;
  323. $moduleID = $oldScene->module;
  324. $parentID = $oldScene->parent;
  325. /* 设置菜单。 */
  326. /* Set menu. */
  327. $this->setMenu((int)$this->session->project, (int)$this->session->execution, $productID, $branchID);
  328. $product = $this->product->getByID($productID);
  329. if(!isset($this->products[$productID])) $this->products[$productID] = $product->name;
  330. /* Display status of branch. */
  331. $branches = array();
  332. $branchList = $this->loadModel('branch')->getList($productID, 0, 'all');
  333. foreach($branchList as $branch) $branches[$branch->id] = $branch->name . ($branch->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '');
  334. if(!isset($branches[$branchID]))
  335. {
  336. $sceneBranch = $this->branch->getByID($branchID, $productID, '');
  337. if($sceneBranch) $branches[$branchID] = $sceneBranch->name . ($sceneBranch->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '');
  338. }
  339. $modules = $this->tree->getOptionMenu($productID, 'case', 0, $branchID);
  340. if(!isset($modules[$moduleID])) $modules += $this->tree->getModulesName($moduleID);
  341. $scenes = $this->testcase->getSceneMenu($productID, $moduleID, $branchID, 0, $oldScene->id);
  342. if(!isset($scenes[$parentID])) $scenes += $this->testcase->getScenesName((array)$parentID);
  343. $this->view->title = $this->products[$productID] . $this->lang->hyphen . $this->lang->testcase->editScene;
  344. $this->view->products = $this->products;
  345. $this->view->product = $product;
  346. $this->view->branches = $branches;
  347. $this->view->modules = $modules;
  348. $this->view->scenes = $scenes;
  349. $this->view->scene = $oldScene;
  350. }
  351. /**
  352. * 展示创建 testcase 的模块变量。
  353. * Show the modules associated with the creation testcase.
  354. *
  355. * @param int $productID
  356. * @param int $moduleID
  357. * @param string $branch
  358. * @param int $storyID
  359. * @param array $branches
  360. * @access protected
  361. * @return void
  362. */
  363. protected function assignModulesForCreate($productID, $moduleID, $branch, $storyID, $branches)
  364. {
  365. if($storyID)
  366. {
  367. $story = $this->loadModel('story')->getByID($storyID);
  368. if(empty($moduleID)) $moduleID = $story->module;
  369. }
  370. $currentModuleID = !$moduleID && $productID == (int)$this->cookie->lastCaseProduct ? (int)$this->cookie->lastCaseModule : $moduleID;
  371. $this->view->currentModuleID = $currentModuleID;
  372. $this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, 'case', 0, $branch === 'all' || !isset($branches[$branch]) ? '0' : $branch);
  373. $this->view->sceneOptionMenu = $this->testcase->getSceneMenu($productID, $moduleID, $branch === 'all' || !isset($branches[$branch]) ? '0' : $branch);
  374. }
  375. /**
  376. * 处理浏览页面的用例列表。
  377. * Process cases for browse page.
  378. *
  379. * @param array $cases
  380. * @access public
  381. * @return array
  382. */
  383. public function processCasesForBrowse($cases)
  384. {
  385. if(!$cases) return [];
  386. foreach($cases as $case)
  387. {
  388. $case->caseID = $case->id;
  389. $case->id = 'case_' . $case->id; // 给用例 ID 加前缀以防止和场景 ID 重复。Add prefix to case ID to prevent it from conflicting with scene ID.
  390. $case->parent = 0;
  391. $case->isScene = false;
  392. $case->title = htmlspecialchars_decode($case->title);
  393. }
  394. $caseScenes = array_unique(array_filter(array_column($cases, 'scene'))); // 获取用例的场景 ID。Get unique scene IDs from cases.
  395. if(!$caseScenes) return $cases;
  396. $pathList = $this->dao->select('path')->from(TABLE_SCENE)->where('deleted')->eq('0')->andWhere('id')->in($caseScenes)->fetchPairs(); // 获取场景的路径列表。Get path list of scenes.
  397. $idList = array_unique(array_filter(explode(',', implode(',', $pathList))));
  398. $scenes = $this->dao->select('*')->from(TABLE_SCENE)->where('deleted')->eq('0')->andWhere('id')->in($idList)->orderBy('grade_desc, sort_asc')->fetchAll('id'); // 获取场景列表。Get scene list.
  399. foreach($cases as $case)
  400. {
  401. if(!isset($scenes[$case->scene])) continue;
  402. $scene = $scenes[$case->scene];
  403. $scene->hasCase = true;
  404. $case->parent = $scene->id;
  405. $case->grade = $scene->grade + 1;
  406. $case->path = $scene->path . $case->id . ',';
  407. }
  408. $this->dao->setTable(TABLE_CASE);
  409. $fieldTypes = $this->dao->getFieldsType();
  410. foreach($scenes as $scene)
  411. {
  412. $scene = $this->testcase->buildSceneBaseOnCase($scene, $fieldTypes);
  413. $scene->scene = $scene->parent;
  414. if(isset($scene->hasCase))
  415. {
  416. if (isset($scenes[$scene->parent])) $scenes[$scene->parent]->hasCase = true; // 如果子场景有用例,那么父场景也有用例。If the child scene has a use case, the parent scene also has a use case.
  417. }
  418. else
  419. {
  420. $scene->hasCase = false; // hasCase 为 false 时在页面上显示“暂无用例”。If hasCase is false, show "No use case" on the page.
  421. }
  422. }
  423. return array_merge($scenes, $cases);
  424. }
  425. /**
  426. * 指定浏览用例页面的用例列表。
  427. * Assign cases for browse page.
  428. *
  429. * @param int $productID
  430. * @param string $branch
  431. * @param string $browseType
  432. * @param int $queryID
  433. * @param int $moduleID
  434. * @param string $caseType
  435. * @param string $orderBy
  436. * @param int $recTotal
  437. * @param int $recPerPage
  438. * @param int $pageID
  439. * @access protected
  440. * @return void
  441. * @param string $from
  442. */
  443. protected function assignCasesForBrowse($productID, $branch, $browseType, $queryID, $moduleID, $caseType, $orderBy, $recTotal, $recPerPage, $pageID, $from = 'testcase')
  444. {
  445. $this->app->loadClass('pager', $static = true);
  446. $pager = new pager($recTotal, $recPerPage, $pageID);
  447. $sort = common::appendOrder($orderBy);
  448. if(strpos($sort, 'caseID') !== false) $sort = str_replace('caseID', 'id', $sort);
  449. $cases = $this->testcase->getTestCases($productID, $branch, $browseType, $queryID, $moduleID, $caseType, 'no', $sort, $pager, $from);
  450. $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', false);
  451. $cases = $this->loadModel('story')->checkNeedConfirm($cases);
  452. $cases = $this->testcase->appendData($cases);
  453. if($this->config->edition != 'open')
  454. {
  455. $caseRelatedObjectList = $this->loadModel('custom')->getRelatedObjectList(array_keys($cases), 'testcase', 'byRelation', true);
  456. foreach($cases as $caseID => $case) $case->relatedObject = zget($caseRelatedObjectList, $caseID, 0);
  457. }
  458. $this->view->cases = $this->processCasesForBrowse($cases);
  459. $this->view->orderBy = $orderBy;
  460. $this->view->pager = $pager;
  461. }
  462. /**
  463. *
  464. * 指定模块树。
  465. * Assign module tree in browse page.
  466. *
  467. * @param int $productID
  468. * @param string $branch
  469. * @param int $projectID
  470. * @access protected
  471. * @return void
  472. */
  473. protected function assignModuleTreeForBrowse($productID, $branch, $projectID)
  474. {
  475. if($projectID && empty($productID))
  476. {
  477. $this->view->moduleTree = $this->tree->getCaseTreeMenu($projectID, $productID, 0, array('treeModel', 'createCaseLink'));
  478. }
  479. else
  480. {
  481. $this->view->moduleTree = $this->tree->getTreeMenu($productID, 'case', 0, array('treeModel', 'createCaseLink'), array('projectID' => $projectID, 'productID' => $productID), $branch);
  482. }
  483. }
  484. /**
  485. *
  486. * 指定产品和分支。
  487. * Assign product and branch.
  488. *
  489. * @param int $productID
  490. * @param string $branch
  491. * @param int $projectID
  492. * @access protected
  493. * @return void
  494. */
  495. protected function assignProductAndBranchForBrowse($productID, $branch, $projectID = 0)
  496. {
  497. /* 根据产品类型判断是否展示分支,获取分支选项信息和带标签的分支选项信息。*/
  498. /* Judge whether to show branch according to the type of product, get branch option and branch tag option. */
  499. $product = $this->product->getByID($productID);
  500. $showBranch = false;
  501. $branchOption = array();
  502. $branchTagOption = array();
  503. if($product && $product->type != 'normal')
  504. {
  505. /* Display of branch label. */
  506. $showBranch = $this->loadModel('branch')->showBranch($productID);
  507. /* Display status of branch. */
  508. $branches = $this->loadModel('branch')->getList($productID, $projectID, 'all');
  509. foreach($branches as $branchInfo)
  510. {
  511. $branchOption[$branchInfo->id] = $branchInfo->name;
  512. $branchTagOption[$branchInfo->id] = $branchInfo->name . ($branchInfo->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '');
  513. }
  514. }
  515. $this->view->productID = $productID;
  516. $this->view->productName = zget($this->products, $productID, '');
  517. $this->view->product = $product;
  518. $this->view->branch = (!empty($product) and $product->type != 'normal') ? $branch : 0;
  519. $this->view->branchOption = $branchOption;
  520. $this->view->branchTagOption = $branchTagOption;
  521. }
  522. /**
  523. * 指定变量。
  524. * Assign variables.
  525. *
  526. * @param int $productID
  527. * @param string $branch
  528. * @param string $browseType
  529. * @param int $projectID
  530. * @param int $param
  531. * @param int $moduleID
  532. * @param int $suiteID
  533. * @param string $caseType
  534. * @access protected
  535. * @return void
  536. */
  537. protected function assignForBrowse($productID, $branch, $browseType, $projectID, $param, $moduleID, $suiteID, $caseType)
  538. {
  539. $showModule = !empty($this->config->testcase->browse->showModule) ? $this->config->testcase->browse->showModule : '';
  540. $tree = $moduleID ? $this->tree->getByID($moduleID) : '';
  541. $this->view->title = zget($this->products, $productID, '') . $this->lang->hyphen . $this->lang->testcase->common;
  542. $this->view->projectID = $projectID;
  543. $this->view->projectType = !empty($projectID) ? $this->dao->select('model')->from(TABLE_PROJECT)->where('id')->eq($projectID)->fetch('model') : '';
  544. $this->view->browseType = $browseType;
  545. $this->view->param = $param;
  546. $this->view->moduleID = $moduleID;
  547. $this->view->moduleName = $moduleID ? $tree->name : $this->lang->tree->all;
  548. $this->view->suiteID = $suiteID;
  549. $this->view->caseType = $caseType;
  550. $this->view->users = $this->user->getPairs('noletter');
  551. $this->view->modules = $this->tree->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, $branch == 'all' ? '0' : $branch);
  552. $this->view->iscenes = $this->testcase->getSceneMenu($productID, $moduleID);
  553. $this->view->suiteList = $this->loadModel('testsuite')->getSuites($productID);
  554. $this->view->modulePairs = $showModule ? $this->tree->getModulePairs($productID, 'case', $showModule) : array();
  555. $this->view->libraries = $this->loadModel('caselib')->getLibraries();
  556. $this->view->stories = array('') + $this->loadModel('story')->getPairs($productID);
  557. $this->view->automation = $this->loadModel('zanode')->getAutomationByProduct($productID);
  558. if($projectID)
  559. {
  560. $productPairs = $this->product->getProductPairsByProject($projectID);
  561. $this->view->switcherParams = "projectID={$projectID}&productID={$productID}&currentMethod=testcase";
  562. $this->view->switcherText = zget($productPairs, $productID, $this->lang->product->all);
  563. $this->view->project = $this->loadModel('project')->getByID($projectID);
  564. $this->view->switcherObjectID = $productID;
  565. }
  566. }
  567. /**
  568. * 处理更新请求数据。
  569. * Processing request data.
  570. *
  571. * @param form $formData
  572. * @param object $oldBug
  573. * @access protected
  574. * @return object|false
  575. * @param object $oldCase
  576. */
  577. protected function prepareEditExtras($formData, $oldCase)
  578. {
  579. foreach($formData->data->expects as $key => $value)
  580. {
  581. if(!empty($value) && empty($formData->data->steps[$key]))
  582. {
  583. dao::$errors['message'][] = sprintf($this->lang->testcase->stepsEmpty, $key);
  584. return false;
  585. }
  586. }
  587. if(!empty($_FILES['scriptFile'])) unset($_FILES['scriptFile']);
  588. $result = $this->getStatusForUpdate($oldCase);
  589. if(!$result || !is_array($result)) return $result;
  590. list($stepChanged, $status) = $result;
  591. $case = $formData->add('id', $oldCase->id)
  592. ->add('version', $stepChanged ? (int)$oldCase->version + 1 : (int)$oldCase->version)
  593. ->add('lastEditedBy', $this->app->user->account)
  594. ->add('lastEditedDate', helper::now())
  595. ->add('stepChanged', $stepChanged)
  596. ->setForce('status', $status)
  597. ->setDefault('story,branch', 0)
  598. ->setDefault('stage', '')
  599. ->setDefault('deleteFiles', array())
  600. ->setDefault('storyVersion', $oldCase->storyVersion)
  601. ->setDefault('script', $oldCase->script)
  602. ->setIF($formData->data->story != false && $formData->data->story != $oldCase->story, 'storyVersion', $this->loadModel('story')->getVersion((int)$formData->data->story))
  603. ->setIF(!$formData->data->linkCase, 'linkCase', '')
  604. ->setIF($formData->data->auto == 'auto' && $formData->data->script, 'script', htmlentities($formData->data->script))
  605. ->setIF($formData->data->auto == 'no', 'script', '')
  606. ->join('stage', ',')
  607. ->join('linkCase', ',')
  608. ->cleanInt('story,product,branch,module')
  609. ->skipSpecial('script')
  610. ->remove('files,labels,scriptFile,scriptName')
  611. ->removeIF($formData->data->auto == 'auto' && !$formData->data->script, 'script')
  612. ->get();
  613. if(!empty($oldCase->lib) && empty($oldCase->product) && !empty($_POST['lib'])) $case->lib = $this->post->lib;
  614. return !empty($this->config->testcase->editor->edit['id']) ? $this->loadModel('file')->processImgURL($case, $this->config->testcase->editor->edit['id'], $this->post->uid) : $case;
  615. }
  616. /**
  617. * 提前处理用例数据。
  618. * Preprocess case.
  619. *
  620. * @param object $case
  621. * @access protected
  622. * @return object
  623. */
  624. protected function preProcessForEdit($case)
  625. {
  626. /* 初始化用例步骤。*/
  627. /* Unit the steps of case. */
  628. if(empty($case->steps))
  629. {
  630. $step = new stdclass();
  631. $step->type = 'step';
  632. $step->desc = '';
  633. $step->name = '';
  634. $step->expect = '';
  635. $case->steps[] = $step;
  636. }
  637. return $case;
  638. }
  639. /**
  640. * 设置编辑用例库用例的导航。
  641. * Set menu for editing lib case.
  642. *
  643. * @param object $case
  644. * @param array $libraries
  645. * @access protected
  646. * @return void
  647. */
  648. protected function setMenuForLibCaseEdit($case, $libraries)
  649. {
  650. if($this->app->tab == 'project')
  651. {
  652. $this->loadModel('project')->setMenu($this->session->project);
  653. }
  654. else
  655. {
  656. $this->loadModel('caselib')->setLibMenu($libraries, $case->lib);
  657. }
  658. }
  659. /**
  660. * 设置编辑用例的导航。
  661. * Set menu for editing case.
  662. *
  663. * @param object $case
  664. * @param int $executionID
  665. * @access protected
  666. * @return void
  667. */
  668. protected function setMenuForCaseEdit($case, $executionID)
  669. {
  670. if($this->app->tab == 'project')
  671. {
  672. $this->loadModel('project')->setMenu($case->project);
  673. $this->view->projectID = $case->project;
  674. }
  675. if($this->app->tab == 'execution')
  676. {
  677. if(!$executionID) $executionID = $case->execution;
  678. $this->loadModel('execution')->setMenu($executionID);
  679. $this->view->executionID = $executionID;
  680. }
  681. if($this->app->tab == 'qa') $this->testcase->setMenu($case->product, $case->branch);
  682. }
  683. /**
  684. * 指定编辑用例库用例的数据。
  685. * Assign data for editint lib case.
  686. *
  687. * @param object $case
  688. * @param array $libraries
  689. * @access protected
  690. * @return void
  691. */
  692. protected function assignForEditLibCase($case, $libraries)
  693. {
  694. $this->view->title = "CASE #$case->id $case->title - " . $libraries[$case->lib];
  695. $this->view->isLibCase = true;
  696. $this->view->libraries = $libraries;
  697. $this->view->moduleOptionMenu = $this->tree->getOptionMenu($case->lib, 'caselib', 0);
  698. $this->view->libName = $libraries[$case->lib];
  699. $this->view->libID = $case->lib;
  700. }
  701. /**
  702. * 指定编辑用例的数据。
  703. * Assign for editing case.
  704. *
  705. * @param object $case
  706. * @param int $executionID
  707. * @access protected
  708. * @return void
  709. */
  710. protected function assignForEditCase($case, $executionID)
  711. {
  712. $product = $this->product->getByID($case->product);
  713. if(!isset($this->products[$case->product])) $this->products[$case->product] = $product->name;
  714. $this->view->title = $this->products[$case->product] . $this->lang->hyphen . $this->lang->testcase->edit;
  715. $this->view->isLibCase = false;
  716. $this->view->product = $product;
  717. $this->view->products = $this->products;
  718. $this->view->branch = $this->cookie->preBranch;
  719. $this->assignBranchForEdit($case, $executionID);
  720. $this->assignModuleOptionMenuForEdit($case);
  721. }
  722. /**
  723. * 指定编辑用例的分支。
  724. * Assign branch data for editing case.
  725. *
  726. * @param object $case
  727. * @param int $executionID
  728. * @access private
  729. * @return void
  730. */
  731. private function assignBranchForEdit($case, $executionID)
  732. {
  733. $objectID = 0;
  734. if($this->app->tab == 'execution') $objectID = $executionID;
  735. if($this->app->tab == 'project') $objectID = $case->project;
  736. $branches = $this->loadModel('branch')->getList($case->product, $objectID, 'all');
  737. $branchTagOption = array();
  738. foreach($branches as $branchInfo) $branchTagOption[$branchInfo->id] = $branchInfo->name . ($branchInfo->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : '');
  739. if(!isset($branchTagOption[$case->branch]))
  740. {
  741. $caseBranch = $this->branch->getByID($case->branch, $case->product, '');
  742. $branchTagOption[$case->branch] = $case->branch == BRANCH_MAIN ? $caseBranch : ($caseBranch->name . ($caseBranch->status == 'closed' ? ' (' . $this->lang->branch->statusList['closed'] . ')' : ''));
  743. }
  744. $this->view->branchTagOption = $branchTagOption;
  745. }
  746. /**
  747. * 指定编辑用的模块选项。
  748. * Assign module option menu for editing case.
  749. *
  750. * @param object $case
  751. * @access private
  752. * @return void
  753. */
  754. private function assignModuleOptionMenuForEdit($case)
  755. {
  756. $moduleOptionMenu = $this->tree->getOptionMenu($case->product, 'case', 0, (string)$case->branch);
  757. if($case->lib && $case->fromCaseID)
  758. {
  759. $lib = $this->loadModel('caselib')->getByID($case->lib);
  760. $libModules = $this->tree->getOptionMenu($case->lib, 'caselib');
  761. foreach($libModules as $moduleID => $moduleName)
  762. {
  763. if($moduleID == 0) continue;
  764. $moduleOptionMenu[$moduleID] = $lib->name . $moduleName;
  765. }
  766. }
  767. if(!isset($moduleOptionMenu[$case->module])) $moduleOptionMenu += $this->tree->getModulesName((array)$case->module);
  768. $this->view->moduleOptionMenu = $moduleOptionMenu;
  769. }
  770. /**
  771. * 指定编辑用例的其他数据。
  772. * Assign other data for editing case.
  773. *
  774. * @param int $productID
  775. * @param object $case
  776. * @param array $testtasks
  777. * @access protected
  778. * @return void
  779. */
  780. protected function assignForEdit($productID, $case, $testtasks)
  781. {
  782. $sceneOptionMenu = $this->testcase->getSceneMenu($productID, $case->module);
  783. if(!isset($sceneOptionMenu[$case->scene])) $sceneOptionMenu += $this->testcase->getScenesName((array)$case->scene);
  784. $forceNotReview = $this->testcase->forceNotReview();
  785. if($forceNotReview) unset($this->lang->testcase->statusList['wait']);
  786. $this->view->case = $case;
  787. $this->view->testtasks = $testtasks;
  788. $this->view->forceNotReview = $forceNotReview;
  789. $this->view->sceneOptionMenu = $sceneOptionMenu;
  790. $this->view->users = $this->user->getPairs('noletter');
  791. $this->view->actions = $this->loadModel('action')->getList('case', $case->id);
  792. }
  793. /**
  794. * 指定批量编辑用例的数据。
  795. * Assign for editing case.
  796. *
  797. * @param int $productID
  798. * @param string $branch
  799. * @param string $type
  800. * @param array $cases
  801. * @access protected
  802. * @return void
  803. */
  804. protected function assignForBatchEdit($productID, $branch, $type, $cases)
  805. {
  806. list($productIdList, $libIdList) = $this->assignTitleForBatchEdit($productID, $branch, $type, $cases);
  807. /* 设置模块。 */
  808. /* Set modules. */
  809. $modules = array();
  810. $branchProduct = false;
  811. $branchTagOption = array();
  812. $products = $this->product->getByIdList($productIdList);
  813. $branches = array(0 => 0);
  814. foreach($products as $product)
  815. {
  816. if($product->type != 'normal')
  817. {
  818. $branches = $this->loadModel('branch')->getList($product->id, 0, 'all');
  819. foreach($branches as $branchInfo) $branchTagOption[$product->id][$branchInfo->id] = "/{$product->name}/{$branchInfo->name}" . ($branchInfo->status == 'closed' ? " ({$this->lang->branch->statusList['closed']})" : '');
  820. $branchProduct = true;
  821. }
  822. $modulePairs = $this->tree->getOptionMenu($product->id, 'case', 0, array_keys($branches));
  823. foreach($modulePairs as $branchID => $branchModules)
  824. {
  825. $modules['case'][$product->id][$branchID] = array();
  826. foreach($branchModules as $moduleID => $module) $modules['case'][$product->id][$branchID][] = array('text' => $module, 'value' => $moduleID);
  827. }
  828. }
  829. foreach($libIdList as $libID)
  830. {
  831. $libModules = $this->tree->getOptionMenu($libID, 'caselib');
  832. $modules['lib'][$libID][0] = array();
  833. foreach($libModules as $moduleID => $module) $modules['lib'][$libID][0][] = array('text' => $module, 'value' => $moduleID);
  834. }
  835. if($this->app->tab == 'project') $branchTagOption = $this->loadModel('branch')->getPairsByProjectProduct($this->session->project, $productID);
  836. /* 指派模块和场景。 */
  837. /* Assign modules and scenes. */
  838. $this->assignModuleAndSceneForBatchEdit($productID, $branch, $branches, $cases, $modules);
  839. /* 设置自定义字段。 */
  840. /* Set custom fields. */
  841. foreach(explode(',', $this->config->testcase->list->customBatchEditFields) as $field) $customFields[$field] = $this->lang->testcase->$field;
  842. $this->view->customFields = $customFields;
  843. $this->view->showFields = $this->config->testcase->custom->batchEditFields;
  844. $this->view->branchTagOption = $branchTagOption;
  845. $this->view->products = $products;
  846. $this->view->branchProduct = $branchProduct;
  847. }
  848. /**
  849. * 指派用例库下批量编辑用例的数据。
  850. * Assign for batch editing case in caselib.
  851. *
  852. * @param int $libID
  853. * @access protected
  854. * @return void
  855. */
  856. protected function assignLibForBatchEdit($libID)
  857. {
  858. $libraries = $this->loadModel('caselib')->getLibraries();
  859. $this->loadModel('caselib')->setLibMenu($libraries, $libID);
  860. $this->view->libID = $libID;
  861. }
  862. /**
  863. * 指定批量编辑用例的页面标题。
  864. * Assign title for editing case.
  865. *
  866. * @param int $productID
  867. * @param string $branch
  868. * @param string $type
  869. * @param array $cases
  870. * @access protected
  871. * @return array
  872. */
  873. protected function assignTitleForBatchEdit($productID, $branch, $type, $cases)
  874. {
  875. $productIdList = array();
  876. $libIdList = array();
  877. /* 指派用例库用例。 */
  878. /* Assign lib cases. */
  879. if($type == 'lib')
  880. {
  881. $libID = $productID;
  882. $libIdList = array($libID);
  883. $libraries = $this->loadModel('caselib')->getLibraries();
  884. /* Remove story custom fields from caselib */
  885. $this->config->testcase->list->customBatchEditFields = str_replace(',story', '', $this->config->testcase->list->customBatchEditFields);
  886. $this->config->testcase->custom->batchEditFields = str_replace(',story', '', $this->config->testcase->custom->batchEditFields);
  887. /* Set caselib menu. */
  888. $this->caselib->setLibMenu($libraries, $libID);
  889. $this->view->title = $libraries[$libID] . $this->lang->hyphen . $this->lang->testcase->batchEdit;
  890. }
  891. /* 指派测试用例。 */
  892. /* Assign test cases. */
  893. elseif($productID)
  894. {
  895. $product = $this->product->getByID($productID);
  896. $productIdList = array($productID);
  897. $this->setMenu((int)$this->session->project, (int)$this->session->execution, $productID, $branch);
  898. $this->view->title = $product->name . $this->lang->hyphen . $this->lang->testcase->batchEdit;
  899. }
  900. /* 指派地盘标签下的用例。 */
  901. /* Assign cases of my tab. */
  902. else
  903. {
  904. foreach($cases as $case)
  905. {
  906. if($case->lib == 0) $productIdList[$case->product] = $case->product;
  907. if($case->lib > 0) $libIdList[$case->lib] = $case->lib;
  908. }
  909. $this->view->title = $this->lang->testcase->batchEdit;
  910. }
  911. return array($productIdList, $libIdList);
  912. }
  913. /**
  914. * 指定批量编辑用例的模块和场景标题。
  915. * Assign modules and scenes for editing case.
  916. *
  917. * @param int $productID
  918. * @param string $branch
  919. * @param array $branches
  920. * @param array $cases
  921. * @param array $modules
  922. * @access protected
  923. * @return void
  924. */
  925. protected function assignModuleAndSceneForBatchEdit($productID, $branch, $branches, $cases, $modules)
  926. {
  927. $moduleScenes = array();
  928. $moduleScenesPairs = array();
  929. $modulePairs = array();
  930. $scenePairs = array();
  931. foreach($cases as $case)
  932. {
  933. $case->story = $case->story ? $case->story : '';
  934. /* 设置用例模块。 */
  935. /* Set case module. */
  936. $objectID = $case->lib > 0 ? $case->lib : $case->product;
  937. $objectType = $case->lib > 0 ? 'lib' : 'case';
  938. if(isset($modules[$objectType][$objectID][$case->branch]))
  939. {
  940. $modulePairs[$case->id] = $modules[$objectType][$objectID][$case->branch];
  941. }
  942. elseif(isset($modules[$objectType][$objectID]))
  943. {
  944. $modulePairs[$case->id] = $modules[$objectType][$objectID][0];
  945. }
  946. else
  947. {
  948. $module = $this->tree->getByID($case->module);
  949. $modulePairs[$case->id][] = array('text' => zget($module, 'name', ''), 'value' => $case->module);
  950. }
  951. /* 设置用例场景。 */
  952. /* Set case 场景. */
  953. if(!isset($moduleScenesPairs[$case->module]))
  954. {
  955. $moduleScenesPairs[$case->module] = array();
  956. $moduleScenes = $this->testcase->getSceneMenu($productID, $case->module, $branch !== 'all' && !isset($branches[$branch]) ? '0' : $branch);
  957. foreach($moduleScenes as $sceneID => $scene) $moduleScenesPairs[$case->module][] = array('text' => $scene, 'value' => $sceneID);
  958. }
  959. $scenePairs[$case->id][] = $moduleScenesPairs[$case->module];
  960. if(!isset($scenes[$case->scene])) $scenePairs[$case->id][] = array('text' => '/' . $this->testcase->fetchSceneName($case->scene), 'value' => $case->scene);
  961. }
  962. $this->view->scenePairs = $scenePairs;
  963. $this->view->modulePairs = $modulePairs;
  964. }
  965. /**
  966. * 指派导入的数据。
  967. * Assign show imported data.
  968. *
  969. * @param int $productID
  970. * @param string $branch
  971. * @param array $caseData
  972. * @param int $stepVars
  973. * @param int $pagerID
  974. * @param int $maxImport
  975. * @access protected
  976. * @return void
  977. */
  978. protected function assignShowImportVars($productID, $branch, $caseData, $stepVars, $pagerID, $maxImport)
  979. {
  980. if(empty($caseData)) return $this->sendError($this->lang->error->noData, $this->createLink('testcase', 'browse', "productID={$productID}&branch={$branch}"));
  981. /* 设置模块。 */
  982. /* Set modules. */
  983. $modules = array();
  984. $branches = $this->loadModel('branch')->getPairs($productID, 'active');
  985. $branchModules = $this->loadModel('tree')->getOptionMenu($productID, 'case', 0, empty($branches) ? array(0) : array_keys($branches));
  986. foreach($branchModules as $branchID => $moduleList)
  987. {
  988. $modules[$branchID] = array();
  989. foreach($moduleList as $moduleID => $moduleName)
  990. {
  991. $modules[$branchID][$moduleID] = $moduleName;
  992. }
  993. }
  994. /* 如果导入的用例数大于最大导入数,则在最大导入时截取。 */
  995. /* If the number of imported cases is greater than max import, intercept at max import. */
  996. $allCount = count($caseData);
  997. $allPager = 1;
  998. if($allCount > $this->config->file->maxImport)
  999. {
  1000. if(empty($maxImport))
  1001. {
  1002. $this->view->allCount = $allCount;
  1003. $this->view->maxImport = $maxImport;
  1004. $this->view->productID = $productID;
  1005. $this->view->branch = $branch;
  1006. $this->display();
  1007. exit;
  1008. }
  1009. $allPager = ceil($allCount / $maxImport);
  1010. $caseData = array_slice($caseData, ($pagerID - 1) * $maxImport, $maxImport, true);
  1011. }
  1012. if(empty($caseData)) return $this->sendError($this->lang->error->noData, $this->createLink('testcase', 'browse', "productID={$productID}&branch={$branch}"));
  1013. /* 判断输入的用例是否超出限制,并设定session。 */
  1014. /* Judge whether the imported cases is too large and set session. */
  1015. $countInputVars = count($caseData) * 12 + $stepVars;
  1016. $showSuhosinInfo = common::judgeSuhosinSetting($countInputVars);
  1017. if($showSuhosinInfo) $this->view->suhosinInfo = extension_loaded('suhosin') ? sprintf($this->lang->suhosinInfo, $countInputVars) : sprintf($this->lang->maxVarsInfo, $countInputVars);
  1018. $this->view->modules = $modules;
  1019. $this->view->caseData = $caseData;
  1020. $this->view->branches = $branches;
  1021. $this->view->allCount = $allCount;
  1022. $this->view->allPager = $allPager;
  1023. $this->view->isEndPage = $pagerID >= $allPager;
  1024. $this->view->pagerID = $pagerID;
  1025. }
  1026. /**
  1027. * Add edit action.
  1028. *
  1029. * @param int $caseID
  1030. * @param string $oldStatus
  1031. * @param string $status
  1032. * @param array $changes
  1033. * @param string $comment
  1034. * @access protected
  1035. * @return void
  1036. */
  1037. protected function addEditAction($caseID, $oldStatus, $status, $changes = array(), $comment = '')
  1038. {
  1039. $this->loadModel('action');
  1040. $action = !empty($changes) ? 'Edited' : 'Commented';
  1041. $actionID = $this->action->create('case', $caseID, $action, $comment);
  1042. $this->action->logHistory($actionID, $changes);
  1043. if($oldStatus != 'wait' && $status == 'wait') $this->action->create('case', $caseID, 'submitReview');
  1044. }
  1045. /**
  1046. * 指定 testcase 查看的关联的变量。
  1047. * Assign testcase related variables.
  1048. *
  1049. * @param object $case
  1050. * @param string $from
  1051. * @param int $taskID
  1052. * @access protected
  1053. * @return void
  1054. */
  1055. protected function assignCaseForView($case, $from, $taskID)
  1056. {
  1057. $case = $this->loadModel('story')->checkNeedConfirm($case);
  1058. if($from == 'testtask')
  1059. {
  1060. $run = $this->loadModel('testtask')->getRunByCase($taskID, $case->id);
  1061. $case->assignedTo = $run->assignedTo;
  1062. $case->lastRunner = $run->lastRunner;
  1063. $case->lastRunDate = $run->lastRunDate;
  1064. $case->lastRunResult = $run->lastRunResult;
  1065. $case->caseStatus = $case->status;
  1066. $case->status = $run->status;
  1067. $results = $this->testtask->getResults($run->id);
  1068. $result = array_shift($results);
  1069. if($result)
  1070. {
  1071. $case->xml = $result->xml;
  1072. $case->duration = $result->duration;
  1073. }
  1074. }
  1075. $case = $this->testcase->appendCaseFails($case, $from, $taskID);
  1076. $case = $this->processStepsForMindMap($case);
  1077. $sceneOptionMenu = $this->testcase->getSceneMenu($case->product, $case->module);
  1078. if(!isset($sceneOptionMenu[$case->scene])) $sceneOptionMenu += $this->testcase->getScenesName((array)$case->scene);
  1079. $this->view->from = $from;
  1080. $this->view->taskID = $taskID;
  1081. $this->view->runID = $from == 'testcase' || $from == 'testsuite' ? 0 : $run->id;
  1082. $this->view->case = $case;
  1083. $this->view->caseFails = $case->caseFails;
  1084. $this->view->modulePath = $this->tree->getParents($case->module);
  1085. $this->view->caseModule = empty($case->module) ? '' : $this->tree->getById($case->module);
  1086. $this->view->preAndNext = !isOnlybody() ? $this->loadModel('common')->getPreAndNextObject('testcase', $case->id) : '';
  1087. $this->view->users = $this->user->getPairs('noletter');
  1088. $this->view->actions = $this->loadModel('action')->getList('case', $case->id);
  1089. $this->view->scenes = $sceneOptionMenu;
  1090. }
  1091. /**
  1092. * 指定批量创建用例的相关变量。
  1093. * Assign for batch creating case.
  1094. *
  1095. * @param int $productID
  1096. * @param string $branch
  1097. * @param int $moduleID
  1098. * @param int $storyID
  1099. * @access protected
  1100. * @return void
  1101. */
  1102. protected function assignForBatchCreate($productID, $branch = '', $moduleID = 0, $storyID = 0)
  1103. {
  1104. $product = $this->product->getById($productID);
  1105. /* 设置分支。 */
  1106. /* Set branches. */
  1107. if($this->app->tab == 'project' and $product->type != 'normal')
  1108. {
  1109. $this->lang->product->branch = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
  1110. $productBranches = $this->loadModel('execution')->getBranchByProduct(array($productID), $this->session->project, 'noclosed|withMain');
  1111. $branches = isset($productBranches[$productID]) ? $productBranches[$productID] : array();
  1112. $branch = key($branches);
  1113. }
  1114. else
  1115. {
  1116. $branches = $this->loadModel('branch')->getPairs($productID, 'active');
  1117. }
  1118. /* 设置自定义字段和展示字段。 */
  1119. /* Set custom fields and show fields. */
  1120. foreach(explode(',', $this->config->testcase->list->customBatchCreateFields) as $field)
  1121. {
  1122. if($product->type != 'normal') $customFields[$product->type] = $this->lang->product->branchName[$product->type];
  1123. $customFields[$field] = $this->lang->testcase->$field;
  1124. }
  1125. $showFields = sprintf($this->config->testcase->custom->batchCreateFields, $product->type);
  1126. if($product->type == 'normal')
  1127. {
  1128. $showFields = str_replace(array(",branch,", ",platform,"), '', ",$showFields,");
  1129. $showFields = trim($showFields, ',');
  1130. }
  1131. /* 设置需求键对。 */
  1132. /* Set story pairs. */
  1133. $storyPairs = $this->loadModel('story')->getProductStoryPairs($productID, $branch === 'all' ? 0 : $branch, array(), 'active,reviewing', 'id_desc', 50, '', 'story', false);
  1134. $story = $storyID ? $this->story->fetchByID($storyID) : '';
  1135. $storyPairs += $storyID ? array($storyID => $story->id . ':' . $story->title) : array();
  1136. if($storyID && empty($moduleID)) $moduleID = $story->module;
  1137. $this->view->product = $product;
  1138. $this->view->branches = $branches;
  1139. $this->view->customFields = $customFields;
  1140. $this->view->showFields = $showFields;
  1141. $this->view->story = $story;
  1142. $this->view->storyPairs = $this->story->addGradeLabel($storyPairs);
  1143. $this->view->sceneOptionMenu = $this->testcase->getSceneMenu($productID, $moduleID, $branch === 'all' || !isset($branches[$branch]) ? '0' : $branch);
  1144. $this->view->currentModuleID = $moduleID;
  1145. }
  1146. /**
  1147. * 构建创建 testcase 页面数据。
  1148. * Build form fields for creating testcase.
  1149. *
  1150. * @param string $from
  1151. * @param int $param
  1152. * @access protected
  1153. * @return void
  1154. */
  1155. protected function buildCaseForCreate($from, $param)
  1156. {
  1157. $status = $this->getStatusForCreate();
  1158. $case = form::data($this->config->testcase->form->create)
  1159. ->add('status', $status)
  1160. ->setIF($from == 'bug', 'fromBug', $param)
  1161. ->setIF($from == 'project' && $param, 'project', $param)
  1162. ->setIF($from == 'execution' && $param, 'execution', $param)
  1163. ->setIF($from != 'project' && $this->app->tab == 'project', 'project', $this->session->project)
  1164. ->setIF($from != 'execution' && $this->app->tab == 'execution', 'execution', $this->session->execution)
  1165. ->setIF($this->post->project, 'project', $this->post->project) // API will post project param
  1166. ->setIF($this->post->execution, 'execution', $this->post->execution) // API will post execution param
  1167. ->setIF($this->post->auto, 'auto', 'auto')
  1168. ->setIF($this->post->auto && $this->post->script, 'script', $this->post->script ? htmlentities($this->post->script) : '')
  1169. ->setIF($this->post->story, 'storyVersion', $this->loadModel('story')->getVersion((int)$this->post->story))
  1170. ->get();
  1171. /* 如果用例产品是影子产品,同步用例到项目中。 */
  1172. $product = $this->loadModel('product')->getById($case->product);
  1173. if($product->shadow && empty($case->project))
  1174. {
  1175. $project = $this->loadModel('project')->getByShadowProduct($case->product);
  1176. $case->project = $project->id;
  1177. }
  1178. /* 如果用例的项目不为空,且不启用迭代,同步用例到影子迭代中。 */
  1179. if(!empty($case->project))
  1180. {
  1181. if(!isset($project)) $project = $this->loadModel('project')->fetchByID($case->project);
  1182. if(empty($project->multiple)) $case->execution = $this->loadModel('execution')->getNoMultipleID($case->project);
  1183. }
  1184. return !empty($this->config->testcase->editor->create['id']) ? $this->loadModel('file')->processImgURL($case, $this->config->testcase->editor->create['id'], $this->post->uid) : $case;
  1185. }
  1186. /**
  1187. * 构建批量创建用例的数据。
  1188. * Build cases for batch creating.
  1189. *
  1190. * @param int $productID
  1191. * @access protected
  1192. * @return array
  1193. */
  1194. protected function buildCasesForBathcCreate($productID)
  1195. {
  1196. $now = helper::now();
  1197. $account = $this->app->user->account;
  1198. $forceNotReview = $this->testcase->forceNotReview();
  1199. $storyVersions = array();
  1200. $testcases = form::batchData($this->config->testcase->form->batchCreate)->get();
  1201. $projectID = $this->app->tab == 'project' ? $this->session->project : 0;
  1202. $executionID = 0;
  1203. if($projectID)
  1204. {
  1205. $project = $this->loadModel('project')->getByID($projectID);
  1206. if(!$project->multiple) $executionID = $this->dao->select('id')->from(TABLE_PROJECT)->where('parent')->eq($projectID)->fetch('id');
  1207. }
  1208. foreach($testcases as $testcase)
  1209. {
  1210. $testcase->product = $productID;
  1211. $testcase->project = $projectID;
  1212. $testcase->execution = $executionID;
  1213. $testcase->openedBy = $account;
  1214. $testcase->openedDate = $now;
  1215. $testcase->status = $forceNotReview || $testcase->review == 0 ? 'normal' : 'wait';
  1216. $testcase->version = 1;
  1217. $testcase->storyVersion = isset($storyVersions[$testcase->story]) ? $storyVersions[$testcase->story] : 0;
  1218. $testcase->stepType = array();
  1219. if($testcase->story && !isset($storyVersions[$testcase->story]))
  1220. {
  1221. if(count($storyVersions) == 0) $this->loadModel('story');
  1222. $testcase->storyVersion = $this->story->getVersion((int)$testcase->story);
  1223. $storyVersions[$testcase->story] = $testcase->storyVersion;
  1224. }
  1225. unset($testcase->review);
  1226. list($testcase->steps, $testcase->stepType) = $this->testcase->processStepsOrExpects($testcase->steps);
  1227. list($testcase->expects) = $this->testcase->processStepsOrExpects($testcase->expects);
  1228. }
  1229. return $testcases;
  1230. }
  1231. /**
  1232. * 构建批量编辑用例的数据。
  1233. * Build cases for batch editing.
  1234. *
  1235. * @param array $oldCases
  1236. * @access protected
  1237. * @return array
  1238. * @param mixed[] $oldSteps
  1239. */
  1240. protected function buildCasesForBathcEdit($oldCases, $oldSteps)
  1241. {
  1242. $caseIdList = array_keys($oldCases);
  1243. if(empty($caseIdList)) return array();
  1244. $now = helper::now();
  1245. $account = $this->app->user->account;
  1246. $cases = form::batchData($this->config->testcase->form->batchEdit)->get();
  1247. $forceNotReview = $this->testcase->forceNotReview();
  1248. foreach($cases as $caseID => $case)
  1249. {
  1250. $oldCase = $oldCases[$caseID];
  1251. $case->id = $caseID;
  1252. $case->product = $oldCase->product;
  1253. $case->lastEditedBy = $account;
  1254. $case->lastEditedDate = $now;
  1255. if(!isset($case->precondition)) $case->precondition = $oldCase->precondition;
  1256. $versionChanged = false;
  1257. if($case->title && $case->title != $oldCase->title) $versionChanged = true;
  1258. if($case->precondition && $case->precondition != $oldCase->precondition) $versionChanged = true;
  1259. list($case->steps, $case->stepType) = $this->testcase->processStepsOrExpects($case->steps);
  1260. list($case->expects) = $this->testcase->processStepsOrExpects($case->expects);
  1261. $oldStep = zget($oldSteps, $caseID, array());
  1262. $stepChanged = $this->testcase->processStepsChanged($case, $oldStep);
  1263. if($stepChanged && !$forceNotReview) $case->status = 'wait';
  1264. if($stepChanged) $versionChanged = true;
  1265. $case->version = $versionChanged ? (int)$oldCase->version + 1 : (int)$oldCase->version;
  1266. $case->stepChanged = $stepChanged;
  1267. }
  1268. return $cases;
  1269. }
  1270. /**
  1271. * 构建导入用例的数据。
  1272. * Build cases for showing imported.
  1273. *
  1274. * @param int $productID
  1275. * @access protected
  1276. * @return array
  1277. */
  1278. protected function buildCasesForShowImport($productID)
  1279. {
  1280. /* 初始化变量。 */
  1281. /* Initialize variables. */
  1282. $now = helper::now();
  1283. $account = $this->app->user->account;
  1284. $cases = form::batchData($this->config->testcase->form->showImport)->get();
  1285. $forceNotReview = $this->testcase->forceNotReview();
  1286. $insert = $this->post->insert;
  1287. $caseIdList = !$insert ? array_keys($this->post->title) : array();
  1288. $oldCases = $this->testcase->getByList($caseIdList);
  1289. $oldSteps = $this->testcase->fetchStepsByList($caseIdList);
  1290. $storyVersionPairs = $this->loadModel('story')->getVersions($this->post->story);
  1291. foreach($cases as $case)
  1292. {
  1293. list($case->steps, $case->stepType) = $this->testcase->processStepsOrExpects($case->steps);
  1294. list($case->expects) = $this->testcase->processStepsOrExpects($case->expects);
  1295. /* 构建更新的用例. */
  1296. /* Build updated case. */
  1297. if(!empty($case->rawID) && !$insert)
  1298. {
  1299. $oldCase = zget($oldCases, $case->rawID, new stdclass());
  1300. $oldStep = zget($oldSteps, $case->rawID, array());
  1301. $stepChanged = $this->testcase->processStepsChanged($case, $oldStep);
  1302. $case->id = $case->rawID;
  1303. $case->product = $productID;
  1304. $case->lastEditedBy = $account;
  1305. $case->lastEditedDate = $now;
  1306. $case->version = $stepChanged ? $oldCase->version + 1 : $oldCase->version;
  1307. if($case->story != $oldCase->story) $case->storyVersion = zget($storyVersionPairs, $case->story, 1);
  1308. $changes = common::createChanges($oldCase, $case);
  1309. if(!$changes && !$stepChanged) unset($cases[$case->rawID]);
  1310. }
  1311. /* 构建插入的用例. */
  1312. /* Build inserted case. */
  1313. else
  1314. {
  1315. $case->product = $productID;
  1316. $case->version = 1;
  1317. $case->openedBy = $this->app->user->account;
  1318. $case->openedDate = $now;
  1319. $case->status = !$forceNotReview ? 'wait' : 'normal';
  1320. if($this->app->tab == 'project') $case->project = $this->session->project;
  1321. if($case->story) $case->storyVersion = zget($storyVersionPairs, $case->story, 1);
  1322. }
  1323. $case->frequency = 1;
  1324. unset($case->rawID);
  1325. }
  1326. return $cases;
  1327. }
  1328. /**
  1329. * 根据 xmind 构建导入用例的数据。
  1330. * Build imported cases by xmind data.
  1331. *
  1332. * @param int $productID
  1333. * @param string $branch
  1334. * @param array $caseList
  1335. * @param bool $isInsert
  1336. * @access protected
  1337. * @return array
  1338. */
  1339. protected function buildCasesByXmind($productID, $branch, $caseList, $isInsert)
  1340. {
  1341. $caseIdList = array_filter(array_map(function($case){return zget($case, 'id', 0);}, $caseList));
  1342. $now = helper::now();
  1343. $forceNotReview = $this->testcase->forceNotReview();
  1344. $account = $this->app->user->account;
  1345. $oldCases = $this->testcase->getByList($caseIdList);
  1346. $oldSteps = $this->testcase->fetchStepsByList($caseIdList);
  1347. $branch = (int)$branch;
  1348. $modules = $this->loadModel('tree')->getOptionMenu($productID, 'case');
  1349. $cases = array();
  1350. foreach($caseList as $caseData)
  1351. {
  1352. $moduleID = (int)$caseData['module'];
  1353. $case = new stdclass();
  1354. $case->module = isset($modules[$moduleID]) ? $moduleID : 0;
  1355. $case->product = $productID;
  1356. $case->branch = $branch;
  1357. $case->title = $caseData['name'];
  1358. $case->pri = $caseData['pri'];
  1359. $case->precondition = $caseData['precondition'];
  1360. $case->tmpPId = $caseData['tmpPId'];
  1361. $case->version = 1;
  1362. $case = $this->testcase->processCaseSteps($case, (object)$caseData);
  1363. $caseID = (int)zget($caseData, 'id', 0);
  1364. $oldCase = zget($oldCases, $caseID, null);
  1365. if(empty($oldCase) || $isInsert)
  1366. {
  1367. $case->type = 'feature';
  1368. $case->status = !$forceNotReview ? 'wait' : 'normal';
  1369. $case->openedBy = $account;
  1370. $case->openedDate = $now;
  1371. }
  1372. else
  1373. {
  1374. $oldStep = zget($oldSteps, $caseID, array());
  1375. $stepChanged = $this->testcase->processStepsChanged($case, $oldStep);
  1376. $case->version = $stepChanged ? (int)$oldCase->version + 1 : (int)$oldCase->version;
  1377. $case->stepChanged = $stepChanged;
  1378. if($stepChanged && !$forceNotReview) $case->status = 'wait';
  1379. $case->id = $caseID;
  1380. $case->lastEditedBy = $account;
  1381. $case->lastEditedDate = $now;
  1382. }
  1383. $cases[] = $case;
  1384. }
  1385. return $cases;
  1386. }
  1387. /**
  1388. * 构建从用例库导入的数据。
  1389. * Build data for importing from lib.
  1390. *
  1391. * @param int $productID
  1392. * @param string $branch
  1393. * @param int $libID
  1394. * @access protected
  1395. * @return array
  1396. */
  1397. protected function buildDataForImportFromLib($productID, $branch, $libID)
  1398. {
  1399. /* 处理模块。 */
  1400. /* Process modules. */
  1401. $preModule = 0;
  1402. $modules = $this->post->module ? $this->post->module : array();
  1403. foreach($modules as $caseID => $module)
  1404. {
  1405. if($module != 'ditto') $preModule = $module;
  1406. if($module == 'ditto') $modules[$caseID] = $preModule;
  1407. }
  1408. /* 处理分支。 */
  1409. /* Process branches. */
  1410. $preBranch = 0;
  1411. $caseModules = array();
  1412. $branches = $this->post->branch;
  1413. $caseIdList = $this->post->caseIdList;
  1414. if(!empty($branches))
  1415. {
  1416. foreach($branches as $caseID => $branch)
  1417. {
  1418. if($branch != 'ditto') $prevBranch = $branch;
  1419. if($branch == 'ditto') $branches[$caseID] = $prevBranch;
  1420. if(!isset($caseModules[$branch]) && in_array($caseID, $caseIdList) !== false) $caseModules[$branch] = $this->testcase->getCanImportedModules($productID, $libID, $branch, 'pairs', $caseIdList);
  1421. }
  1422. }
  1423. else
  1424. {
  1425. $caseModules[$branch] = $this->testcase->getCanImportedModules($productID, $libID, $branch, 'pairs', $caseIdList);
  1426. }
  1427. /* 构建用例。 */
  1428. /* Build cases. */
  1429. $hasImported = '';
  1430. $cases = array();
  1431. $steps = array();
  1432. $files = array();
  1433. $fromCases = $this->testcase->getByList($caseIdList);
  1434. $fromSteps = $this->testcase->getRelatedSteps($caseIdList);
  1435. $fromFiles = $this->testcase->getRelatedFiles($caseIdList);
  1436. foreach($caseIdList as $caseID)
  1437. {
  1438. if(!isset($fromCases[$caseID])) continue;
  1439. $case = clone $fromCases[$caseID];
  1440. $case->lib = 0;
  1441. $case->steps = array();
  1442. $case->expects = array();
  1443. $case->stepType = array();
  1444. $case->product = $productID;
  1445. $case->fromCaseID = $case->id;
  1446. $case->fromCaseVersion = $case->version;
  1447. if(isset($modules[$case->id])) $case->module = $modules[$case->id];
  1448. if(isset($branches[$case->id])) $case->branch = $branches[$case->id];
  1449. unset($case->id);
  1450. if(!$case->scriptedDate) unset($case->scriptedDate);
  1451. if(!$case->reviewedDate) unset($case->reviewedDate);
  1452. if(!$case->lastEditedDate) unset($case->lastEditedDate);
  1453. if(!$case->lastRunDate) unset($case->lastRunDate);
  1454. $caseBranch = zget($case, 'branch', $branch);
  1455. if(empty($caseModules[$caseBranch][$case->fromCaseID][$case->module]))
  1456. {
  1457. $hasImported .= "$case->fromCaseID,";
  1458. }
  1459. else
  1460. {
  1461. $cases[$caseID] = $case;
  1462. $steps[$caseID] = zget($fromSteps, $caseID, array());
  1463. $files[$caseID] = zget($fromFiles, $caseID, array());
  1464. foreach($files[$caseID] as $file) $file->oldpathname = $file->pathname;
  1465. }
  1466. }
  1467. return array($cases, $steps, $files, $hasImported);
  1468. }
  1469. /**
  1470. * 构建导入到用例库的数据。
  1471. * Build data for import to lib.
  1472. *
  1473. * @param int $caseID
  1474. * @param int $libID
  1475. * @access protected
  1476. * @return array
  1477. */
  1478. protected function buildDataForImportToLib($caseID, $libID)
  1479. {
  1480. $cases = array();
  1481. $steps = array();
  1482. $files = array();
  1483. $caseIdList = !empty($caseID) ? $caseID : $this->post->caseIdList;
  1484. $caseIdList = str_replace('case_' , '', (string)$caseIdList);
  1485. $caseIdList = explode(',' , $caseIdList);
  1486. $fromCases = $this->testcase->getByList($caseIdList);
  1487. $fromSteps = $this->testcase->fetchStepsByList($caseIdList);
  1488. $fromFiles = $this->testcase->getRelatedFiles($caseIdList);
  1489. $libCases = $this->dao->select('*')->from(TABLE_CASE)->where('lib')->eq($libID)->andWhere('product')->eq(0)->andWhere('deleted')->eq('0')->fetchGroup('fromCaseID', 'id');
  1490. $maxOrder = $this->dao->select('max(`order`) as maxOrder')->from(TABLE_CASE)->where('deleted')->eq(0)->fetch('maxOrder');
  1491. $maxModuleOrder = $this->dao->select('max(`order`) as maxOrder')->from(TABLE_MODULE)->where('deleted')->eq(0)->fetch('maxOrder');
  1492. foreach($fromCases as $caseID => $case)
  1493. {
  1494. /* 初始化用例。 */
  1495. /* Init case. */
  1496. $libCase = $this->initLibCase($case, $libID, ++ $maxOrder, $maxModuleOrder, $libCases);
  1497. /* 设置用例步骤。 */
  1498. /* Set case steps. */
  1499. $steps[$caseID] = array();
  1500. if(!isset($fromSteps[$caseID])) $fromSteps[$caseID] = array();
  1501. foreach($fromSteps[$caseID] as $step)
  1502. {
  1503. $stepID = $step->id;
  1504. $step->version = zget($libCase, 'version', 1);
  1505. unset($step->id);
  1506. $steps[$caseID][$stepID] = $step;
  1507. }
  1508. /* 设置用例文件。 */
  1509. /* Set case files. */
  1510. $files[$caseID] = array();
  1511. if(!isset($fromFiles[$caseID])) $fromFiles[$caseID] = array();
  1512. foreach($fromFiles[$caseID] as $file)
  1513. {
  1514. $file->oldpathname = $file->pathname;
  1515. $files[$caseID][$file->id] = $file;
  1516. }
  1517. $cases[$caseID] = $libCase;
  1518. }
  1519. return array($cases, $steps, $files);
  1520. }
  1521. /**
  1522. * 为展示导入用例构建更新的用例。
  1523. * Build update case for showing import.
  1524. *
  1525. * @param object $case
  1526. * @param object $oldCase
  1527. * @param array $oldStep
  1528. * @param bool $forceNotReview
  1529. * @access protected
  1530. * @return bool
  1531. */
  1532. protected function buildUpdateCaseForShowImport($case, $oldCase, $oldStep, $forceNotReview)
  1533. {
  1534. $stepChanged = (count($oldStep) != count($case->desc));
  1535. if(!$stepChanged)
  1536. {
  1537. $desc = array_values($case->desc);
  1538. $expect = array_values($case->expect);
  1539. $stepType = array_values($case->stepType);
  1540. foreach($oldStep as $index => $step)
  1541. {
  1542. if(!isset($desc[$index]) || !isset($expect[$index]) || $step->desc != $desc[$index] || $step->expect != $expect[$index] || $step->type != $stepType[$index])
  1543. {
  1544. $stepChanged = true;
  1545. break;
  1546. }
  1547. }
  1548. }
  1549. $case->version = $stepChanged ? (int)$oldCase->version + 1 : (int)$oldCase->version;
  1550. $case->stepChanged = $stepChanged;
  1551. if($stepChanged && !$forceNotReview) $case->status = 'wait';
  1552. return $stepChanged;
  1553. }
  1554. /**
  1555. * 创建测试用例前检验表单数据是否正确。
  1556. * check from data for create case.
  1557. *
  1558. * @param object $case
  1559. * @access protected
  1560. * @return bool
  1561. */
  1562. protected function checkCreateFormData($case)
  1563. {
  1564. $steps = $case->steps;
  1565. $expects = $case->expects;
  1566. foreach($expects as $key => $value)
  1567. {
  1568. if(!empty($value) && empty($steps[$key])) dao::$errors[] = sprintf($this->lang->testcase->stepsEmpty, $key);
  1569. }
  1570. foreach(explode(',', $this->config->testcase->create->requiredFields) as $field)
  1571. {
  1572. $field = trim($field);
  1573. if($field && empty($case->{$field}))
  1574. {
  1575. $fieldName = $this->config->testcase->form->create[$field]['type'] != 'array' ? "{$field}" : "{$field}[]";
  1576. dao::$errors[$fieldName] = sprintf($this->lang->error->notempty, $this->lang->testcase->{$field});
  1577. }
  1578. }
  1579. if(dao::isError()) return false;
  1580. return true;
  1581. }
  1582. /**
  1583. * 批量创建测试用例前检验数据是否正确。
  1584. * Check testcases for batch creating.
  1585. *
  1586. * @param array $testcases
  1587. * @access protected
  1588. * @return array
  1589. */
  1590. protected function checkTestcasesForBatchCreate($testcases)
  1591. {
  1592. $this->loadModel('common');
  1593. $requiredErrors = array();
  1594. foreach($testcases as $i => $testcase)
  1595. {
  1596. /* 检验必填项。 */
  1597. /* Check reuqired. */
  1598. foreach(explode(',', $this->config->testcase->create->requiredFields) as $field)
  1599. {
  1600. $field = trim($field);
  1601. if($field && empty($testcases[$i]->{$field}))
  1602. {
  1603. $fieldName = $this->config->testcase->form->batchCreate[$field]['type'] != 'array' ? "{$field}[{$i}]" : "{$field}[{$i}][]";
  1604. $requiredErrors[$fieldName] = sprintf($this->lang->error->notempty, $this->lang->testcase->{$field});
  1605. }
  1606. }
  1607. }
  1608. if(!empty($requiredErrors)) dao::$errors = $requiredErrors;
  1609. return $testcases;
  1610. }
  1611. /**
  1612. * 批量创建测试用例前检验数据是否正确。
  1613. * Check testcases for batch editing.
  1614. *
  1615. * @param array $cases
  1616. * @access protected
  1617. * @return array
  1618. */
  1619. protected function checkCasesForBatchEdit($cases)
  1620. {
  1621. $this->loadModel('common');
  1622. $requiredErrors = array();
  1623. foreach($cases as $i => $testcase)
  1624. {
  1625. /* 检验必填项。 */
  1626. /* Check reuqired. */
  1627. foreach(explode(',', $this->config->testcase->edit->requiredFields) as $field)
  1628. {
  1629. $field = trim($field);
  1630. if($field && empty($cases[$i]->{$field}))
  1631. {
  1632. $fieldName = $this->config->testcase->form->batchEdit[$field]['type'] != 'array' ? "{$field}[{$i}]" : "{$field}[{$i}][]";
  1633. $requiredErrors[$fieldName] = sprintf($this->lang->error->notempty, $this->lang->testcase->{$field});
  1634. }
  1635. }
  1636. }
  1637. if(!empty($requiredErrors)) dao::$errors = $requiredErrors;
  1638. return $cases;
  1639. }
  1640. /**
  1641. * 导入测试用例前检验数据是否正确。
  1642. * Check testcases for importing.
  1643. *
  1644. * @param array $cases
  1645. * @access protected
  1646. * @return array
  1647. */
  1648. protected function checkCasesForShowImport($cases)
  1649. {
  1650. if($this->config->edition != 'open')
  1651. {
  1652. $extendFields = $this->testcase->getFlowExtendFields();
  1653. $notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty');
  1654. foreach($extendFields as $extendField)
  1655. {
  1656. if(strpos(",$extendField->rules,", ",$notEmptyRule->id,") !== false)
  1657. {
  1658. $this->config->testcase->create->requiredFields .= ',' . $extendField->field;
  1659. }
  1660. }
  1661. }
  1662. foreach($cases as $i => $case)
  1663. {
  1664. /* 检查期望对应的步骤是否填写。 */
  1665. /* Check the step which has expect. */
  1666. foreach($case->expects as $exportID => $value)
  1667. {
  1668. if(!empty($value) && empty($case->steps[$exportID]))
  1669. {
  1670. $caseErrors["steps[{$i}]"][] = sprintf($this->lang->testcase->stepsEmpty, $exportID);
  1671. }
  1672. }
  1673. /* 检验必填项。 */
  1674. /* Check reuqired. */
  1675. foreach(explode(',', $this->config->testcase->edit->requiredFields) as $field)
  1676. {
  1677. $field = trim($field);
  1678. if($field && empty($case->{$field}))
  1679. {
  1680. $fieldName = $this->config->testcase->form->showImport[$field]['type'] != 'array' ? "{$field}[{$i}]" : "{$field}[{$i}][]";
  1681. $caseErrors[$fieldName][] = sprintf($this->lang->error->notempty, $this->lang->testcase->{$field});
  1682. }
  1683. }
  1684. }
  1685. if(!empty($caseErrors)) dao::$errors = $caseErrors;
  1686. return $cases;
  1687. }
  1688. /**
  1689. * 检查 mind 配置。
  1690. * Build mind config.
  1691. *
  1692. * @access protected
  1693. * @return array|bool
  1694. * @param string $type
  1695. */
  1696. protected function buildMindConfig($type)
  1697. {
  1698. $configList = array();
  1699. $module = $this->post->module;
  1700. if(!empty($module)) $configList[] = array('key' => 'module', 'value' => $module);
  1701. $scene = $this->post->scene;
  1702. if(!empty($scene)) $configList[] = array('key' => 'scene', 'value' => $scene);
  1703. $case = $this->post->case;
  1704. if(!empty($case)) $configList[] = array('key' => 'case', 'value' => $case);
  1705. $precondition = $this->post->precondition;
  1706. if(!empty($precondition)) $configList[] = array('key' => 'precondition', 'value' => $precondition);
  1707. $pri = $this->post->pri;
  1708. if(!empty($pri)) $configList[] = array('key' => 'pri', 'value' => $pri);
  1709. $group = $this->post->group;
  1710. if(!empty($group)) $configList[] = array('key' => 'group', 'value' => $group);
  1711. $configErrors = array();
  1712. foreach($configList as $config)
  1713. {
  1714. $key = $config['key'];
  1715. $value = $config['value'];
  1716. if(!preg_match("/^[a-zA-Z]{1,10}$/", $value)) $configErrors[$key][] = sprintf($this->lang->testcase->errorMindConfig, $this->lang->testcase->{$key});
  1717. }
  1718. if(!empty($configErrors)) dao::$errors = $configErrors;
  1719. $map = array();
  1720. $map[strtolower($module)] = true;
  1721. $map[strtolower($scene)] = true;
  1722. $map[strtolower($case)] = true;
  1723. $map[strtolower($precondition)] = true;
  1724. $map[strtolower($pri)] = true;
  1725. $map[strtolower($group)] = true;
  1726. if(count($map) < 5 && count($map) > 0) dao::$errors['message'][] = '特征字符串不能重复';
  1727. return !dao::isError() ? $configList : false;
  1728. }
  1729. /**
  1730. * 初始化用例数据。
  1731. * Initialize the testcase.
  1732. *
  1733. * @param int $storyID
  1734. * @param int $testcaseID
  1735. * @param int $bugID
  1736. * @access protected
  1737. * @return object
  1738. */
  1739. protected function initTestcase($storyID, $testcaseID, $bugID)
  1740. {
  1741. /* 初始化用例。 */
  1742. /* Initialize the testcase. */
  1743. $case = new stdclass();
  1744. $case->type = 'feature';
  1745. $case->pri = 3;
  1746. $case->scene = 0;
  1747. $case->story = $storyID;
  1748. $case->stage = $case->title = $case->precondition = $case->keywords = $case->color = $case->auto = $case->script = '';
  1749. $case->steps = array();
  1750. /* 如果用例 id 大于 0,使用这个用例数据作为模板。 */
  1751. /* If testcaseID large than 0, use this testcase as template. */
  1752. if($testcaseID > 0)
  1753. {
  1754. $testcase = $this->testcase->getById($testcaseID);
  1755. $case->product = $testcase->product;
  1756. $case->type = $testcase->type ? $testcase->type : 'feature';
  1757. $case->stage = $testcase->stage;
  1758. $case->pri = $testcase->pri;
  1759. $case->scene = $testcase->scene;
  1760. $case->story = $testcase->story;
  1761. $case->title = $testcase->title;
  1762. $case->precondition = $testcase->precondition;
  1763. $case->keywords = $testcase->keywords;
  1764. $case->steps = $testcase->steps;
  1765. $case->color = $testcase->color;
  1766. $case->auto = $testcase->auto;
  1767. $case->script = $testcase->script;
  1768. $case->files = $testcase->files;
  1769. foreach($case->files as $file)
  1770. {
  1771. $file->name = $file->title;
  1772. $file->url = $this->createLink('file', 'download', "fileID={$file->id}");
  1773. }
  1774. if($this->config->edition != 'open')
  1775. {
  1776. $extendFields = $this->loadModel('flow')->getExtendFields('testcase', 'create');
  1777. foreach($extendFields as $field) $case->{$field->field} = $testcase->{$field->field};
  1778. }
  1779. }
  1780. /* 如果 bug id 大于 0,使用这个 bug 数据作为模板。 */
  1781. /* If bugID large than 0, use this bug as template. */
  1782. if($bugID > 0)
  1783. {
  1784. $bug = $this->loadModel('bug')->getById($bugID);
  1785. $case->type = 'feature';
  1786. $case->pri = $bug->pri ? $bug->pri : $bug->severity;
  1787. $case->story = $bug->story;
  1788. $case->title = $bug->title;
  1789. $case->keywords = $bug->keywords;
  1790. $case->steps = $this->testcase->createStepsFromBug($bug->steps);
  1791. }
  1792. /* 追加步骤到默认的步骤数。 */
  1793. /* Append the steps to the default steps count. */
  1794. $case->steps = $this->testcase->appendSteps(!empty($case->steps) ? $case->steps : array());
  1795. return $case;
  1796. }
  1797. /**
  1798. * 初始化要导入到用例库的用例
  1799. * Init case to import to lib.
  1800. *
  1801. * @param object $case
  1802. * @param int $libID
  1803. * @param int $maxOrder
  1804. * @param int $maxModuleOrder
  1805. * @param array $libCases
  1806. * @access protected
  1807. * @return object
  1808. */
  1809. protected function initLibCase($case, $libID, $maxOrder, $maxModuleOrder, $libCases)
  1810. {
  1811. $libCase = new stdclass();
  1812. $libCase->lib = $libID;
  1813. $libCase->title = $case->title;
  1814. $libCase->precondition = $case->precondition;
  1815. $libCase->keywords = $case->keywords;
  1816. $libCase->pri = $case->pri;
  1817. $libCase->type = $case->type;
  1818. $libCase->stage = $case->stage;
  1819. $libCase->status = $case->status;
  1820. $libCase->fromCaseID = $case->id;
  1821. $libCase->fromCaseVersion = $case->version;
  1822. $libCase->color = $case->color;
  1823. $libCase->order = $maxOrder;
  1824. $libCase->module = empty($case->module) ? 0 : $this->testcase->importCaseRelatedModules($libID, $case->module, $maxModuleOrder);
  1825. if(!isset($libCases[$case->id]))
  1826. {
  1827. $libCase->openedBy = $this->app->user->account;
  1828. $libCase->openedDate = helper::now();
  1829. }
  1830. else
  1831. {
  1832. $libCaseList = array_keys($libCases[$case->id]);
  1833. $libCaseID = $libCaseList[0];
  1834. $libCase->id = $libCaseID;
  1835. $libCase->lastEditedBy = $this->app->user->account;
  1836. $libCase->lastEditedDate = helper::now();
  1837. $libCase->version = (int)$libCases[$case->id][$libCaseID]->version + 1;
  1838. }
  1839. return $libCase;
  1840. }
  1841. /**
  1842. * 导入用例。
  1843. * Import cases.
  1844. *
  1845. * @param array $cases
  1846. * @access protected
  1847. * @return array
  1848. */
  1849. protected function importCases($cases)
  1850. {
  1851. $this->loadModel('action');
  1852. $daoErrors = array();
  1853. foreach($cases as $case)
  1854. {
  1855. if(isset($case->id))
  1856. {
  1857. $oldCase = $this->testcase->getByID($case->id);
  1858. if($oldCase->product != $case->product) continue;
  1859. $changes = $this->testcase->update($case, $oldCase);
  1860. $actionID = $this->action->create('case', $case->id, 'Edited');
  1861. $this->action->logHistory($actionID, $changes);
  1862. $this->testcase->updateCase2Project($oldCase, $case);
  1863. }
  1864. else
  1865. {
  1866. $caseID = $this->testcase->create($case);
  1867. $case->id = $caseID;
  1868. dao::isError() ? $daoErrors = array_merge($daoErrors, dao::getError()) : $this->testcase->syncCase2Project($case, $caseID);
  1869. }
  1870. }
  1871. if(!empty($daoErrors)) dao::$errors = $daoErrors;
  1872. return $cases;
  1873. }
  1874. /**
  1875. * 处理xmind数据
  1876. * Process scene data.
  1877. *
  1878. * @param array $result
  1879. * @access protected
  1880. * @return array
  1881. */
  1882. protected function processScene($result)
  1883. {
  1884. $scenes['id'] = $result['id'];
  1885. $scenes['text'] = $result['title'];
  1886. $scenes['type'] = 'root';
  1887. if(!empty($result['children'] && !empty($result['children']['attached']))) $scenes['children'] = $this->processChildScene($result['children']['attached'], $result['id'], 'sub');
  1888. if(!empty($result['children'] && !empty($result['children']['topics']['topic']))) $scenes['children'] = $this->processChildScene($result['children']['topics']['topic'], $result['id'], 'sub');
  1889. return $scenes;
  1890. }
  1891. /**
  1892. * 处理xmind的节点数据
  1893. * process scene child data.
  1894. *
  1895. * @param array $results
  1896. * @param string $parent
  1897. * @param string $type
  1898. * @access protected
  1899. * @return void
  1900. */
  1901. protected function processChildScene($results, $parent, $type)
  1902. {
  1903. $scenes = array();
  1904. foreach($results as $result)
  1905. {
  1906. if(!isset($result['id'])) continue;
  1907. $scene['id'] = $result['id'];
  1908. $scene['text'] = $result['title'];
  1909. $scene['parent'] = $parent;
  1910. $scene['type'] = $type;
  1911. if(!empty($result['children']) && !empty($result['children']['attached']))
  1912. {
  1913. $scene['children'] = $this->processChildScene($result['children']['attached'], $result['id'], 'node');
  1914. }
  1915. elseif(!empty($result['children']) && !empty($result['children']['topics']))
  1916. {
  1917. if(!isset($result['children']['topics']['type']) || empty($result['children']['topics']['topic'])) continue;
  1918. $topic = $result['children']['topics']['topic'];
  1919. if(isset($topic['id']))
  1920. {
  1921. $sceneChild = array();
  1922. $sceneChild['id'] = $topic['id'];
  1923. $sceneChild['text'] = $topic['title'];
  1924. $sceneChild['parent'] = $result['id'];
  1925. $sceneChild['type'] = 'node';
  1926. $grandChild = array();
  1927. if(!empty($topic['children']) && !empty($topic['children']['topics']['topic'])) $grandChild = $topic['children']['topics']['topic'];
  1928. if(isset($topic['nodeType'])) $grandChild = $topic;
  1929. if(isset($grandChild['id'])) $grandChild = array($grandChild);
  1930. $sceneChild['children'] = $this->processChildScene($grandChild, $topic['id'], 'node');
  1931. $scene['children'][] = $sceneChild;
  1932. }
  1933. else
  1934. {
  1935. $scene['children'] = $this->processChildScene($topic, $result['id'], 'node');
  1936. }
  1937. }
  1938. $scenes[] = $scene;
  1939. }
  1940. return $scenes;
  1941. }
  1942. /**
  1943. * 为展示脑图计算步骤数据。
  1944. * Process steps for mindmap.
  1945. *
  1946. * @param object $case
  1947. * @access protected
  1948. * @return object
  1949. */
  1950. protected function processStepsForMindMap($case)
  1951. {
  1952. $mindMapSteps = array();
  1953. $mindMapSteps['id'] = 'case_' . $case->id;
  1954. $mindMapSteps['text'] = $case->title;
  1955. $mindMapSteps['type'] = 'root';
  1956. $stepItem['subSide'] = 'right';
  1957. $reverseSteps = array_reverse($case->steps);
  1958. $stepList = array();
  1959. $parentSteps = array();
  1960. foreach($reverseSteps as $step)
  1961. {
  1962. if(empty($step->id)) continue;
  1963. if($step->type != 'group')
  1964. {
  1965. $descItem = array();
  1966. $descItem['id'] = 'desc_' . $step->id;
  1967. $descItem['text'] = $step->expect;
  1968. $descItem['type'] = 'node';
  1969. $descItem['parent'] = $step->id;
  1970. $descItem['subSide'] = 'right';
  1971. $parentSteps[$step->id][] = $descItem;
  1972. }
  1973. $stepItem = array();
  1974. $stepItem['id'] = $step->id;
  1975. $stepItem['text'] = $step->step;
  1976. $stepItem['type'] = $step->grade == 1 ? 'sub' : 'node';
  1977. $stepItem['parent'] = $step->parent > 0 ? $step->parent : 'case_' . $case->id;
  1978. $stepItem['subSide'] = 'right';
  1979. if(isset($parentSteps[$step->id])) $stepItem['children'] = array_reverse($parentSteps[$step->id]);
  1980. if($step->parent > 0)
  1981. {
  1982. $parentSteps[$step->parent][] = $stepItem;
  1983. }
  1984. else
  1985. {
  1986. $stepList[] = $stepItem;
  1987. }
  1988. }
  1989. $mindMapSteps['children'] = array_reverse($stepList);
  1990. $case->mindMapSteps = $mindMapSteps;
  1991. return $case;
  1992. }
  1993. /**
  1994. * 计算导入列的键值。
  1995. * Process import column key.
  1996. *
  1997. * @param string $fileName
  1998. * @param array $fields
  1999. * @access protected
  2000. * @return void
  2001. */
  2002. protected function processImportColumnKey($fileName, $fields)
  2003. {
  2004. /* 获取文件内的行,设置头。 */
  2005. /* Get rows and set header. */
  2006. $rows = $this->loadModel('file')->parseCSV($fileName);
  2007. $header = array();
  2008. foreach($rows[0] as $i => $rowValue)
  2009. {
  2010. if(empty($rowValue)) break;
  2011. $header[$i] = $rowValue;
  2012. }
  2013. unset($rows[0]);
  2014. /* 设置列的键值。 */
  2015. /* Set column key. */
  2016. $columnKey = array();
  2017. foreach($header as $title)
  2018. {
  2019. if(isset($fields[$title])) $columnKey[] = $fields[$title];
  2020. }
  2021. return $columnKey;
  2022. }
  2023. /**
  2024. * 获取分组用例。
  2025. * Get group cases.
  2026. *
  2027. * @param int $productID
  2028. * @param string $branch
  2029. * @param string $groupBy
  2030. * @param string $caseType
  2031. * @param string $browseType
  2032. * @access protected
  2033. * @return array
  2034. */
  2035. protected function getGroupCases($productID, $branch, $groupBy, $caseType, $browseType = '')
  2036. {
  2037. /* 获取用例。 */
  2038. /* Get cases. */
  2039. $cases = $this->testcase->getModuleCases($productID, $branch, 0, $browseType, 'no', $caseType, $groupBy);
  2040. $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', false);
  2041. $cases = $this->loadModel('story')->checkNeedConfirm($cases);
  2042. $cases = $this->testcase->appendData($cases);
  2043. foreach($cases as $case) $case->caseID = $case->id;
  2044. /* 获取用例的需求分组。 */
  2045. /* Get story groups of cases. */
  2046. $groupCases = array();
  2047. if($groupBy == 'story')
  2048. {
  2049. foreach($cases as $case) $groupCases[$case->story][] = $case;
  2050. }
  2051. /* 设置用例的需求分组的行占比。 */
  2052. /* Set row span of story group in cases. */
  2053. $story = null;
  2054. foreach($cases as $index => $case)
  2055. {
  2056. if($case->storyDeleted)
  2057. {
  2058. unset($cases[$index]);
  2059. continue;
  2060. }
  2061. $case->rowspan = 0;
  2062. if($story !== $case->story)
  2063. {
  2064. $story = $case->story;
  2065. if(!empty($groupCases[$case->story])) $case->rowspan = count($groupCases[$case->story]);
  2066. }
  2067. }
  2068. return $cases;
  2069. }
  2070. /**
  2071. * 创建完 testcase 后的相关处理。
  2072. * Relevant processing after create testcase.
  2073. *
  2074. * @param object $case
  2075. * @param int $caseID
  2076. * @access protected
  2077. * @return void
  2078. */
  2079. protected function afterCreate($case, $caseID)
  2080. {
  2081. /* 设置 cookie。 */
  2082. /* Set cookie. */
  2083. helper::setcookie('lastCaseModule', (string)$case->module);
  2084. helper::setcookie('lastCaseProduct', (string)$case->product);
  2085. helper::setcookie('lastCaseScene', (string)$case->scene);
  2086. helper::setcookie('caseModule', '0');
  2087. if(!empty($_POST['fileList']))
  2088. {
  2089. $fileList = $this->post->fileList;
  2090. if($fileList) $fileList = json_decode($fileList, true);
  2091. $this->loadModel('file')->saveDefaultFiles($fileList, 'testcase', $caseID);
  2092. }
  2093. /* 如果需求关联到项目,把用例也关联到项目。 */
  2094. /* If the story is linked project, make the case link the project. */
  2095. $this->testcase->syncCase2Project($case, $caseID);
  2096. }
  2097. /**
  2098. * 返回创建 testcase 的结果。
  2099. * Respond after creating testcase.
  2100. *
  2101. * @param int $caseID
  2102. * @access protected
  2103. * @return array|int
  2104. * @param int $moduleID
  2105. */
  2106. protected function responseAfterCreate($caseID, $moduleID = 0)
  2107. {
  2108. $message = $this->executeHooks($caseID);
  2109. if(!$message) $message = $this->lang->saveSuccess;
  2110. if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $message, 'id' => $caseID));
  2111. /* If link from no head then reload. */
  2112. if(isInModal() || helper::isAjaxRequest('modal')) return $this->send(array('result' => 'success', 'message' => $message, 'load' => true, 'closeModal' => true));
  2113. /* 判断是否当前一级菜单不是 QA,并且 caseList session 存在,并且 caseList 不是动态页面。 */
  2114. /* Use this session link, when the tab is not QA, a session of the case list exists, and the session is not from the Dynamic page. */
  2115. $useSession = ($this->app->tab != 'qa' and $this->session->caseList and strpos($this->session->caseList, 'dynamic') === false);
  2116. if($this->app->tab == 'project')
  2117. {
  2118. $locateLink = $this->createLink('project', 'testcase', "projectID={$this->session->project}");
  2119. }
  2120. else
  2121. {
  2122. $params = "productID={$this->post->product}&branch={$this->post->branch}";
  2123. if($moduleID) $params .= "&browseType=byModule&param=$moduleID";
  2124. $locateLink = $this->createLink('testcase', 'browse', $params);
  2125. }
  2126. return $this->send(array('result' => 'success', 'message' => $message, 'load' => $useSession ? $this->session->caseList : $locateLink));
  2127. }
  2128. /**
  2129. * 返回批量创建 testcase 的结果。
  2130. * Respond after batch creating testcase.
  2131. *
  2132. * @param int $productID
  2133. * @param string|int $branch
  2134. * @access protected
  2135. * @return void
  2136. */
  2137. protected function responseAfterBatchCreate($productID, $branch)
  2138. {
  2139. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  2140. if(helper::isAjaxRequest('modal')) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => true));
  2141. if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'idList' => $caseIdList));
  2142. helper::setcookie('caseModule', '0');
  2143. $currentModule = $this->app->tab == 'qa' ? 'testcase' : $this->app->tab;
  2144. $currentMethod = $this->app->tab == 'qa' ? 'browse' : 'testcase';
  2145. $projectParam = $this->app->tab == 'qa' ? '' : "{$this->app->tab}ID=" . zget($_SESSION, $this->app->tab, 0) . '&';
  2146. return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'load' => $this->createLink($currentModule, $currentMethod, "{$projectParam}productID={$productID}&branch={$branch}")));
  2147. }
  2148. /**
  2149. * 返回展示导入 testcase 的结果。
  2150. * Respond after showing imported testcase.
  2151. *
  2152. * @param int $productID
  2153. * @param string $branch
  2154. * @param int $maxImport
  2155. * @param string $tmpFile
  2156. * @param string $message
  2157. * @access protected
  2158. * @return void
  2159. */
  2160. protected function responseAfterShowImport($productID, $branch = '0', $maxImport = 0, $tmpFile = '', $message = '')
  2161. {
  2162. if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
  2163. if($this->post->isEndPage)
  2164. {
  2165. if(!empty($_SESSION['fileImport']) && file_exists($_SESSION['fileImport'])) unlink($tmpFile);
  2166. if(!empty($_SESSION['fileImport'])) unset($_SESSION['fileImport']);
  2167. $locateLink = $this->app->tab == 'project' ? $this->createLink('project', 'testcase', "projectID={$this->session->project}&productID={$productID}") : inlink('browse', "productID={$productID}");
  2168. }
  2169. else
  2170. {
  2171. $locateLink = inlink('showImport', "productID={$productID}&branch={$branch}&pagerID=" . ((int)$this->post->pagerID + 1) . "&maxImport={$maxImport}&insert=" . zget($_POST, 'insert', ''));
  2172. }
  2173. return $this->send(array('result' => 'success', 'message' => $message ? $message : $this->lang->saveSuccess, 'load' => $locateLink));
  2174. }
  2175. /**
  2176. * 处理评审数据。
  2177. * Prepare review data.
  2178. *
  2179. * @param int $caseID
  2180. * @param object $oldCase
  2181. * @access protected
  2182. * @return bool|object
  2183. */
  2184. protected function prepareReviewData($caseID, $oldCase)
  2185. {
  2186. $now = helper::now();
  2187. $status = $this->getStatusForReview($oldCase);
  2188. $case = form::data($this->config->testcase->form->review, $caseID)->add('id', $caseID)
  2189. ->setForce('status', $status)
  2190. ->setDefault('reviewedDate', substr($now, 0, 10))
  2191. ->setDefault('lastEditedBy', $this->app->user->account)
  2192. ->setDefault('lastEditedDate', $now)
  2193. ->join('reviewedBy', ',')
  2194. ->stripTags($this->config->testcase->editor->review['id'], $this->config->allowedTags)
  2195. ->get();
  2196. if(!$case->result)
  2197. {
  2198. dao::$errors['result'] = $this->lang->testcase->mustChooseResult;
  2199. return false;
  2200. }
  2201. return $this->loadModel('file')->processImgURL($case, $this->config->testcase->editor->review['id'], $this->post->uid);
  2202. }
  2203. /**
  2204. * 构建关联用例页面的搜索表单。
  2205. * Build search form for link cases.
  2206. *
  2207. * @param object $case
  2208. * @param int $queryID
  2209. * @access protected
  2210. * @return void
  2211. */
  2212. protected function buildLinkCasesSearchForm($case, $queryID)
  2213. {
  2214. $actionURL = $this->createLink('testcase', 'linkCases', "caseID={$case->id}&browseType=bySearch&queryID=myQueryID", '', true);
  2215. $objectID = 0;
  2216. if($this->app->tab == 'project') $objectID = $case->project;
  2217. if($this->app->tab == 'execution') $objectID = $case->execution;
  2218. unset($this->config->testcase->search['fields']['product']);
  2219. $this->testcase->buildSearchForm($case->product, $this->products, $queryID, $actionURL, $objectID);
  2220. }
  2221. /**
  2222. * 构建关联 bug 页面的搜索表单。
  2223. * Build search form for link bugs.
  2224. *
  2225. * @param object $case
  2226. * @param int $queryID
  2227. * @access protected
  2228. * @return void
  2229. */
  2230. protected function buildLinkBugsSearchForm($case, $queryID)
  2231. {
  2232. $actionURL = $this->createLink('testcase', 'linkBugs', "caseID={$case->id}&browseType=bySearch&queryID=myQueryID", '', true);
  2233. $objectID = 0;
  2234. if($this->app->tab == 'project') $objectID = $case->project;
  2235. if($this->app->tab == 'execution') $objectID = $case->execution;
  2236. /* 删除单一项目的计划字段。*/
  2237. /* Unset search field 'plan' in single project. */
  2238. unset($this->config->bug->search['fields']['product']);
  2239. if($case->project && ($this->app->tab == 'project' || $this->app->tab == 'execution'))
  2240. {
  2241. $project = $this->loadModel('project')->getByID($case->project);
  2242. if(!$project->hasProduct && $project->model == 'waterfall') unset($this->config->bug->search['fields']['plan']);
  2243. }
  2244. $this->loadModel('bug')->buildSearchForm($case->product, $this->products, $queryID, $actionURL, (string)$objectID);
  2245. }
  2246. /**
  2247. * 获取导出的字段列表。
  2248. * Get the export fields.
  2249. *
  2250. * @param string $productType
  2251. * @access protected
  2252. * @return array
  2253. */
  2254. protected function getExportFields($productType)
  2255. {
  2256. $fields = $this->post->exportFields ? $this->post->exportFields : explode(',', $this->config->testcase->exportFields);
  2257. foreach($fields as $key => $fieldName)
  2258. {
  2259. $fieldName = trim($fieldName);
  2260. if($productType != 'normal' || $fieldName != 'branch') $fields[$fieldName] = zget($this->lang->testcase, $fieldName);
  2261. unset($fields[$key]);
  2262. }
  2263. return $fields;
  2264. }
  2265. /**
  2266. * 获取 mind 导出的数据。
  2267. * Get export data.
  2268. *
  2269. * @param int $productID
  2270. * @param int $moduleID
  2271. * @param string $branch
  2272. * @access protected
  2273. * @return array
  2274. * @param string $type
  2275. */
  2276. protected function getMindExport($type, $productID, $moduleID, $branch)
  2277. {
  2278. $caseList = $this->testcase->getCaseListForXmindExport($productID, $moduleID, $branch);
  2279. $stepList = $this->testcase->getStepByProductAndModule($productID, $moduleID);
  2280. $moduleList = $this->getModuleListForXmindExport($productID, $moduleID, $branch);
  2281. $sceneInfo = $this->testcase->getSceneByProductAndModule($productID, $moduleID);
  2282. $config = $this->testcase->getMindConfig($type);
  2283. return array('caseList' => $caseList, 'stepList' => $stepList, 'sceneMaps' => $sceneInfo['sceneMaps'], 'topScenes' => $sceneInfo['topScenes'], 'moduleList' => $moduleList, 'config' => $config);
  2284. }
  2285. /**
  2286. * 导出xmind格式用例时获取模块列表。
  2287. * Get module list for xmind export.
  2288. *
  2289. * @param int $productID
  2290. * @param int $moduleID
  2291. * @param string $branch
  2292. * @access public
  2293. * @return array
  2294. */
  2295. private function getModuleListForXmindExport($productID, $moduleID, $branch)
  2296. {
  2297. if($moduleID)
  2298. {
  2299. $module = $this->loadModel('tree')->getByID($moduleID);
  2300. if(!$module) return array();
  2301. return array($module->id => $module->name);
  2302. }
  2303. $moduleList = $this->loadModel('tree')->getOptionMenu($productID, $viewType = 'case', $startModuleID = 0, ($branch === 'all' || !isset($branches[$branch])) ? 'all' : $branch);
  2304. unset($moduleList['0']);
  2305. return $moduleList;
  2306. }
  2307. /**
  2308. * 获取导入的数据。
  2309. * Get imported data.
  2310. *
  2311. * @param int $productID
  2312. * @param string $file
  2313. * @access protected
  2314. * @return void
  2315. */
  2316. protected function getImportedData($productID, $file)
  2317. {
  2318. $rows = $this->loadModel('file')->parseCSV($file);
  2319. $header = array();
  2320. foreach($rows[0] as $i => $rowValue)
  2321. {
  2322. if(empty($rowValue)) break;
  2323. $header[$i] = $rowValue;
  2324. }
  2325. unset($rows[0]);
  2326. $fields = $this->testcase->getImportFields($productID);
  2327. $fields = array_flip($fields);
  2328. $caseData = array();
  2329. $stepVars = 0;
  2330. foreach($rows as $row => $data)
  2331. {
  2332. $case = new stdclass();
  2333. foreach($header as $key => $title)
  2334. {
  2335. if(!isset($fields[$title]) || !isset($data[$key])) continue;
  2336. $field = $fields[$title];
  2337. $cellValue = $data[$key];
  2338. $case = $this->getImportField($field, $cellValue, $case);
  2339. }
  2340. if(empty($case->title)) continue;
  2341. $caseData[$row] = $case;
  2342. unset($case);
  2343. }
  2344. return array(array('caseData' => $caseData), $stepVars);
  2345. }
  2346. /**
  2347. * 获取导入的用例字段。
  2348. * Get imported field.
  2349. *
  2350. * @param string $field
  2351. * @param string $cellValue
  2352. * @param object $case
  2353. * @access protected
  2354. * @return object
  2355. */
  2356. protected function getImportField($field, $cellValue, $case)
  2357. {
  2358. if($field == 'story' || $field == 'module' || $field == 'branch')
  2359. {
  2360. $case->{$field} = 0;
  2361. if(strrpos($cellValue, '(#') !== false)
  2362. {
  2363. $id = trim(substr($cellValue, strrpos($cellValue,'(#') + 2), ')');
  2364. $case->{$field} = $id;
  2365. }
  2366. }
  2367. elseif($field == 'stepDesc')
  2368. {
  2369. $case->steps = $cellValue;
  2370. }
  2371. elseif($field == 'stepExpect')
  2372. {
  2373. $case->expects = $cellValue;
  2374. }
  2375. elseif(in_array($field, $this->config->testcase->export->listFields))
  2376. {
  2377. if($field == 'stage')
  2378. {
  2379. $stages = explode("\n", $cellValue);
  2380. foreach($stages as $stage) $case->stage[] = array_search($stage, $this->lang->testcase->{$field . 'List'});
  2381. $case->stage = join(',', $case->stage);
  2382. }
  2383. else
  2384. {
  2385. $case->{$field} = array_search($cellValue, $this->lang->testcase->{$field . 'List'});
  2386. }
  2387. }
  2388. else
  2389. {
  2390. $case->{$field} = $cellValue;
  2391. }
  2392. return $case;
  2393. }
  2394. /**
  2395. * 获取导入的用例步骤。
  2396. * Get imported steps.
  2397. *
  2398. * @param string $field
  2399. * @param array $steps
  2400. * @param array $stepData
  2401. * @param int $row
  2402. * @access protected
  2403. * @return array
  2404. */
  2405. protected function getImportSteps($field, $steps, $stepData, $row)
  2406. {
  2407. $caseSteps = array();
  2408. foreach($steps as $step)
  2409. {
  2410. $step = trim($step);
  2411. if(empty($step)) continue;
  2412. preg_match('/^((([0-9]+)[.]([0-9]+))[.]([0-9]+))[.、](.*)$/Uu', $step, $out);
  2413. if(!$out) preg_match('/^(([0-9]+)[.]([0-9]+))[.、](.*)$/Uu', $step, $out);
  2414. if(!$out) preg_match('/^([0-9]+)[.、](.*)$/Uu', $step, $out);
  2415. if($out)
  2416. {
  2417. $count = count($out);
  2418. $num = $out[1];
  2419. $parent = $count > 4 ? $out[2] : '0';
  2420. $grand = $count > 6 ? $out[3] : '0';
  2421. $step = trim($out[2]);
  2422. if($count > 4) $step = $count > 6 ? trim($out[6]) : trim($out[4]);
  2423. $caseSteps[$num]['content'] = $step;
  2424. $caseSteps[$num]['number'] = $num;
  2425. $caseSteps[$num]['type'] = $count > 4 ? 'item' : 'step';
  2426. if(!empty($parent)) $caseSteps[$parent]['type'] = 'group';
  2427. if(!empty($grand)) $caseSteps[$grand]['type'] = 'group';
  2428. }
  2429. elseif(isset($num))
  2430. {
  2431. $caseSteps[$num]['content'] = isset($caseSteps[$num]['content']) ? "{$caseSteps[$num]['content']}\n{$step}" : "\n{$step}";
  2432. }
  2433. elseif($field == 'stepDesc')
  2434. {
  2435. $num = 1;
  2436. $caseSteps[$num]['content'] = $step;
  2437. $caseSteps[$num]['type'] = 'step';
  2438. $caseSteps[$num]['number'] = $num;
  2439. }
  2440. elseif($field == 'stepExpect' && isset($stepData[$row]['desc']))
  2441. {
  2442. end($stepData[$row]['desc']);
  2443. $num = key($stepData[$row]['desc']);
  2444. $caseSteps[$num]['content'] = $step;
  2445. $caseSteps[$num]['number'] = $num;
  2446. }
  2447. }
  2448. return $caseSteps;
  2449. }
  2450. /**
  2451. * 处理导出的用例数据。
  2452. * Process export cases.
  2453. *
  2454. * @param array $cases
  2455. * @param int $productID
  2456. * @param int $taskID
  2457. * @access protected
  2458. * @return array
  2459. */
  2460. protected function processCasesForExport($cases, $productID, $taskID)
  2461. {
  2462. $products = $this->product->getPairs('', 0, '', 'all');
  2463. $branches = $this->loadModel('branch')->getPairs($productID);
  2464. $users = $this->loadModel('user')->getPairs('noletter');
  2465. $results = $this->testcase->getCaseResultsForExport(array_keys($cases), $taskID);
  2466. $relatedModules = $this->loadModel('tree')->getAllModulePairs('case');
  2467. $relatedStories = $this->testcase->getRelatedStories($cases);
  2468. $relatedCases = $this->testcase->getRelatedCases($cases);
  2469. $relatedSteps = $this->testcase->getRelatedSteps(array_keys($cases));
  2470. $relatedFiles = $this->testcase->getRelatedFiles(array_keys($cases));
  2471. $relatedScenes = $this->testcase->getSceneMenu($productID, 0);
  2472. if($taskID) $this->app->loadLang('testtask');
  2473. $cases = $this->testcase->appendData($cases);
  2474. foreach($cases as $case) $this->processCaseForExport($case, $products, $branches, $users, $results, $relatedModules, $relatedStories, $relatedCases, $relatedSteps, $relatedFiles, $relatedScenes);
  2475. return $cases;
  2476. }
  2477. /**
  2478. * 处理导出的某个用例。
  2479. * Process export case.
  2480. *
  2481. * @param object $case
  2482. * @param array $products
  2483. * @param array $branches
  2484. * @param array $users
  2485. * @param array $results
  2486. * @param array $relatedModules
  2487. * @param array $relatedStories
  2488. * @param array $relatedCases
  2489. * @param array $relatedSteps
  2490. * @param array $relatedFiles
  2491. * @param array $relatedScenes
  2492. * @access protected
  2493. * @return void
  2494. */
  2495. protected function processCaseForExport($case, $products, $branches, $users, $results, $relatedModules, $relatedStories, $relatedCases, $relatedSteps, $relatedFiles, $relatedScenes)
  2496. {
  2497. $case->stepDesc = '';
  2498. $case->stepExpect = '';
  2499. $case->real = '';
  2500. $case->openedDate = !helper::isZeroDate($case->openedDate) ? substr($case->openedDate, 0, 10) : '';
  2501. $case->lastEditedDate = !helper::isZeroDate($case->lastEditedDate) ? substr($case->lastEditedDate, 0, 10) : '';
  2502. $case->lastRunDate = !helper::isZeroDate($case->lastRunDate) ? $case->lastRunDate : '';
  2503. $case->product = isset($products[$case->product]) ? $products[$case->product] . "(#$case->product)" : '';
  2504. $case->branch = isset($branches[$case->branch]) ? $branches[$case->branch] . "(#$case->branch)" : '';
  2505. $case->module = isset($relatedModules[$case->module])? $relatedModules[$case->module] . "(#$case->module)" : '';
  2506. $case->story = isset($relatedStories[$case->story]) ? $relatedStories[$case->story] . "(#$case->story)" : '';
  2507. $case->scene = isset($relatedScenes[$case->scene]) ? $relatedScenes[$case->scene] . "(#$case->scene)" : '';
  2508. $case->pri = zget($this->lang->testcase->priList, $case->pri);
  2509. $case->type = zget($this->lang->testcase->typeList, $case->type);
  2510. $case->status = $this->processStatus('testcase', $case);
  2511. $case->openedBy = zget($users, $case->openedBy);
  2512. $case->lastEditedBy = zget($users, $case->lastEditedBy);
  2513. $case->lastRunner = zget($users, $case->lastRunner);
  2514. $case->lastRunResult = zget($this->lang->testcase->resultList, $case->lastRunResult);
  2515. $case->bugsAB = $case->bugs;
  2516. $case->resultsAB = $case->results;
  2517. $case->stepNumberAB = $case->stepNumber;
  2518. unset($case->bugs);
  2519. unset($case->results);
  2520. unset($case->stepNumber);
  2521. unset($case->caseFails);
  2522. $this->processStepForExport($case, zget($results, $case->id, array()), $relatedSteps);
  2523. $this->processStageForExport($case);
  2524. $this->processFileForExport($case, $relatedFiles);
  2525. if($case->linkCase) $this->processLinkCaseForExport($case);
  2526. }
  2527. /**
  2528. * 处理导出的用例的步骤。
  2529. * Process step of case for export.
  2530. *
  2531. * @param object $case
  2532. * @param array $result
  2533. * @param array $relatedSteps
  2534. * @access protected
  2535. * @return void
  2536. */
  2537. protected function processStepForExport($case, $result, $relatedSteps)
  2538. {
  2539. $case->real = '';
  2540. if(!empty($result) && !isset($relatedSteps[$case->id]))
  2541. {
  2542. $firstStep = reset($result);
  2543. $case->real = $firstStep['real'];
  2544. }
  2545. if(!isset($case->stepDesc)) $case->stepDesc = '';
  2546. if(!isset($case->stepExpect)) $case->stepExpect = '';
  2547. if(!empty($case->id) && !empty($relatedSteps[$case->id]))
  2548. {
  2549. $preGrade = 1;
  2550. $parentSteps = array();
  2551. $key = array(0, 0, 0);
  2552. foreach($relatedSteps[$case->id] as $step)
  2553. {
  2554. $grade = 1;
  2555. $parentSteps[$step->id] = $step->parent;
  2556. if(isset($parentSteps[$step->parent])) $grade = isset($parentSteps[$parentSteps[$step->parent]]) ? 3 : 2;
  2557. if($grade > $preGrade)
  2558. {
  2559. $key[$grade - 1] = 1;
  2560. }
  2561. else
  2562. {
  2563. if($grade < $preGrade)
  2564. {
  2565. if($grade < 2) $key[1] = 0;
  2566. if($grade < 3) $key[2] = 0;
  2567. }
  2568. $key[$grade - 1] ++;
  2569. }
  2570. $stepID = implode('.', $key);
  2571. $stepID = str_replace('.0', '', $stepID);
  2572. $stepID = str_replace('.0', '', $stepID);
  2573. $sign = (in_array($this->post->fileType, array('html', 'xml'))) ? '<br />' : "\n";
  2574. $case->stepDesc .= $stepID . ". " . htmlspecialchars_decode($step->desc) . $sign;
  2575. $case->stepExpect .= $stepID . ". " . htmlspecialchars_decode($step->expect) . $sign;
  2576. $case->real .= $stepID . ". " . (isset($result[$step->id]) ? $result[$step->id]['real'] : '') . $sign;
  2577. $preGrade = $grade;
  2578. }
  2579. }
  2580. $case->stepDesc = !empty($case->stepDesc) ? trim($case->stepDesc) : '';
  2581. $case->stepExpect = !empty($case->stepExpect) ? trim($case->stepExpect) : '';
  2582. $case->real = !empty($case->real) ? trim($case->real) : '';
  2583. if($this->post->fileType == 'csv')
  2584. {
  2585. $case->stepDesc = str_replace('"', '""', $case->stepDesc);
  2586. $case->stepExpect = str_replace('"', '""', $case->stepExpect);
  2587. }
  2588. }
  2589. /**
  2590. * 处理导出的用例的适用阶段。
  2591. * Process stage of case for export.
  2592. *
  2593. * @param object $case
  2594. * @access protected
  2595. * @return void
  2596. */
  2597. protected function processStageForExport($case)
  2598. {
  2599. $case->stage = explode(',', $case->stage);
  2600. foreach($case->stage as $key => $stage) $case->stage[$key] = isset($this->lang->testcase->stageList[$stage]) ? $this->lang->testcase->stageList[$stage] : $stage;
  2601. $case->stage = join("\n", $case->stage);
  2602. }
  2603. /**
  2604. * 处理导出用例的相关用例。
  2605. * Process link case of the case for export.
  2606. *
  2607. * @param object $case
  2608. * @access protected
  2609. * @return void
  2610. */
  2611. protected function processLinkCaseForExport($case)
  2612. {
  2613. $tmpLinkCases = array();
  2614. $linkCaseIdList = explode(',', $case->linkCase);
  2615. foreach($linkCaseIdList as $linkCaseID)
  2616. {
  2617. $linkCaseID = trim($linkCaseID);
  2618. $tmpLinkCases[] = isset($relatedCases[$linkCaseID]) ? $relatedCases[$linkCaseID] . "(#$linkCaseID)" : $linkCaseID;
  2619. }
  2620. $case->linkCase = join("; \n", $tmpLinkCases);
  2621. }
  2622. /**
  2623. * 处理导出用例的附件。
  2624. * Process file of case for export.
  2625. *
  2626. * @param object $case
  2627. * @param array $relatedFiles
  2628. * @access protected
  2629. * @return void
  2630. */
  2631. protected function processFileForExport($case, $relatedFiles)
  2632. {
  2633. $case->files = '';
  2634. if(isset($relatedFiles[$case->id]))
  2635. {
  2636. foreach($relatedFiles[$case->id] as $file)
  2637. {
  2638. $fileURL = common::getSysURL() . $this->createLink('file', 'download', "fileID={$file->id}");
  2639. $case->files .= html::a($fileURL, $file->title, '_blank') . '<br />';
  2640. }
  2641. }
  2642. }
  2643. /**
  2644. * 处理批量编辑用例的步骤和预期。
  2645. * Process steps and expects for batch edit.
  2646. *
  2647. * @param array $cases
  2648. * @access protected
  2649. * @return array
  2650. */
  2651. protected function processStepsAndExpectsForBatchEdit($cases)
  2652. {
  2653. $relatedSteps = $this->testcase->getRelatedSteps(array_column($cases, 'id'));
  2654. foreach($cases as $case)
  2655. {
  2656. $this->processStepForExport($case, array(), $relatedSteps);
  2657. $case->steps = $case->stepDesc;
  2658. $case->expects = $case->stepExpect;
  2659. }
  2660. return $cases;
  2661. }
  2662. /**
  2663. * 获取导出模板的字段。
  2664. * Get fields for export template.
  2665. *
  2666. * @param string $productType
  2667. * @access protected
  2668. * @return array
  2669. */
  2670. protected function getFieldsForExportTemplate($productType)
  2671. {
  2672. $fields = array();
  2673. $fields['branch'] = $this->lang->product->branchName[$productType];
  2674. $fields['module'] = $this->lang->testcase->module;
  2675. $fields['title'] = $this->lang->testcase->title;
  2676. $fields['precondition'] = $this->lang->testcase->precondition;
  2677. $fields['stepDesc'] = $this->lang->testcase->stepDesc;
  2678. $fields['stepExpect'] = $this->lang->testcase->stepExpect;
  2679. $fields['keywords'] = $this->lang->testcase->keywords;
  2680. $fields['pri'] = $this->lang->testcase->pri;
  2681. $fields['type'] = $this->lang->testcase->type;
  2682. $fields['stage'] = $this->lang->testcase->stage;
  2683. $fields[''] = '';
  2684. $fields['typeValue'] = $this->lang->testcase->lblTypeValue;
  2685. $fields['stageValue'] = $this->lang->testcase->lblStageValue;
  2686. $fields['branchValue'] = $this->lang->product->branchName[$productType];
  2687. if($productType == 'normal')
  2688. {
  2689. unset($fields['branch']);
  2690. unset($fields['branchValue']);
  2691. }
  2692. return $fields;
  2693. }
  2694. /**
  2695. * 获取导出模板的行。
  2696. * Get rows for export template.
  2697. *
  2698. * @param object $product
  2699. * @param int $num
  2700. * @access protected
  2701. * @return array
  2702. */
  2703. protected function getRowsForExportTemplate($product, $num)
  2704. {
  2705. $this->loadModel('tree');
  2706. $projectID = $this->app->tab == 'project' ? $this->session->project : 0;
  2707. $branches = $this->loadModel('branch')->getPairs($product->id, '' , $projectID);
  2708. $modules = $product->type == 'normal' ? $this->tree->getOptionMenu($product->id, 'case', 0, 'all') : array();
  2709. foreach($branches as $branchID => $branchName)
  2710. {
  2711. $branches[$branchID] = $branchName . "(#$branchID)";
  2712. $modules += $this->tree->getOptionMenu($product->id, 'case', 0, (string)$branchID);
  2713. }
  2714. $rows = array();
  2715. for($i = 0; $i < $num; $i++)
  2716. {
  2717. foreach($modules as $moduleID => $module)
  2718. {
  2719. $row = new stdclass();
  2720. $row->module = $module . "(#$moduleID)";
  2721. $row->stepDesc = "1. \n2. \n3.";
  2722. $row->stepExpect = "1. \n2. \n3.";
  2723. if(empty($rows))
  2724. {
  2725. unset($this->lang->testcase->typeList['unit']);
  2726. $row->typeValue = join("\n", $this->lang->testcase->typeList);
  2727. $row->stageValue = join("\n", $this->lang->testcase->stageList);
  2728. if($product->type != 'normal') $row->branchValue = join("\n", $branches);
  2729. }
  2730. $rows[] = $row;
  2731. }
  2732. }
  2733. return $rows;
  2734. }
  2735. /**
  2736. * 获取创建用例时的状态值。
  2737. * Get status for create.
  2738. *
  2739. * @access public
  2740. * @return string
  2741. */
  2742. public function getStatusForCreate()
  2743. {
  2744. if($this->testcase->forceNotReview() || !$this->post->needReview) return 'normal';
  2745. return 'wait';
  2746. }
  2747. /**
  2748. * 获取评审用例时的状态值。
  2749. * Get status for review.
  2750. *
  2751. * @param object $case
  2752. * @access private
  2753. * @return string
  2754. */
  2755. private function getStatusForReview($case)
  2756. {
  2757. if($this->post->result == 'pass') return 'normal';
  2758. return zget($case, 'status', '');
  2759. }
  2760. /**
  2761. * 获取更新的状态。
  2762. * Get status for update.
  2763. *
  2764. * @param object $case
  2765. * @access public
  2766. * @return bool|array
  2767. */
  2768. public function getStatusForUpdate($case)
  2769. {
  2770. if($this->post->lastEditedDate && $case->lastEditedDate != $this->post->lastEditedDate)
  2771. {
  2772. dao::$errors['message'][] = $this->lang->error->editedByOther;
  2773. return false;
  2774. }
  2775. /* 判断步骤是否变更。*/
  2776. /* Judge steps changed or not. */
  2777. $stepChanged = false;
  2778. if($this->post->steps)
  2779. {
  2780. $steps = array();
  2781. foreach($this->post->steps as $key => $desc)
  2782. {
  2783. if(!$desc) continue;
  2784. $steps[] = array('desc' => trim($desc), 'type' => trim(zget($this->post->stepType, $key, 'step')), 'expect' => trim(zget($this->post->expects, $key, '')));
  2785. }
  2786. /* 如果步骤数量发生变化,步骤变更。*/
  2787. /* If step count changed, case changed. */
  2788. if(count($case->steps) != count($steps))
  2789. {
  2790. $stepChanged = true;
  2791. }
  2792. else
  2793. {
  2794. /* 对比步骤的每一步。*/
  2795. /* Compare every step. */
  2796. $i = 0;
  2797. foreach($case->steps as $key => $oldStep)
  2798. {
  2799. if(trim($oldStep->desc) != trim($steps[$i]['desc']) || trim($oldStep->expect) != $steps[$i]['expect'] || trim($oldStep->type) != $steps[$i]['type'])
  2800. {
  2801. $stepChanged = true;
  2802. break;
  2803. }
  2804. $i++;
  2805. }
  2806. }
  2807. }
  2808. $status = $this->post->status ? $this->post->status : $case->status;
  2809. if(!$this->testcase->forceNotReview() && $stepChanged) $status = 'wait';
  2810. if($this->post->title && $case->title != $this->post->title) $stepChanged = true;
  2811. if(isset($_POST['precondition']) && $case->precondition != $this->post->precondition) $stepChanged = true;
  2812. if(!empty($_FILES['files']['name'][0])) $stepChanged = true;
  2813. if(!empty($_POST['deleteFiles'])) $stepChanged = true;
  2814. return array($stepChanged, $status);
  2815. }
  2816. /**
  2817. * 创建 freemind 的 xml 文档。
  2818. * Create freemind's xml doc.
  2819. *
  2820. * @param int $productID
  2821. * @param string $productName
  2822. * @param array $context
  2823. * @access protected
  2824. * @return object
  2825. */
  2826. protected function createFreeMindXmlDoc($productID, $productName, $context)
  2827. {
  2828. $this->classFreeMind = $this->app->loadClass('freemind');
  2829. $xmlDoc = new DOMDocument('1.0', 'UTF-8');
  2830. $xmlDoc->formatOutput = true;
  2831. $versionAttr = $xmlDoc->createAttribute('version');
  2832. $versionAttr->value = '1.0.1';
  2833. $textAttr = $xmlDoc->createAttribute('TEXT');
  2834. $textAttr->value = $this->classFreeMind->toText("$productName", $productID);
  2835. $mapNode = $xmlDoc->createElement('map');
  2836. $mapNode->appendChild($versionAttr);
  2837. $productNode = $xmlDoc->createElement('node');
  2838. $productNode->appendChild($textAttr);
  2839. $mapNode->appendChild($productNode);
  2840. $xmlDoc->appendChild($mapNode);
  2841. $sceneNodes = array();
  2842. $moduleNodes = array();
  2843. $this->classFreeMind->createModuleNode($xmlDoc, $context, $productNode, $moduleNodes);
  2844. $this->classFreeMind->createSceneNode($xmlDoc, $context, $productNode, $moduleNodes, $sceneNodes);
  2845. $this->classFreeMind->createTestcaseNode($xmlDoc, $context, $productNode, $moduleNodes, $sceneNodes);
  2846. return $xmlDoc;
  2847. }
  2848. /**
  2849. * 获取 xmind 导出的数据。
  2850. * Get export data of xmind.
  2851. *
  2852. * @param int $productID
  2853. * @param string $productName
  2854. * @param array $context
  2855. * @access protected
  2856. * @return string
  2857. */
  2858. protected function getXmindExportData($productID, $productName, $context)
  2859. {
  2860. $this->classXmind = $this->app->loadClass('xmind');
  2861. $xmlDoc = new DOMDocument('1.0', 'UTF-8');
  2862. $xmlDoc->formatOutput = true;
  2863. $titleAttr = $xmlDoc->createElement('title', $this->classXmind->toText("$productName", $productID));
  2864. $idAttr = $xmlDoc->createElement('id', $this->classXmind->toText(uniqid(), ''));
  2865. $productChildren = $xmlDoc->createElement('children');
  2866. $productTopics = $this->classXmind->createTopics($xmlDoc);
  2867. $productChildren->appendChild($productTopics);
  2868. $class = $xmlDoc->createAttribute('structure-class');
  2869. $classValue = $xmlDoc->createTextNode('org.xmind.ui.map.clockwise');
  2870. $class->appendChild($classValue);
  2871. $productTopic = $xmlDoc->createElement('topic');
  2872. $productTopic->appendChild($titleAttr);
  2873. $productTopic->appendChild($idAttr);
  2874. $productTopic->appendChild($class);
  2875. $productTopic->appendChild($productChildren);
  2876. $titleAttr = $xmlDoc->createElement('title', 'sheet');
  2877. $sheet = $xmlDoc->createElement('sheet');
  2878. $theme = $xmlDoc->createAttribute('theme');
  2879. $themeValue = $xmlDoc->createTextNode('65q18ujpt3vgdbk1ifknidq03m');
  2880. $theme->appendChild($themeValue);
  2881. $sheet->appendChild($titleAttr);
  2882. $sheet->appendChild($theme);
  2883. $sheet->appendChild($productTopic);
  2884. $xmapContent = $this->classXmind->initXmapContent($xmlDoc);
  2885. $xmapContent->appendChild($sheet);
  2886. $xmlDoc->appendChild($xmapContent);
  2887. $sceneTopics = array();
  2888. $moduleTopics = array();
  2889. $this->classXmind->createModuleTopic($xmlDoc, $context, $productTopics, $moduleTopics);
  2890. $this->classXmind->createSceneTopic($xmlDoc, $context, $productTopics, $moduleTopics, $sceneTopics);
  2891. $this->classXmind->createTestcaseTopic($xmlDoc, $context, $productTopics, $moduleTopics, $sceneTopics);
  2892. $this->app->loadClass('pclzip', true);
  2893. $zfile = $this->app->loadClass('zfile');
  2894. /* Init xmind file. */
  2895. $exportPath = $this->app->getCacheRoot() . $this->app->user->account . uniqid() . '/';
  2896. if(is_dir($exportPath)) $zfile->removeDir($exportPath);
  2897. $zfile->mkdir($exportPath);
  2898. file_put_contents($exportPath . 'content.xml', $xmlDoc->saveXML());
  2899. /* create style.xml. */
  2900. $styleXmlContent = '<?xml version="1.0" encoding="UTF-8" standalone="no"?><xmap-styles xmlns="urn:xmind:xmap:xmlns:style:2.0" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg" version="2.0"><master-styles><style id="65q18ujpt3vgdbk1ifknidq03m" type="theme"/></master-styles></xmap-styles><?xml version="1.0" encoding="UTF-8" standalone="no"?><xmap-styles xmlns="urn:xmind:xmap:xmlns:style:2.0" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg" version="2.0"><master-styles><style id="65q18ujpt3vgdbk1ifknidq03m" type="theme"/></master-styles></xmap-styles>';
  2901. file_put_contents($exportPath . 'style.xml', $styleXmlContent);
  2902. /* create mate.xml. */
  2903. $metaXmlContent = '<?xml version="1.0" encoding="UTF-8" standalone="no"?><meta xmlns="urn:xmind:xmap:xmlns:meta:2.0" version="2.0"></meta>';
  2904. file_put_contents($exportPath . 'meta.xml', $metaXmlContent);
  2905. /* create META_INF/manifest.xml. */
  2906. $zfile->mkdir($exportPath . 'META-INF');
  2907. $manifestXmlContent = '<?xml version="1.0" encoding="UTF-8" standalone="no"?><manifest xmlns="urn:xmind:xmap:xmlns:manifest:2.0" version="2.0"></manifest>';
  2908. file_put_contents($exportPath . 'META-INF' . DS . 'manifest.xml', $manifestXmlContent);
  2909. /* Zip to xmind. */
  2910. $fileName = uniqid() . '.xmind';
  2911. helper::cd($exportPath);
  2912. $files = array('style.xml', 'meta.xml', 'content.xml', 'META-INF/manifest.xml');
  2913. $zip = new pclzip($fileName);
  2914. $zip->create($files);
  2915. $fileData = file_get_contents($exportPath . $fileName);
  2916. $zfile->removeDir($exportPath);
  2917. return $fileData;
  2918. }
  2919. /**
  2920. * 解析上传的 xmind 文件。
  2921. * Parse the upload xmind file.
  2922. *
  2923. * @param int $productID
  2924. * @param int|string $branch
  2925. * @access protected
  2926. * @return void
  2927. */
  2928. protected function parseUploadFile($productID, $branch)
  2929. {
  2930. /* 创建导入目录。*/
  2931. /* Create import Directory. */
  2932. $importDir = $this->app->getTmpRoot() . 'import';
  2933. if(!is_dir($importDir)) mkdir($importDir, 0755, true);
  2934. /* 将上传的临时文件移动到指定的临时位置。*/
  2935. /* Move the uploaded temporary file to the specified temporary location. */
  2936. $fileName = $this->app->user->id . '-xmind';
  2937. $filePath = $importDir . '/' . $fileName;
  2938. $tmpFile = $filePath . 'tmp';
  2939. if(!move_uploaded_file($_FILES['file']['tmp_name'][0], $tmpFile)) return array('result' => 'fail', 'message' => $this->lang->testcase->errorXmindUpload);
  2940. /* 删掉已经存在的当前用户的导入目录。*/
  2941. /* Remove the file path. */
  2942. $this->classFile = $this->app->loadClass('zfile');
  2943. if(is_dir($filePath)) $this->classFile->removeDir($filePath);
  2944. /* 压缩临时文件。*/
  2945. /* Zip the temporary file. */
  2946. $this->app->loadClass('pclzip', true);
  2947. $zip = new pclzip($tmpFile);
  2948. /* 获取 ZIP 文件中的内容列表。*/
  2949. /* Get the content list from the zip file. */
  2950. $files = $zip->listContent();
  2951. $removePath = $files[0]['filename'];
  2952. /* 限制解压的文件内容以阻止 ZIP 解压缩的目录穿越漏洞。*/
  2953. /* Limit the file content to prevent the directory traversal vulnerability of ZIP decompression. */
  2954. $extractFiles = array('content.json', 'content.xml');
  2955. if(in_array($removePath, $extractFiles)) $removePath = '';
  2956. if($zip->extract(PCLZIP_OPT_PATH, $filePath, PCLZIP_OPT_BY_NAME, $extractFiles, PCLZIP_OPT_REMOVE_PATH, $removePath) == 0) return array('result' => 'fail', 'message' => $this->lang->testcase->errorXmindUpload);
  2957. $this->classFile->removeFile($tmpFile);
  2958. $this->classXmind = $this->app->loadClass('xmind');
  2959. if(file_exists($filePath . '/content.json'))
  2960. {
  2961. $fetchResult = $this->fetchByJSON($filePath, $productID, $branch);
  2962. }
  2963. else
  2964. {
  2965. $fetchResult = $this->fetchByXML($filePath, $productID);
  2966. }
  2967. if($fetchResult['result'] == 'fail') return $fetchResult;
  2968. $this->session->set('xmindImport', $filePath);
  2969. $this->session->set('xmindImportType', $fetchResult['type']);
  2970. return (int)$fetchResult['pID'];
  2971. }
  2972. /**
  2973. * 获取 content.xml 的内容。
  2974. * Fetch content.xml.
  2975. *
  2976. * @param string $filePath
  2977. * @param int $productID
  2978. * @access public
  2979. * @return array
  2980. */
  2981. private function fetchByXML($filePath, $productID)
  2982. {
  2983. $file = $filePath . '/content.xml';
  2984. $xmlNode = simplexml_load_file($file);
  2985. $title = (string)$xmlNode->sheet->topic->title;
  2986. if(!is_string($title) || strlen($title) == 0) return array('result' => 'fail', 'message' => $this->lang->testcase->errorXmindUpload);
  2987. $pID = $productID;
  2988. if($this->classXmind->endsWith($title, ']'))
  2989. {
  2990. $tmpID = $this->classXmind->getBetween($title, '[', ']');
  2991. if(!empty($tmpID))
  2992. {
  2993. $product = $this->loadModel('product')->getByID((int)$tmpID);
  2994. if(!$product || $product->deleted) return array('result' => 'fail', 'message' => $this->lang->testcase->errorImportBadProduct);
  2995. $pID = $tmpID;
  2996. }
  2997. }
  2998. return array('result' => 'success', 'pID' => $pID, 'type' => 'xml');
  2999. }
  3000. /**
  3001. * 获取 content.json 的内容。
  3002. * Fetch by json.
  3003. *
  3004. * @param string $filePath
  3005. * @param int $productID
  3006. * @param int|string $branch
  3007. * @access public
  3008. * @return array
  3009. */
  3010. function fetchByJSON($filePath, $productID, $branch)
  3011. {
  3012. $file = $filePath . '/content.json';
  3013. $jsonStr = file_get_contents($file);
  3014. $jsonDatas = json_decode($jsonStr, true);
  3015. $title = $jsonDatas[0]['rootTopic']['title'];
  3016. if(strlen($title) == 0) return array('result' => 'fail', 'message' => $this->lang->testcase->errorXmindUpload);
  3017. $pID = $productID;
  3018. if($this->classXmind->endsWith($title, ']'))
  3019. {
  3020. $tmpID = $this->classXmind->getBetween($title, '[', ']');
  3021. if(!empty($tmpID))
  3022. {
  3023. $product = $this->loadModel('product')->getByID((int)$tmpID);
  3024. if(!$product || $product->deleted) return array('result' => 'fail', 'message' => $this->lang->testcase->errorImportBadProduct);
  3025. $pID = $tmpID;
  3026. }
  3027. }
  3028. return array('result' => 'success', 'pID' => $pID, 'type' => 'json');
  3029. }
  3030. }