PageRenderTime 87ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/functions/requirement_spec_mgr.class.php

https://bitbucket.org/pfernandez/testlink1.9.6
PHP | 1752 lines | 905 code | 187 blank | 660 comment | 102 complexity | 1e2884e1ca4a8c448c3a0ee6f4322385 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * TestLink Open Source Project - http://testlink.sourceforge.net/
  4. * This script is distributed under the GNU General Public License 2 or later.
  5. *
  6. * Filename $RCSfile: requirement_spec_mgr.class.php,v $
  7. *
  8. * @version $Revision: 1.82 $
  9. * @modified $Date: 2010/05/11 18:36:26 $ by $Author: franciscom $
  10. * @author Francisco Mancardi
  11. *
  12. * Manager for requirement specification (requirement container)
  13. *
  14. * @internal revision:
  15. * 20100320 - franciscom - xmlToMapReqSpec() added attributes: type,total_req
  16. * 20100311 - franciscom - fixed bug due to missed isset() control
  17. * 20100307 - amitkhullar - small bug fix for Requirements based report.
  18. * 20100209 - franciscom - changes in delete_subtree_objects() call due to BUGID 3147
  19. * 20091228 - franciscom - get_requirements() - refactored to manage req versions
  20. * get_coverage() - refactored to manage req versions
  21. * 20091225 - franciscom - new method - generateDocID()
  22. * 20091223 - franciscom - new method - copy_to() + changes to check_main_data()
  23. * 20091209 - asimon - contrib for testcase creation, BUGID 2996
  24. * 20091202 - franciscom - create(), update()
  25. * added contribution by asimon83/mx-julian that creates
  26. * links inside scope field.
  27. *
  28. * 20091122 - franciscom - new methods getByDocID(), check_main_data()
  29. * 20091119 - franciscom - added doc_id management
  30. *
  31. * 20090525 - franciscom - avoid getDisplayName() crash due to deleted user
  32. * 20090514 - franciscom - BUGID 2491
  33. * 20090427 - amitkhullar- BUGID : 2439 Modified query to handle lower case status codes.
  34. * 20090322 - franciscom - create() - added node_order.
  35. * check_title() - improvements now manages test project id and parent id.
  36. * get_by_title() - improvements now manages test project id and parent id.
  37. *
  38. * 20090322 - franciscom - xmlToMapReqSpec()
  39. * 20090321 - franciscom - added customFieldValuesAsXML() to improve exportReqSpecToXML()
  40. *
  41. * 20090315 - franciscom - added delete_deep();
  42. *
  43. * 20090222 - franciscom - added getReqTree(), get_by_id() added node_order in result
  44. * exportReqSpecToXML() (will be available on TL 1.9)
  45. *
  46. * 20090111 - franciscom - BUGID 1967 - html_table_of_custom_field_inputs()
  47. * get_linked_cfields()
  48. *
  49. */
  50. require_once( dirname(__FILE__) . '/attachments.inc.php' );
  51. require_once( dirname(__FILE__) . '/requirements.inc.php' );
  52. class requirement_spec_mgr extends tlObjectWithAttachments
  53. {
  54. const CASE_SENSITIVE=0;
  55. const CASE_INSENSITIVE=1;
  56. var $db;
  57. var $cfield_mgr;
  58. var $tree_mgr;
  59. var $import_file_types = array("XML" => "XML");
  60. var $export_file_types = array("XML" => "XML");
  61. var $my_node_type;
  62. var $node_types_descr_id;
  63. var $node_types_id_descr;
  64. var $attachmentTableName;
  65. /*
  66. contructor
  67. args: db: reference to db object
  68. returns: instance of requirement_spec_mgr
  69. */
  70. function __construct(&$db)
  71. {
  72. $this->db = &$db;
  73. $this->cfield_mgr = new cfield_mgr($this->db);
  74. $this->tree_mgr = new tree($this->db);
  75. $this->node_types_descr_id = $this->tree_mgr->get_available_node_types();
  76. $this->node_types_id_descr = array_flip($this->node_types_descr_id);
  77. $this->my_node_type = $this->node_types_descr_id['requirement_spec'];
  78. $this->attachmentTableName = 'req_specs';
  79. tlObjectWithAttachments::__construct($this->db,$this->attachmentTableName);
  80. $this->object_table=$this->tables['req_specs'];
  81. }
  82. /*
  83. function: get_export_file_types
  84. getter
  85. args: -
  86. returns: map
  87. key: export file type code
  88. value: export file type verbose description
  89. */
  90. function get_export_file_types()
  91. {
  92. return $this->export_file_types;
  93. }
  94. /*
  95. function: get_impor_file_types
  96. getter
  97. args: -
  98. returns: map
  99. key: import file type code
  100. value: import file type verbose description
  101. */
  102. function get_import_file_types()
  103. {
  104. return $this->import_file_types;
  105. }
  106. /*
  107. function: create
  108. args:
  109. tproject_id: requirement spec parent (till we will manage unlimited tree depth)
  110. parent_id:
  111. doc_id
  112. title
  113. scope
  114. countReq
  115. user_id: requirement spec author
  116. [type]
  117. [node_order]
  118. [options]
  119. returns: map with following keys:
  120. status_ok -> 1/0
  121. msg -> some simple message, useful when status_ok ==0
  122. id -> id of requirement specification
  123. rev:
  124. 20091202 - franciscom - added contribution by asimon83/mx-julian
  125. 20080830 - franciscom - added new argument parent_id
  126. 20080318 - franciscom - removed code to get last inserted id
  127. */
  128. function create($tproject_id,$parent_id,$doc_id,$title, $scope,$countReq,$user_id,
  129. $type = TL_REQ_SPEC_TYPE_FEATURE,$node_order=null, $options=null)
  130. {
  131. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  132. $result=array('status_ok' => 0, 'msg' => 'ko', 'id' => 0);
  133. $title=trim($title);
  134. $chk=$this->check_main_data($title,$doc_id,$tproject_id,$parent_id);
  135. $result['msg']=$chk['msg'];
  136. $my['options'] = array( 'actionOnDuplicate' => "block");
  137. $my['options'] = array_merge($my['options'], (array)$options);
  138. if ($chk['status_ok'])
  139. {
  140. /* contribution by asimon83/mx-julian */
  141. if( config_get('internal_links')->enable )
  142. {
  143. $scope = req_link_replace($this->db, $scope, $tproject_id);
  144. }
  145. /* end contribution by asimon83/mx-julian */
  146. $req_spec_id = $this->tree_mgr->new_node($parent_id,$this->my_node_type,$title,$node_order);
  147. $sql = "/* $debugMsg */ INSERT INTO {$this->object_table} " .
  148. " (id, testproject_id, doc_id, scope, type, total_req, author_id, creation_ts) " .
  149. " VALUES (" . $req_spec_id . "," . $tproject_id . ",'" .
  150. $this->db->prepare_string($doc_id) . "','" .
  151. $this->db->prepare_string($scope) . "','" . $this->db->prepare_string($type) . "','" .
  152. $this->db->prepare_string($countReq) . "'," . $user_id . ", " . $this->db->db_now() . ")";
  153. if (!$this->db->exec_query($sql))
  154. {
  155. $result['msg']=lang_get('error_creating_req_spec');
  156. }
  157. else
  158. {
  159. $result['id']=$req_spec_id;
  160. $result['status_ok'] = 1;
  161. $result['msg'] = 'ok';
  162. }
  163. }
  164. return $result;
  165. }
  166. /*
  167. function: get_by_id
  168. args : id: requirement spec id
  169. returns: null if query fails
  170. map with requirement spec info
  171. */
  172. function get_by_id($id)
  173. {
  174. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  175. $sql = "/* $debugMsg */ " .
  176. " SELECT '' AS author, '' AS modifier, NH.node_order, " .
  177. " RSPEC.id,testproject_id,RSPEC.scope,RSPEC.total_req,RSPEC.type," .
  178. " RSPEC.author_id,RSPEC.creation_ts,RSPEC.modifier_id," .
  179. " RSPEC.modification_ts,NH.name AS title,RSPEC.doc_id " .
  180. " FROM {$this->object_table} RSPEC, {$this->tables['nodes_hierarchy']} NH" .
  181. " WHERE RSPEC.id = NH.id " .
  182. " AND RSPEC.id = {$id}";
  183. $recordset = $this->db->get_recordset($sql);
  184. $rs = null;
  185. if(!is_null($recordset))
  186. {
  187. // Decode users
  188. $rs = $recordset[0];
  189. if(trim($rs['author_id']) != "")
  190. {
  191. $user = tlUser::getByID($this->db,$rs['author_id']);
  192. // need to manage deleted users
  193. if($user)
  194. {
  195. $rs['author'] = $user->getDisplayName();
  196. }
  197. else
  198. {
  199. $rs['author'] = lang_get('undefined');
  200. }
  201. }
  202. if(trim($rs['modifier_id']) != "")
  203. {
  204. $user = tlUser::getByID($this->db,$rs['modifier_id']);
  205. // need to manage deleted users
  206. if($user)
  207. {
  208. $rs['modifier'] = $user->getDisplayName();
  209. }
  210. else
  211. {
  212. $rs['modifier'] = lang_get('undefined');
  213. }
  214. }
  215. }
  216. return $rs;
  217. }
  218. /**
  219. * get analyse based on requirements and test specification
  220. *
  221. * @param integer $id: Req Spec id
  222. * @return array Coverage in three internal arrays: covered, uncovered, nottestable REQ
  223. * @author martin havlat
  224. */
  225. function get_coverage($id)
  226. {
  227. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  228. $req_mgr = new requirement_mgr($this->db);
  229. // $statusFilter = " AND status IN('" . strtoupper(VALID_REQ)."','".VALID_REQ."') ";
  230. // $order_by = " ORDER BY req_doc_id,title";
  231. $output = array( 'covered' => array(), 'uncovered' => array(),'nottestable' => array());
  232. // function get_requirements($id, $range = 'all', $testcase_id = null, $options=null, $filters = null)
  233. $getOptions = array('order_by' => " ORDER BY req_doc_id,title");
  234. $getFilters = array('status' => VALID_REQ);
  235. $validReq = $this->get_requirements($id,'all',null,$getOptions,$getFilters);
  236. // get not-testable requirements
  237. $getFilters = array('status' => NON_TESTABLE_REQ);
  238. $output['nottestable'] = $this->get_requirements($id,'all',null,$getOptions,$getFilters);
  239. // get coverage
  240. if (sizeof($validReq))
  241. {
  242. foreach ($validReq as $req)
  243. {
  244. // collect TC for REQ
  245. $arrCoverage = $req_mgr->get_coverage($req['id']);
  246. if (count($arrCoverage) > 0)
  247. {
  248. // add information about coverage
  249. $req['coverage'] = $arrCoverage;
  250. $output['covered'][] = $req;
  251. }
  252. else
  253. {
  254. $output['uncovered'][] = $req;
  255. }
  256. }
  257. }
  258. return $output;
  259. }
  260. /**
  261. * get requirement coverage metrics
  262. *
  263. * @param integer $srs_id
  264. * @return array results
  265. * @author havlatm
  266. */
  267. function get_metrics($id)
  268. {
  269. $output = array('notTestable' => 0, 'total' => 0, 'covered' => 0, 'uncovered' => 0);
  270. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  271. $getFilters = array('status' => NON_TESTABLE_REQ);
  272. $output['notTestable'] = $this->get_requirements_count($id,'all',null,$getFilters);
  273. $sql = "/* $debugMsg */ SELECT count(0) AS cnt FROM {$this->tables['requirements']} WHERE srs_id={$id}";
  274. $output['total'] = $this->db->fetchFirstRowSingleColumn($sql,'cnt');
  275. //
  276. $sql = "/* $debugMsg */ SELECT total_req FROM {$this->object_table} WHERE id={$id}";
  277. $output['expectedTotal'] = $this->db->fetchFirstRowSingleColumn($sql,'total_req');
  278. if ($output['expectedTotal'] == 0)
  279. {
  280. $output['expectedTotal'] = $output['total'];
  281. }
  282. $sql = "/* $debugMsg */ SELECT DISTINCT REQ.id " .
  283. " FROM {$this->tables['requirements']} REQ " .
  284. " JOIN {$this->tables['req_coverage']} REQ_COV ON REQ.id=REQ_COV.req_id" .
  285. " WHERE REQ.srs_id={$id} " ;
  286. $rs = $this->db->get_recordset($sql);
  287. if (!is_null($rs))
  288. {
  289. $output['covered'] = count($rs);
  290. }
  291. $output['uncovered'] = $output['expectedTotal'] - $output['total'];
  292. return $output;
  293. }
  294. /*
  295. function: get_all_in_testproject
  296. get info about all req spec defined for a testproject
  297. args: tproject_id
  298. [order_by]
  299. returns: null if no srs exits, or no srs exists for id
  300. array, where each element is a map with req spec data.
  301. map keys:
  302. id
  303. testproject_id
  304. title
  305. scope
  306. total_req
  307. type
  308. author_id
  309. creation_ts
  310. modifier_id
  311. modification_ts
  312. */
  313. function get_all_in_testproject($tproject_id,$order_by=" ORDER BY title")
  314. {
  315. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  316. $sql = "/* $debugMsg */ " .
  317. " SELECT RSPEC.id,testproject_id,RSPEC.scope,RSPEC.total_req,RSPEC.type," .
  318. " RSPEC.author_id,RSPEC.creation_ts,RSPEC.modifier_id," .
  319. " RSPEC.modification_ts,NH.name AS title,NH.node_order " .
  320. " FROM {$this->object_table} RSPEC, {$this->tables['nodes_hierarchy']} NH " .
  321. " WHERE NH.id=RSPEC.id" .
  322. " AND testproject_id={$tproject_id}";
  323. if (!is_null($order_by))
  324. {
  325. $sql .= $order_by;
  326. }
  327. return $this->db->get_recordset($sql);
  328. }
  329. /*
  330. function: update
  331. args: id
  332. title
  333. scope
  334. countReq
  335. user_id,
  336. [type]
  337. returns: map with following keys:
  338. status_ok -> 1/0
  339. msg -> some simple message, useful when status_ok ==0
  340. */
  341. function update($id,$doc_id,$title, $scope, $countReq,$user_id,
  342. $type = TL_REQ_SPEC_TYPE_FEATURE,$node_order = null)
  343. {
  344. $result['status_ok'] = 1;
  345. $result['msg'] = 'ok';
  346. $title=trim_and_limit($title);
  347. $doc_id=trim_and_limit($doc_id);
  348. $path=$this->tree_mgr->get_path($id);
  349. $tproject_id = $path[0]['parent_id'];
  350. $last_idx=count($path)-1;
  351. $parent_id = $last_idx==0 ? null : $path[$last_idx]['parent_id'];
  352. $chk=$this->check_main_data($title,$doc_id,$path[0]['parent_id'],$parent_id,$id);
  353. if ($chk['status_ok'])
  354. {
  355. /* contribution by asimon83/mx-julian */
  356. if( config_get('internal_links')->enable )
  357. {
  358. $scope = req_link_replace($this->db, $scope, $tproject_id);
  359. }
  360. /* end contribution by asimon83/mx-julian */
  361. $db_now = $this->db->db_now();
  362. $sql = " UPDATE {$this->object_table} " .
  363. " SET scope='" . $this->db->prepare_string($scope) . "', " .
  364. " doc_id='" . $this->db->prepare_string($doc_id) . "', " .
  365. " type='" . $this->db->prepare_string($type) . "', " .
  366. " total_req ='" . $this->db->prepare_string($countReq) . "', " .
  367. " modifier_id={$user_id},modification_ts={$db_now} ";
  368. $sql .= "WHERE id={$id}";
  369. if (!$this->db->exec_query($sql))
  370. {
  371. $result['msg']=lang_get('error_updating_reqspec');
  372. $result['status_ok'] = 0;
  373. }
  374. if( $result['status_ok'] )
  375. {
  376. // need to update node on tree
  377. $sql = " UPDATE {$this->tables['nodes_hierarchy']} " .
  378. " SET name='" . $this->db->prepare_string($title) . "'";
  379. if( !is_null($node_order) )
  380. {
  381. $sql .= ",node_order=" . intval($node_order);
  382. }
  383. $sql .= " WHERE id={$id}";
  384. if (!$this->db->exec_query($sql))
  385. {
  386. $result['msg']=lang_get('error_updating_reqspec');
  387. $result['status_ok'] = 0;
  388. }
  389. }
  390. }
  391. else
  392. {
  393. $result['status_ok']=$chk['status_ok'];
  394. $result['msg']=$chk['msg'];
  395. }
  396. return $result;
  397. }
  398. /*
  399. function: delete
  400. deletes:
  401. Requirements spec
  402. Requirements spec custom fields values
  403. Requirements ( Requirements spec children )
  404. Requirements custom fields values
  405. IMPORTANT/CRITIC:
  406. This function can used to delete a Req Specification that contains ONLY Requirements.
  407. This function is needed by tree class method: delete_subtree_objects()
  408. To delete a Req Specification that contains other Req Specification delete_deep() must be used.
  409. args: id: requirement spec id
  410. returns: message string
  411. ok if everything is ok
  412. */
  413. function delete($id)
  414. {
  415. $req_mgr = new requirement_mgr($this->db);
  416. // Delete Custom fields
  417. $this->cfield_mgr->remove_all_design_values_from_node($id);
  418. $result = $this->attachmentRepository->deleteAttachmentsFor($id,"req_specs");
  419. // delete requirements (one type req spec children) with all related data
  420. // coverage, attachments, custom fields, etc
  421. $requirements_info = $this->get_requirements($id);
  422. if(!is_null($requirements_info))
  423. {
  424. $items = null;
  425. foreach($requirements_info as $req)
  426. {
  427. $items[] = $req["id"];
  428. }
  429. $req_mgr->delete($items);
  430. }
  431. // delete specification itself
  432. $sql = "DELETE FROM {$this->object_table} WHERE id = {$id}";
  433. $result = $this->db->exec_query($sql);
  434. $sql = "DELETE FROM {$this->tables['nodes_hierarchy']} WHERE id = {$id}";
  435. $result = $this->db->exec_query($sql);
  436. if($result)
  437. {
  438. $result = 'ok';
  439. }
  440. else
  441. {
  442. $result = 'The DELETE SRS request fails.';
  443. }
  444. return $result;
  445. } // function end
  446. /**
  447. * delete_deep()
  448. *
  449. * Delete Req Specification, removing all children (other Req. Spec and Requirements)
  450. */
  451. function delete_deep($id)
  452. {
  453. // BUGID 3147 - Delete test project with requirements defined crashed with memory exhausted
  454. $this->tree_mgr->delete_subtree_objects($id,$id,'',array('requirement' => 'exclude_my_children'));
  455. $this->delete($id);
  456. }
  457. /*
  458. function: get_requirements
  459. get LATEST VERSION OF requirements contained in a req spec
  460. args: id: req spec id
  461. [range]: default 'all'
  462. [testcase_id]: default null
  463. if !is_null, is used as filter
  464. [order_by]
  465. returns: array of rows
  466. rev: 20080830 - franciscom - changed to get node_order from nodes hierarchy table
  467. */
  468. // function get_requirements($id, $range = 'all', $testcase_id = null,
  469. // $order_by=" ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id")
  470. //
  471. function get_requirements($id, $range = 'all', $testcase_id = null, $options=null, $filters = null)
  472. {
  473. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  474. $req_mgr = new requirement_mgr($this->db);
  475. $my['options'] = array( 'order_by' => " ORDER BY NH_REQ.node_order,NH_REQ.name,REQ.req_doc_id",
  476. 'output' => 'standard');
  477. $my['options'] = array_merge($my['options'], (array)$options);
  478. // null => do not filter
  479. $my['filters'] = array('status' => null, 'type' => null);
  480. $my['filters'] = array_merge($my['filters'], (array)$filters);
  481. switch($my['options']['output'])
  482. {
  483. case 'count':
  484. $rs = 0;
  485. break;
  486. case 'standard':
  487. default;
  488. $rs = null;
  489. break;
  490. }
  491. $sql = '';
  492. $tcase_filter = '';
  493. // First Step - get only req info
  494. $sql = "/* $debugMsg */ SELECT NH_REQ.id FROM {$this->tables['nodes_hierarchy']} NH_REQ ";
  495. switch($range)
  496. {
  497. case 'all';
  498. break;
  499. case 'assigned':
  500. $sql .= " JOIN {$this->tables['req_coverage']} REQ_COV ON REQ_COV.req_id=NH_REQ.id ";
  501. if(!is_null($testcase_id))
  502. {
  503. $tcase_filter = " AND REQ_COV.testcase_id={$testcase_id}";
  504. }
  505. break;
  506. }
  507. $sql .= " WHERE NH_REQ.parent_id={$id} " .
  508. " AND NH_REQ.node_type_id = {$this->node_types_descr_id['requirement']} {$tcase_filter}";
  509. $itemSet = $this->db->fetchRowsIntoMap($sql,'id');
  510. if( !is_null($itemSet) )
  511. {
  512. $reqSet = array_keys($itemSet);
  513. $sql = "/* $debugMsg */ SELECT MAX(NH_REQV.id) AS version_id" .
  514. " FROM {$this->tables['nodes_hierarchy']} NH_REQV " .
  515. " WHERE NH_REQV.parent_id IN (" . implode(",",$reqSet) . ") " .
  516. " GROUP BY NH_REQV.parent_id ";
  517. $latestVersionSet = $this->db->fetchRowsIntoMap($sql,'version_id');
  518. $reqVersionSet = array_keys($latestVersionSet);
  519. $getOptions = null;
  520. if( !is_null($my['options']['order_by']) )
  521. {
  522. $getOptions = array('order_by' => $my['options']['order_by']);
  523. }
  524. $rs = $req_mgr->get_by_id($reqSet,$reqVersionSet,null,$getOptions,$my['filters']);
  525. switch($my['options']['output'])
  526. {
  527. case 'standard':
  528. break;
  529. case 'count':
  530. $rs = !is_null($rs) ? count($rs) : 0;
  531. break;
  532. }
  533. }
  534. return $rs;
  535. }
  536. /*
  537. function: get_by_title
  538. get req spec information using title as access key.
  539. args : title: req spec title
  540. [tproject_id]
  541. [parent_id]
  542. [case_analysis]: control case sensitive search.
  543. default 0 -> case sensivite search
  544. returns: map.
  545. key: req spec id
  546. value: srs info, map with folowing keys:
  547. id
  548. testproject_id
  549. doc_id
  550. title
  551. scope
  552. total_req
  553. type
  554. author_id
  555. creation_ts
  556. modifier_id
  557. modification_ts
  558. */
  559. function get_by_title($title,$tproject_id=null,$parent_id=null,$case_analysis=self::CASE_SENSITIVE)
  560. {
  561. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  562. $output=null;
  563. $title=trim($title);
  564. $the_title=$this->db->prepare_string($title);
  565. $sql = "/* $debugMsg */ " .
  566. " SELECT RSPEC.id,testproject_id,RSPEC,doc_id,RSPEC.scope,RSPEC.total_req,RSPEC.type," .
  567. " RSPEC.author_id,RSPEC.creation_ts,RSPEC.modifier_id," .
  568. " RSPEC.modification_ts,NH.name AS title " .
  569. " FROM {$this->object_table} RSPEC, {$this->tables['nodes_hierarchy']} NH";
  570. switch ($case_analysis)
  571. {
  572. case self::CASE_SENSITIVE:
  573. $sql .= " WHERE NH.name='{$the_title}'";
  574. break;
  575. case self::CASE_INSENSITIVE:
  576. $sql .= " WHERE UPPER(NH.name)='" . strtoupper($the_title) . "'";
  577. break;
  578. }
  579. $sql .= " AND RSPEC.id=NH.id ";
  580. if( !is_null($tproject_id) )
  581. {
  582. $sql .= " AND RSPEC.testproject_id={$tproject_id}";
  583. }
  584. if( !is_null($parent_id) )
  585. {
  586. $sql .= " AND NH.parent_id={$parent_id}";
  587. }
  588. $sql .= " AND RSPEC.id=NH.id ";
  589. $output = $this->db->fetchRowsIntoMap($sql,'id');
  590. return $output;
  591. }
  592. /*
  593. function: check_title
  594. Do checks on req spec title, to understand if can be used.
  595. Checks:
  596. 1. title is empty ?
  597. 2. does already exist a req spec with this title?
  598. args : title: req spec title
  599. [parent_id]: default null -> do check for tile uniqueness system wide.
  600. valid id: only inside parent_id with this id.
  601. [id]: req spec id.
  602. [case_analysis]: control case sensitive search.
  603. default 0 -> case sensivite search
  604. returns:
  605. */
  606. function check_title($title,$tproject_id=null,$parent_id=null,$id=null,
  607. $case_analysis=self::CASE_SENSITIVE)
  608. {
  609. $ret['status_ok'] = 1;
  610. $ret['msg'] = '';
  611. $title = trim($title);
  612. if ($title == "")
  613. {
  614. $ret['status_ok'] = 0;
  615. $ret['msg'] = lang_get("warning_empty_req_title");
  616. }
  617. if($ret['status_ok'])
  618. {
  619. $ret['msg']='ok';
  620. $rs = $this->get_by_title($title,$tproject_id,$parent_id,$case_analysis);
  621. if(!is_null($rs) && (is_null($id) || !isset($rs[$id])))
  622. {
  623. $ret['msg'] = sprintf(lang_get("warning_duplicate_req_title"),$title);
  624. $ret['status_ok'] = 0;
  625. }
  626. }
  627. return $ret;
  628. } //function end
  629. /*
  630. function: check_main_data
  631. Do checks on req spec title and doc id, to understand if can be used.
  632. Checks:
  633. 1. title is empty ?
  634. 2. doc is is empty ?
  635. 3. does already exist a req spec with this title?
  636. 4. does already exist a req spec with this doc id?
  637. VERY IMPORTANT:
  638. $tlCfg->req_cfg->child_requirements_mgmt has effects on check on already
  639. existent title or doc id.
  640. $tlCfg->req_cfg->child_requirements_mgmt == ENABLED => N level tree
  641. title and doc id can not repited on ANY level of tree
  642. This is important due to unique index present on Database
  643. ATTENTION:
  644. Must be rethinked!!!!
  645. args : title: req spec title
  646. doc_id: req spec document id / code / short title
  647. [parent_id]: default null -> do check for tile uniqueness system wide.
  648. valid id: only inside parent_id with this id.
  649. [id]: req spec id.
  650. [case_analysis]: control case sensitive search.
  651. default 0 -> case sensivite search
  652. returns:
  653. */
  654. function check_main_data($title,$doc_id,$tproject_id=null,$parent_id=null,$id=null,
  655. $case_analysis=self::CASE_SENSITIVE)
  656. {
  657. $cfg = config_get('req_cfg');
  658. // 20091223 - this has to be removed if we remove unique index
  659. // $my_parent_id = $cfg->child_requirements_mgmt == ENABLED ? null : $parent_id;
  660. $my_parent_id = $parent_id;
  661. $ret['status_ok'] = 1;
  662. $ret['msg'] = '';
  663. $title = trim($title);
  664. $doc_id = trim($doc_id);
  665. if ($title == "")
  666. {
  667. $ret['status_ok'] = 0;
  668. $ret['msg'] = lang_get("warning_empty_req_title");
  669. }
  670. if ($doc_id == "")
  671. {
  672. $ret['status_ok'] = 0;
  673. $ret['msg'] = lang_get("warning_empty_doc_id");
  674. }
  675. // 20091223 - franciscom -
  676. // Now that req spec has doc id, IMHO this check has not to be done
  677. // or must be improved
  678. //
  679. // if($ret['status_ok'])
  680. // {
  681. // $ret['msg']='ok';
  682. // $rs = $this->get_by_title($title,$tproject_id,$my_parent_id,$case_analysis);
  683. // if(!is_null($rs) && (is_null($id) || !isset($rs[$id])))
  684. // {
  685. // $info = current($rs);
  686. // $ret['msg'] = sprintf(lang_get("warning_duplicated_req_spec_title"),$info['doc_id'],$title);
  687. // $ret['status_ok'] = 0;
  688. // }
  689. // }
  690. if($ret['status_ok'])
  691. {
  692. $ret['msg']='ok';
  693. $rs = $this->getByDocID($doc_id,$tproject_id);
  694. if(!is_null($rs) && (is_null($id) || !isset($rs[$id])))
  695. {
  696. $info = current($rs);
  697. $ret['msg'] = sprintf(lang_get("warning_duplicated_req_spec_doc_id"),$info['title'],$doc_id);
  698. $ret['status_ok'] = 0;
  699. }
  700. }
  701. return $ret;
  702. } //function end
  703. /*
  704. function:
  705. args :
  706. $nodes: array with req_spec in order
  707. returns:
  708. */
  709. function set_order($map_id_order)
  710. {
  711. $this->tree_mgr->change_order_bulk($map_id_order);
  712. } // set_order($map_id_order)
  713. /*
  714. function:
  715. args:
  716. returns:
  717. */
  718. function get_requirements_count($id, $range = 'all', $testcase_id = null,$filters=null)
  719. {
  720. // filters => array('status' => NON_TESTABLE_REQ, 'type' => 'X');
  721. $options = array('output' => 'count');
  722. $count = $this->get_requirements($id,$range,$testcase_id,$options,$filters);
  723. return $count;
  724. }
  725. /**
  726. * getReqTree
  727. *
  728. * Example of returned value ( is a recursive one )
  729. * (
  730. * [childNodes] => Array
  731. * ([0] => Array
  732. * ( [id] => 216
  733. * [parent_id] => 179
  734. * [node_type_id] => 6
  735. * [node_order] => 0
  736. * [node_table] => req_specs
  737. * [name] => SUB-R
  738. * [childNodes] => Array
  739. * ([0] => Array
  740. * ( [id] => 181
  741. * [parent_id] => 216
  742. * [node_type_id] => 7
  743. * [node_order] => 0
  744. * [node_table] => requirements
  745. * [name] => Gamma Ray Emissions
  746. * [childNodes] =>
  747. * )
  748. * [1] => Array
  749. * ( [id] => 182
  750. * [parent_id] => 216
  751. * [node_type_id] => 7
  752. * [node_order] => 0
  753. * [node_table] => requirements
  754. * [name] => Coriolis Effet
  755. * [childNodes] =>
  756. * )
  757. * )
  758. * )
  759. * [1] => Array
  760. * ( [id] => 217
  761. * [parent_id] => 179
  762. * [node_type_id] => 6
  763. * [node_order] => 0
  764. * [node_table] => req_specs
  765. * [name] => SUB-R2
  766. * [childNodes] => Array
  767. * ...
  768. *
  769. *
  770. */
  771. function getReqTree($id)
  772. {
  773. $filters=null;
  774. $options=array('recursive' => true);
  775. $map = $this->tree_mgr->get_subtree($id,$filters,$options);
  776. return $map;
  777. }
  778. /**
  779. * exportReqSpecToXML
  780. * create XML string with following req spec data
  781. * - basic data (title, scope)
  782. * - custom fields values
  783. * - children: can be other req spec or requirements (tree leaves)
  784. *
  785. * Developed using exportTestSuiteDataToXML() as model
  786. *
  787. * @internal revision
  788. * 20100320 - franciscom - added TYPE
  789. * 20091122 - franciscom - added doc id management
  790. */
  791. function exportReqSpecToXML($id,$tproject_id,$optExport=array())
  792. {
  793. static $req_mgr;
  794. // manage missing keys
  795. $optionsForExport=array('RECURSIVE' => true);
  796. foreach($optionsForExport as $key => $value)
  797. {
  798. $optionsForExport[$key]=isset($optExport[$key]) ? $optExport[$key] : $value;
  799. }
  800. $cfXML=null;
  801. $xmlData = null;
  802. if($optionsForExport['RECURSIVE'])
  803. {
  804. $cfXML = $this->customFieldValuesAsXML($id,$tproject_id);
  805. $containerData = $this->get_by_id($id);
  806. $xmlData = "<req_spec title=\"" . htmlspecialchars($containerData['title']) . '" ' .
  807. " doc_id=\"" . htmlspecialchars($containerData['doc_id']) . '" >' .
  808. "\n<type><![CDATA[{$containerData['type']}]]></type>\n" .
  809. "\n<node_order><![CDATA[{$containerData['node_order']}]]></node_order>\n" .
  810. "\n<total_req><![CDATA[{$containerData['total_req']}]]></total_req>\n" .
  811. "<scope><![CDATA[{$containerData['scope']}]]> \n</scope>{$cfXML}";
  812. }
  813. // else
  814. // {
  815. // $xmlData = "<requirement>";
  816. // }
  817. $req_spec = $this->getReqTree($id);
  818. $childNodes = isset($req_spec['childNodes']) ? $req_spec['childNodes'] : null ;
  819. if( !is_null($childNodes) )
  820. {
  821. $loop_qty=sizeof($childNodes);
  822. for($idx = 0;$idx < $loop_qty;$idx++)
  823. {
  824. $cNode = $childNodes[$idx];
  825. $nTable = $cNode['node_table'];
  826. if($optionsForExport['RECURSIVE'] && $cNode['node_table'] == 'req_specs')
  827. {
  828. $xmlData .= $this->exportReqSpecToXML($cNode['id'],$tproject_id,$optionsForExport);
  829. }
  830. else if ($cNode['node_table'] == 'requirements')
  831. {
  832. if( is_null($req_mgr) )
  833. {
  834. $req_mgr = new requirement_mgr($this->db);
  835. }
  836. $reqXMLData = $req_mgr->exportReqToXML($cNode['id'],$tproject_id);
  837. $xmlData .= $req_mgr->exportReqToXML($cNode['id'],$tproject_id);
  838. }
  839. }
  840. }
  841. if ($optionsForExport['RECURSIVE'])
  842. {
  843. $xmlData .= "</req_spec>";
  844. }
  845. // else
  846. // {
  847. // $xmlData .= "</requirement>";
  848. // }
  849. return $xmlData;
  850. }
  851. /**
  852. * xmlToReqSpec
  853. *
  854. * @param object $source:
  855. * $source->type: possible values 'string', 'file'
  856. * $source->value: depends of $source->type
  857. * 'string' => xml string
  858. * 'file' => path name of XML file
  859. *
  860. */
  861. function xmlToReqSpec($source)
  862. {
  863. $status_ok=true;
  864. $xml_string=null;
  865. $req_spec=null;
  866. switch( $source->type )
  867. {
  868. case 'string':
  869. $xml_string = $source->value;
  870. break;
  871. case 'file':
  872. $xml_file = $source->value;
  873. $status_ok=!(($xml_object=@simplexml_load_file($xml_file)) === FALSE);
  874. break;
  875. }
  876. if( $status_ok )
  877. {
  878. }
  879. return $req_spec;
  880. }
  881. /**
  882. * xmlToMapReqSpec
  883. *
  884. */
  885. function xmlToMapReqSpec($xml_item,$level=0)
  886. {
  887. static $iterations=0;
  888. static $mapped;
  889. static $req_mgr;
  890. // Attention: following PHP Manual SimpleXML documentation, Please remember to cast
  891. // before using data from $xml,
  892. if( is_null($xml_item) )
  893. {
  894. return null;
  895. }
  896. // used to reset static structures if calling this in loop
  897. if($level == 0)
  898. {
  899. $iterations = 0;
  900. $mapped = null;
  901. }
  902. $dummy=array();
  903. $dummy['node_order'] = (int)$xml_item->node_order;
  904. $dummy['scope'] = (string)$xml_item->scope;
  905. $dummy['type'] = (int)$xml_item->type;
  906. $dummy['total_req'] = (int)$xml_item->total_req;
  907. $dummy['level'] = $level;
  908. $depth=$level+1;
  909. foreach($xml_item->attributes() as $key => $value)
  910. {
  911. $dummy[$key] = (string)$value; // See PHP Manual SimpleXML documentation.
  912. }
  913. if( property_exists($xml_item,'custom_fields') )
  914. {
  915. $dummy['custom_fields']=array();
  916. foreach($xml_item->custom_fields->children() as $key)
  917. {
  918. $dummy['custom_fields'][(string)$key->name]= (string)$key->value;
  919. }
  920. }
  921. $mapped[]=array('req_spec' => $dummy, 'requirements' => null,
  922. 'level' => $dummy['level']);
  923. // Process children
  924. if( property_exists($xml_item,'requirement') )
  925. {
  926. if( is_null($req_mgr) )
  927. {
  928. $req_mgr = new requirement_mgr($this->db);
  929. }
  930. $loop2do=count($xml_item->requirement);
  931. for($idx=0; $idx <= $loop2do; $idx++)
  932. {
  933. $xml_req=$req_mgr->xmlToMapRequirement($xml_item->requirement[$idx]);
  934. if(!is_null($xml_req))
  935. {
  936. $fdx=count($mapped)-1;
  937. $mapped[$fdx]['requirements'][]=$xml_req;
  938. }
  939. }
  940. }
  941. if( property_exists($xml_item,'req_spec') )
  942. {
  943. $loop2do=count($xml_item->req_spec);
  944. for($idx=0; $idx <= $loop2do; $idx++)
  945. {
  946. $this->xmlToMapReqSpec($xml_item->req_spec[$idx],$depth);
  947. }
  948. }
  949. return $mapped;
  950. }
  951. // ---------------------------------------------------------------------------------------
  952. // Custom field related functions
  953. // ---------------------------------------------------------------------------------------
  954. /*
  955. function: get_linked_cfields
  956. Get all linked custom fields.
  957. Remember that custom fields are defined at system wide level, and
  958. has to be linked to a testproject, in order to be used.
  959. args: id: requirement spec id
  960. [tproject_id]: node id of parent testproject of requirement spec.
  961. need to understand to which testproject requirement spec belongs.
  962. this information is vital, to get the linked custom fields.
  963. Presence /absence of this value changes starting point
  964. on procedure to build tree path to get testproject id.
  965. null -> use requirement spec id as starting point.
  966. !is_null -> use this value as starting point.
  967. returns: map/hash
  968. key: custom field id
  969. value: map with custom field definition and value assigned for choosen req spec,
  970. with following keys:
  971. id: custom field id
  972. name
  973. label
  974. type: custom field type
  975. possible_values: for custom field
  976. default_value
  977. valid_regexp
  978. length_min
  979. length_max
  980. show_on_design
  981. enable_on_design
  982. show_on_execution
  983. enable_on_execution
  984. display_order
  985. value: value assigned to custom field for this req spec
  986. null if for this req spec custom field was never edited.
  987. node_id: req spec id
  988. null if for this req spec, custom field was never edited.
  989. rev :
  990. 20070302 - check for $id not null, is not enough, need to check is > 0
  991. */
  992. function get_linked_cfields($id,$tproject_id=null)
  993. {
  994. $enabled = 1;
  995. if (!is_null($id) && $id > 0)
  996. {
  997. $req_spec_info = $this->get_by_id($id);
  998. $tproject_id = $req_spec_info['testproject_id'];
  999. }
  1000. $cf_map = $this->cfield_mgr->get_linked_cfields_at_design($tproject_id,$enabled,null,
  1001. 'requirement_spec',$id);
  1002. return $cf_map;
  1003. }
  1004. /*
  1005. function: html_table_of_custom_field_inputs
  1006. Return html code, implementing a table with custom fields labels
  1007. and html inputs, for choosen req spec.
  1008. Used to manage user actions on custom fields values.
  1009. args: $id
  1010. [tproject_id]: node id of testproject (req spec parent).
  1011. this information is vital, to get the linked custom fields,
  1012. because custom fields are system wide, but to be used are
  1013. assigned to a test project.
  1014. is null this method or other called will use get_path()
  1015. method to get test project id.
  1016. [parent_id]: Need to e rethinked, may be remove (20090111 - franciscom)
  1017. [$name_suffix]: must start with '_' (underscore).
  1018. Used when we display in a page several items
  1019. (example during test case execution, several test cases)
  1020. that have the same custom fields.
  1021. In this kind of situation we can use the item id as name suffix.
  1022. returns: html string
  1023. */
  1024. function html_table_of_custom_field_inputs($id,$tproject_id=null,$parent_id=null,$name_suffix='')
  1025. {
  1026. $NO_WARNING_IF_MISSING=true;
  1027. $cf_smarty = '';
  1028. $cf_map = $this->get_linked_cfields($id,$tproject_id);
  1029. if(!is_null($cf_map))
  1030. {
  1031. $cf_smarty = "<table>";
  1032. foreach($cf_map as $cf_id => $cf_info)
  1033. {
  1034. $label=str_replace(TL_LOCALIZE_TAG,'',
  1035. lang_get($cf_info['label'],null,$NO_WARNING_IF_MISSING));
  1036. $cf_smarty .= '<tr><td class="labelHolder">' . htmlspecialchars($label) . ":</td><td>" .
  1037. $this->cfield_mgr->string_custom_field_input($cf_info,$name_suffix) .
  1038. "</td></tr>\n";
  1039. }
  1040. $cf_smarty .= "</table>";
  1041. }
  1042. return $cf_smarty;
  1043. }
  1044. /*
  1045. function: html_table_of_custom_field_values
  1046. Return html code, implementing a table with custom fields labels
  1047. and custom fields values, for choosen req spec.
  1048. You can think of this function as some sort of read only version
  1049. of html_table_of_custom_field_inputs.
  1050. args: $id
  1051. returns: html string
  1052. */
  1053. function html_table_of_custom_field_values($id,$tproject_id)
  1054. {
  1055. $NO_WARNING_IF_MISSING=true;
  1056. $cf_smarty = '';
  1057. $cf_map = $this->get_linked_cfields($id,$tproject_id);
  1058. if(!is_null($cf_map))
  1059. {
  1060. foreach($cf_map as $cf_id => $cf_info)
  1061. {
  1062. // if user has assigned a value, then node_id is not null
  1063. if($cf_info['node_id'])
  1064. {
  1065. $label = str_replace(TL_LOCALIZE_TAG,'',
  1066. lang_get($cf_info['label'],null,$NO_WARNING_IF_MISSING));
  1067. $cf_smarty .= '<tr><td class="labelHolder">' .
  1068. htmlspecialchars($label) . ":</td><td>" .
  1069. $this->cfield_mgr->string_custom_field_value($cf_info,$id) .
  1070. "</td></tr>\n";
  1071. }
  1072. }
  1073. if(trim($cf_smarty) != "")
  1074. {
  1075. $cf_smarty = "<table>" . $cf_smarty . "</table>";
  1076. }
  1077. }
  1078. return $cf_smarty;
  1079. } // function end
  1080. /*
  1081. function: values_to_db
  1082. write values of custom fields to db
  1083. args: hash:
  1084. key: custom_field_<field_type_id>_<cfield_id>.
  1085. Example custom_field_0_67 -> 0=> string field
  1086. node_id: req spec id
  1087. [cf_map]: hash -> all the custom fields linked and enabled
  1088. that are applicable to the node type of $node_id.
  1089. For the keys not present in $hash, we will write
  1090. an appropriate value according to custom field
  1091. type.
  1092. This is needed because when trying to udpate
  1093. with hash being $_REQUEST, $_POST or $_GET
  1094. some kind of custom fields (checkbox, list, multiple list)
  1095. when has been deselected by user.
  1096. [hash_type]
  1097. rev:
  1098. */
  1099. function values_to_db($hash,$node_id,$cf_map=null,$hash_type=null)
  1100. {
  1101. $this->cfield_mgr->design_values_to_db($hash,$node_id,$cf_map,$hash_type);
  1102. }
  1103. /**
  1104. * customFieldValuesAsXML
  1105. *
  1106. * @param $id: requirement spec id
  1107. * @param $tproject_id: test project id
  1108. *
  1109. *
  1110. */
  1111. function customFieldValuesAsXML($id,$tproject_id)
  1112. {
  1113. $xml = null;
  1114. $cfMap=$this->get_linked_cfields($id,$tproject_id);
  1115. if( !is_null($cfMap) && count($cfMap) > 0 )
  1116. {
  1117. $xml = $this->cfield_mgr->exportValueAsXML($cfMap);
  1118. }
  1119. return $xml;
  1120. }
  1121. /**
  1122. * create a req spec tree on system from $xml data
  1123. *
  1124. *
  1125. *
  1126. */
  1127. function createFromXML($xml,$tproject_id,$parent_id,$author_id,$filters = null,$options=null)
  1128. {
  1129. $user_feedback = null;
  1130. $items = $this->xmlToMapReqSpec($xml);
  1131. $req_mgr = new requirement_mgr($this->db);
  1132. $copy_reqspec = null;
  1133. $copy_req = null;
  1134. $getOptions = array('output' => 'minimun');
  1135. $has_filters = !is_null($filters);
  1136. $my['options'] = array( 'actionOnDuplicate' => "update");
  1137. $my['options'] = array_merge($my['options'], (array)$options);
  1138. $labels = array('import_req_spec_created' => '', 'import_req_spec_skipped' => '',
  1139. 'import_req_spec_updated' => '', 'import_req_spec_ancestor_skipped' => '',
  1140. 'import_req_created' => '','import_req_skipped' =>'', 'import_req_updated' => '');
  1141. foreach($labels as $key => $dummy)
  1142. {
  1143. $labels[$key] = lang_get($key);
  1144. }
  1145. if($has_filters)
  1146. {
  1147. if(!is_null($filters['requirements']))
  1148. {
  1149. foreach($filters['requirements'] as $reqspec_pos => $requirements_pos)
  1150. {
  1151. $copy_req[$reqspec_pos] = is_null($requirements_pos) ? null : array_keys($requirements_pos);
  1152. }
  1153. }
  1154. }
  1155. $loop2do = count($items);
  1156. $container_id[0] = (is_null($parent_id) || $parent_id == 0) ? $tproject_id : $parent_id;
  1157. // items is an array of req. specs
  1158. $skip_level = -1;
  1159. for($idx = 0;$idx < $loop2do; $idx++)
  1160. {
  1161. $rspec = $items[$idx]['req_spec'];
  1162. $depth = $rspec['level'];
  1163. if( $skip_level > 0 && $depth >= $skip_level)
  1164. {
  1165. $msgID = 'import_req_spec_ancestor_skipped';
  1166. $user_feedback[] = array('doc_id' => $rspec['doc_id'],'title' => $rspec['title'],
  1167. 'import_status' => sprintf($labels[$msgID],$rspec['doc_id']));
  1168. continue;
  1169. }
  1170. $req_spec_order = isset($rspec['node_order']) ? $rspec['node_order'] : 0;
  1171. // 20100320 -
  1172. // Check if req spec with same DOCID exists, inside container_id
  1173. // If there is a hit
  1174. // We will go in update
  1175. // If Check fails, need to repeat check on WHOLE Testproject.
  1176. // If now there is a HIT we can not import this branch
  1177. // If Check fails => we can import creating a new one.
  1178. //
  1179. // Important thing:
  1180. // Working in this way, i.e. doing check while walking the structure to import
  1181. // we can end importing struct with 'holes'.
  1182. //
  1183. $check_in_container = $this->getByDocID($rspec['doc_id'],$tproject_id,$container_id[$depth],$getOptions);
  1184. $skip_level = $depth + 1;
  1185. $result['status_ok'] = 0;
  1186. $msgID = 'import_req_spec_skipped';
  1187. if(is_null($check_in_container))
  1188. {
  1189. $check_in_tproject = $this->getByDocID($rspec['doc_id'],$tproject_id,null,$getOptions);
  1190. if(is_null($check_in_tproject))
  1191. {
  1192. $msgID = 'import_req_spec_created';
  1193. $result = $this->create($tproject_id,$container_id[$depth],$rspec['doc_id'],$rspec['title'],
  1194. $rspec['scope'],$rspec['total_req'],$author_id,$rspec['type'],$req_spec_order);
  1195. }
  1196. }
  1197. else
  1198. {
  1199. $msgID = 'import_req_spec_updated';
  1200. $reqSpecID = key($check_in_container);
  1201. $result = $this->update($reqSpecID,$rspec['doc_id'],$rspec['title'],$rspec['scope'],
  1202. $rspec['total_req'],$author_id,$rspec['type'],$req_spec_order);
  1203. $result['id'] = $reqSpecID;
  1204. }
  1205. $user_feedback[] = array('doc_id' => $rspec['doc_id'],'title' => $rspec['title'],
  1206. 'import_status' => sprintf($labels[$msgID],$rspec['doc_id']));
  1207. if($result['status_ok'])
  1208. {
  1209. $skip_level = -1;
  1210. $container_id[$depth+1] = ($reqSpecID = $result['id']);
  1211. $reqSet = $items[$idx]['requirements'];
  1212. $create_req = (!$has_filters || isset($copy_req[$idx])) && !is_null($reqSet);
  1213. if($create_req)
  1214. {
  1215. $items_qty = isset($copy_req[$idx]) ? count($copy_req[$idx]) : count($reqSet);
  1216. $keys2insert = isset($copy_req[$idx]) ? $copy_req[$idx] : array_keys($reqSet);
  1217. for($jdx = 0;$jdx < $items_qty; $jdx++)
  1218. {
  1219. $req = $reqSet[$keys2insert[$jdx]];
  1220. // Check:
  1221. // If item with SAME DOCID exists inside container
  1222. // If there is a hit
  1223. // We will follow user option: update,create new version
  1224. //
  1225. // If do not exist check must be repeated, but on WHOLE test project
  1226. // If there is a hit -> we can not create
  1227. // else => create
  1228. //
  1229. //
  1230. // 20100321 - we do not manage yet user option
  1231. $msgID = 'import_req_skipped';
  1232. $check_in_reqspec = $req_mgr->getByDocID($req['docid'],$tproject_id,$reqSpecID,$getOptions);
  1233. if(is_null($check_in_reqspec))
  1234. {
  1235. $check_in_tproject = $req_mgr->getByDocID($req['docid'],$tproject_id,null,$getOptions);
  1236. if(is_null($check_in_tproject))
  1237. {
  1238. $req_mgr->create($result['id'],$req['docid'],$req['title'],$req['description'],
  1239. $author_id,$req['status'],$req['type'],$req['expected_coverage'],
  1240. $req['node_order']);
  1241. $msgID = 'import_req_created';
  1242. }
  1243. }
  1244. else
  1245. {
  1246. // function update($id,$version_id,$reqdoc_id,$title, $scope, $user_id, $status, $type,
  1247. // $expected_coverage,$node_order=null,$skip_controls=0)
  1248. //
  1249. // Need to get Last Version no matter active or not.
  1250. // What to do if is Frozen ??? -> now ignore and update anyway
  1251. $reqID = key($check_in_reqspec);
  1252. $last_version = $req_mgr->get_last_version_info($reqID);
  1253. $result = $req_mgr->update($reqID,$last_version['id'],$req['docid'],$req['title'],$req['description'],
  1254. $author_id,$req['status'],$req['type'],$req['expected_coverage'],
  1255. $req['node_order']);
  1256. $msgID = 'import_req_updated';
  1257. }
  1258. }
  1259. $user_feedback[] = array('doc_id' => $req['docid'],'title' => $req['title'],
  1260. 'import_status' => sprintf($labels[$msgID],$req['docid']));
  1261. } // if($create_req)
  1262. } // if($result['status_ok'])
  1263. }
  1264. return $user_feedback;
  1265. }
  1266. /*
  1267. function: getByDocID
  1268. get req spec information using document ID as access key.
  1269. args : doc_id:
  1270. [tproject_id]
  1271. [parent_id]
  1272. [options]:
  1273. [case]: control case sensitive search.
  1274. default 0 -> case sensivite search
  1275. [access_key]:
  1276. [check_criteria]:
  1277. [output]:
  1278. returns: map.
  1279. key: req spec id
  1280. value: srs info, map with folowing keys:
  1281. id
  1282. testproject_id
  1283. title
  1284. scope
  1285. total_req
  1286. type
  1287. author_id
  1288. creation_ts
  1289. modifier_id
  1290. modification_ts
  1291. */
  1292. function getByDocID($doc_id,$tproject_id=null,$parent_id=null,$options=null)
  1293. {
  1294. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  1295. $my['options'] = array( 'check_criteria' => '=', 'access_key' => 'id',
  1296. 'case' => 'sensitive', 'output' => 'standard');
  1297. $my['options'] = array_merge($my['options'], (array)$options);
  1298. $output=null;
  1299. $the_doc_id=$this->db->prepare_string(trim($doc_id));
  1300. switch($my['options']['check_criteria'])
  1301. {
  1302. case '=':
  1303. default:
  1304. $check_criteria = " = '{$the_doc_id}' ";
  1305. break;
  1306. case 'like':
  1307. $check_criteria = " LIKE '{$the_doc_id}%' ";
  1308. break;
  1309. }
  1310. $sql = " /* $debugMsg */ SELECT ";
  1311. switch($my['options']['output'])
  1312. {
  1313. case 'standard':
  1314. $sql .=

Large files files are truncated, but you can click here to view the full file