PageRenderTime 34ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/functions/testproject.class.php

https://bitbucket.org/pfernandez/testlink1.9.6
PHP | 2431 lines | 1289 code | 289 blank | 853 comment | 137 complexity | 8b6769843e84334262f8030161bfb8b3 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, GPL-3.0
  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. * @package TestLink
  7. * @author franciscom
  8. * @copyright 2005-2009, TestLink community
  9. * @version CVS: $Id: testproject.class.php,v 1.171 2010/08/30 16:06:28 franciscom Exp $
  10. * @link http://www.teamst.org/index.php
  11. *
  12. * @internal Revisions:
  13. * 20100821 - franciscom - get_all_testplans() - interface changes
  14. * 20100516 - franciscom - BUGID 3464 - delete()
  15. * 20100310 - asimon - BUGID 3227 - refactored get_all_requirement_ids() and count_all_requirements()
  16. * to not be recursive and pascal-like anymore
  17. * and to use new method on tree class
  18. * 20100309 - asimon - BUGID 3227 - added get_all_requirement_ids() and count_all_requirements()
  19. * 20100209 - franciscom - BUGID 3147 - Delete test project with requirements defined crashed with memory exhausted
  20. * 20100203 - franciscom - addKeyword() return type changed
  21. * 20100201 - franciscom - delete() - missing delete of platforms
  22. * 20100102 - franciscom - show() - interface changes
  23. * 20091206 - franciscom - fixed bug on get_subtree() created furing refactoring
  24. * 20090606 - franciscom - get_by_prefix() interface changes
  25. * 20090512 - franciscom - added setPublicStatus()
  26. * 20090412 - franciscom - BUGID 2363 - getTCasesLinkedToAnyTPlan()
  27. * getFreeTestCases()
  28. *
  29. * 20090205 - franciscom - getReqSpec() - interface additions
  30. * 20090125 - franciscom - added utility method _createHierarchyMap()
  31. * 20090106 - franciscom - get_by_prefix()
  32. * 20081103 - franciscom - get_all_testcases_id() minor refactoring
  33. * 20080518 - franciscom - create() interface changes
  34. * 20080507 - franciscom - get_keywords_tcases() - changed return type
  35. * add AND type filter
  36. * 20080501 - franciscom - typo erro bug in get_keywords_tcases()
  37. * 20080322 - franciscom - get_keywords_tcases() - keyword_id can be array
  38. * 20080322 - franciscom - interface changes get_all_testplans()
  39. * 20080112 - franciscom - changed methods to manage prefix field
  40. * new methods getTestCasePrefix()
  41. *
  42. * 20080107 - franciscom - get_accessible_for_user(), added more data
  43. * for array_of_map output type
  44. * 20080106 - franciscom - checkName() method
  45. * delete() changed return type
  46. * 20080104 - franciscom - fixed bug on gen_combo_test_suites()
  47. * due to wrong exclusion in get_subtree().
  48. * 20071111 - franciscom - new method get_subtree();
  49. * 20071106 - franciscom - createReqSpec() - changed return type
  50. * 20071104 - franciscom - get_accessible_for_user
  51. * added optional arg to get_all()
  52. *
  53. * 20071002 - azl - added ORDER BY to get_all method
  54. * 20070620 - franciscom - BUGID 914 fixed delete() (no delete from nodes_hierarchy)
  55. * 20070603 - franciscom - added delete()
  56. * 20070219 - franciscom - fixed bug on get_first_level_test_suites()
  57. * 20070128 - franciscom - added check_tplan_name_existence()
  58. *
  59. **/
  60. /** related functions */
  61. require_once('attachments.inc.php');
  62. /**
  63. * class is responsible to get project related data and CRUD test project
  64. * @package TestLink
  65. */
  66. class testproject extends tlObjectWithAttachments
  67. {
  68. const RECURSIVE_MODE = true;
  69. const EXCLUDE_TESTCASES = true;
  70. const INCLUDE_TESTCASES = false;
  71. const TESTCASE_PREFIX_MAXLEN = 16; // must be changed if field dimension changes
  72. const GET_NOT_EMPTY_REQSPEC = 1;
  73. const GET_EMPTY_REQSPEC = 0;
  74. /** @var database handler */
  75. var $db;
  76. var $tree_manager;
  77. var $cfield_mgr;
  78. // Node Types (NT)
  79. var $nt2exclude=array('testplan' => 'exclude_me',
  80. 'requirement_spec'=> 'exclude_me',
  81. 'requirement'=> 'exclude_me');
  82. var $nt2exclude_children=array('testcase' => 'exclude_my_children',
  83. 'requirement_spec'=> 'exclude_my_children');
  84. /**
  85. * Class constructor
  86. *
  87. * @param resource &$db reference to database handler
  88. */
  89. function __construct(&$db)
  90. {
  91. $this->db = &$db;
  92. $this->tree_manager = new tree($this->db);
  93. $this->cfield_mgr=new cfield_mgr($this->db);
  94. tlObjectWithAttachments::__construct($this->db,'nodes_hierarchy');
  95. $this->object_table=$this->tables['testprojects'];
  96. }
  97. /**
  98. * Create a new Test project
  99. *
  100. * @param string $name Name of project
  101. * @param string $color value according to CSS color definition
  102. * @param string $notes project description (HTML text)
  103. * @param array $options project features/options
  104. * bolean keys: inventoryEnabled, automationEnabled,
  105. * testPriorityEnabled, requirementsEnabled
  106. * @param boolean $active [1,0] optional
  107. * @param string $tcasePrefix ['']
  108. * @param boolean $is_public [1,0] optional
  109. *
  110. * @return integer test project id or 0 (if fails)
  111. *
  112. * @internal Revisions:
  113. * 20100213 - havlatm - support for options serialization, renamed
  114. * 20060709 - franciscom - return type changed
  115. * added new optional argument active
  116. * 20080112 - franciscom - added $tcasePrefix
  117. * 20090601 - havlatm - update session if required
  118. *
  119. * @TODO havlatm: rollback node if create fails (DB consistency req.)
  120. * @TODO havlatm: $is_public parameter need review (do not agreed in team)
  121. * @TODO havlatm: described return parameter differs from reality
  122. * @TODO havlatm: parameter $options should be
  123. */
  124. function create($name,$color,$options,$notes,$active=1,$tcasePrefix='',$is_public=1)
  125. {
  126. // Create Node and get the id
  127. $root_node_id = $this->tree_manager->new_root_node($name);
  128. $tcprefix = $this->formatTcPrefix($tcasePrefix);
  129. $serOptions = serialize($options);
  130. $sql = " INSERT INTO {$this->object_table} (id,color," .
  131. " options,notes,active,is_public,prefix) " .
  132. " VALUES (" . $root_node_id . ", '" .
  133. $this->db->prepare_string($color) . "','" .
  134. $serOptions . "','" .
  135. $this->db->prepare_string($notes) . "'," .
  136. $active . "," . $is_public . ",'" .
  137. $this->db->prepare_string($tcprefix) . "')";
  138. $result = $this->db->exec_query($sql);
  139. if ($result)
  140. {
  141. tLog('The new testproject '.$name.' was succesfully created.', 'INFO');
  142. // set project to session if not defined (the first project) or update the current
  143. if (!isset($_SESSION['testprojectID']))
  144. {
  145. $this->setSessionProject($root_node_id);
  146. }
  147. }
  148. else
  149. {
  150. tLog('The new testproject '.$name.' was not created.', 'INFO');
  151. $root_node_id = 0;
  152. }
  153. return($root_node_id);
  154. }
  155. /**
  156. * Update Test project data in DB and (if applicable) current session data
  157. *
  158. * @param integer $id project Identifier
  159. * @param string $name Name of project
  160. * @param string $color value according to CSS color definition
  161. * @param string $notes project description (HTML text)
  162. * @param array $options project features/options
  163. * bolean keys: inventoryEnabled, automationEnabled,
  164. * testPriorityEnabled, requirementsEnabled
  165. *
  166. * @return boolean result of DB update
  167. *
  168. * @internal
  169. * 20100213 - havlatm - options updated, header description
  170. * 20060312 - franciscom - name is setted on nodes_hierarchy table
  171. *
  172. **/
  173. function update($id, $name, $color, $notes,$options,$active=null,
  174. $tcasePrefix=null,$is_public=null)
  175. {
  176. $status_ok=1;
  177. $status_msg = 'ok';
  178. $log_msg = 'Test project ' . $name . ' update: Ok.';
  179. $log_level = 'INFO';
  180. $add_upd='';
  181. if( !is_null($active) )
  182. {
  183. $add_upd .=',active=' . (intval($active) > 0 ? 1:0);
  184. }
  185. if( !is_null($is_public) )
  186. {
  187. $add_upd .=',is_public=' . (intval($is_public) > 0 ? 1:0);
  188. }
  189. if( !is_null($tcasePrefix) )
  190. {
  191. $tcprefix=$this->formatTcPrefix($tcasePrefix);
  192. $add_upd .=",prefix='" . $this->db->prepare_string($tcprefix) . "'" ;
  193. }
  194. $serOptions = serialize($options);
  195. $sql = " UPDATE {$this->object_table} SET color='" . $this->db->prepare_string($color) . "', ".
  196. " options='" . $serOptions . "', " .
  197. " notes='" . $this->db->prepare_string($notes) . "' {$add_upd} " .
  198. " WHERE id=" . $id;
  199. $result = $this->db->exec_query($sql);
  200. if ($result)
  201. {
  202. // update related node
  203. $sql = "UPDATE {$this->tables['nodes_hierarchy']} SET name='" .
  204. $this->db->prepare_string($name) .
  205. "' WHERE id= {$id}";
  206. $result = $this->db->exec_query($sql);
  207. }
  208. if ($result)
  209. {
  210. // update session data
  211. $this->setSessionProject($id);
  212. }
  213. else
  214. {
  215. $status_msg = 'Update FAILED!';
  216. $status_ok = 0;
  217. $log_level ='ERROR';
  218. $log_msg = $status_msg;
  219. }
  220. tLog($log_msg,$log_level);
  221. return ($status_ok);
  222. }
  223. /**
  224. * Set session data related to a Test project
  225. *
  226. * @param integer $projectId Project ID; zero causes unset data
  227. */
  228. public function setSessionProject($projectId)
  229. {
  230. $tproject_info = null;
  231. if ($projectId)
  232. {
  233. $tproject_info = $this->get_by_id($projectId);
  234. }
  235. if ($tproject_info)
  236. {
  237. $_SESSION['testprojectID'] = $tproject_info['id'];
  238. $_SESSION['testprojectName'] = $tproject_info['name'];
  239. $_SESSION['testprojectColor'] = $tproject_info['color'];
  240. $_SESSION['testprojectPrefix'] = $tproject_info['prefix'];
  241. if(!isset($_SESSION['testprojectOptions']) )
  242. {
  243. $_SESSION['testprojectOptions'] = new stdClass();
  244. }
  245. $_SESSION['testprojectOptions']->requirementsEnabled =
  246. isset($tproject_info['opt']->requirementsEnabled)
  247. ? $tproject_info['opt']->requirementsEnabled : 0;
  248. $_SESSION['testprojectOptions']->testPriorityEnabled =
  249. isset($tproject_info['opt']->testPriorityEnabled)
  250. ? $tproject_info['opt']->testPriorityEnabled : 0;
  251. $_SESSION['testprojectOptions']->automationEnabled =
  252. isset($tproject_info['opt']->automationEnabled)
  253. ? $tproject_info['opt']->automationEnabled : 0;
  254. $_SESSION['testprojectOptions']->inventoryEnabled =
  255. isset($tproject_info['opt']->inventoryEnabled)
  256. ? $tproject_info['opt']->inventoryEnabled : 0;
  257. tLog("Test Project was activated: [" . $tproject_info['id'] . "]" .
  258. $tproject_info['name'], 'INFO');
  259. }
  260. else
  261. {
  262. if (isset($_SESSION['testprojectID']))
  263. {
  264. tLog("Test Project deactivated: [" . $_SESSION['testprojectID'] . "] " .
  265. $_SESSION['testprojectName'], 'INFO');
  266. }
  267. unset($_SESSION['testprojectID']);
  268. unset($_SESSION['testprojectName']);
  269. unset($_SESSION['testprojectColor']);
  270. unset($_SESSION['testprojectOptions']);
  271. unset($_SESSION['testprojectPrefix']);
  272. }
  273. }
  274. /**
  275. * Unserialize project options
  276. *
  277. * @param array $recorset produced by getTestProject()
  278. */
  279. protected function parseTestProjectRecordset(&$recordset)
  280. {
  281. if (count($recordset) > 0)
  282. {
  283. foreach ($recordset as $number => $row)
  284. {
  285. $recordset[$number]['opt'] = unserialize($row['options']);
  286. }
  287. }
  288. else
  289. {
  290. $recordset = null;
  291. tLog('parseTestProjectRecordset: No project on query', 'DEBUG');
  292. }
  293. }
  294. /**
  295. * Get Test project data according to parameter with unique value
  296. *
  297. * @param string $condition (optional) additional SQL condition(s)
  298. * @return array map with test project info; null if query fails
  299. */
  300. protected function getTestProject($condition = null)
  301. {
  302. $sql = " SELECT testprojects.*, nodes_hierarchy.name ".
  303. " FROM {$this->object_table} testprojects, " .
  304. " {$this->tables['nodes_hierarchy']} nodes_hierarchy".
  305. " WHERE testprojects.id = nodes_hierarchy.id ";
  306. if (!is_null($condition) )
  307. {
  308. $sql .= " AND " . $condition;
  309. }
  310. $recordset = $this->db->get_recordset($sql);
  311. $this->parseTestProjectRecordset($recordset);
  312. return $recordset;
  313. }
  314. /**
  315. * Get Test project data according to name
  316. *
  317. * @param string $name
  318. * @param string $addClause (optional) additional SQL condition(s)
  319. *
  320. * @return array map with test project info; null if query fails
  321. */
  322. public function get_by_name($name, $addClause = null)
  323. {
  324. $condition = "nodes_hierarchy.name='" . $this->db->prepare_string($name) . "'";
  325. $condition .= is_null($addClause) ? '' : " AND {$addClause} ";
  326. return $this->getTestProject($condition);
  327. }
  328. /**
  329. * Get Test project data according to ID
  330. *
  331. * @param integer $id test project
  332. * @return array map with test project info; null if query fails
  333. */
  334. public function get_by_id($id)
  335. {
  336. $condition = "testprojects.id=". intval($id);
  337. $result = $this->getTestProject($condition);
  338. return $result[0];
  339. }
  340. /**
  341. * Get Test project data according to prefix
  342. *
  343. * @param string $prefix
  344. * @param string $addClause optional additional SQL 'AND filter' clause
  345. *
  346. * @return array map with test project info; null if query fails
  347. */
  348. public function get_by_prefix($prefix, $addClause = null)
  349. {
  350. $safe_prefix = $this->db->prepare_string($prefix);
  351. $condition = "testprojects.prefix='{$safe_prefix}'";
  352. $condition .= is_null($addClause) ? '' : " AND {$addClause} ";
  353. $result = $this->getTestProject($condition);
  354. return $result[0];
  355. }
  356. /*
  357. function: get_all
  358. get array of info for every test project
  359. without any kind of filter.
  360. Every array element contains an assoc array with test project info
  361. args:[order_by]: default " ORDER BY nodes_hierarchy.name " -> testproject name
  362. rev:
  363. 20090409 - amitkhullar- added active parameter
  364. 20071104 - franciscom - added order_by
  365. */
  366. // function get_all($order_by=" ORDER BY nodes_hierarchy.name ",$active=null )
  367. function get_all($filters=null,$options=null)
  368. {
  369. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  370. $my = array ('filters' => '', 'options' => '');
  371. $my['filters'] = array('active' => null);
  372. $my['options'] = array('order_by' => " ORDER BY nodes_hierarchy.name ", 'access_key' => null);
  373. $my['filters'] = array_merge($my['filters'], (array)$filters);
  374. $my['options'] = array_merge($my['options'], (array)$options);
  375. $sql = "/* $debugMsg */ SELECT testprojects.*, nodes_hierarchy.name ".
  376. " FROM {$this->object_table} testprojects, " .
  377. " {$this->tables['nodes_hierarchy']} nodes_hierarchy ".
  378. " WHERE testprojects.id = nodes_hierarchy.id ";
  379. if (!is_null($my['filters']['active']) )
  380. {
  381. $sql .= " AND active=" . intval($my['filters']['active']) . " ";
  382. }
  383. if( !is_null($my['options']['order_by']) )
  384. {
  385. $sql .= $my['options']['order_by'];
  386. }
  387. if( is_null($my['options']['access_key']))
  388. {
  389. $recordset = $this->db->get_recordset($sql);
  390. $this->parseTestProjectRecordset($recordset);
  391. }
  392. else
  393. {
  394. $recordset = $this->db->fetchRowsIntoMap($sql,$my['options']['access_key']);
  395. if (count($recordset) > 0)
  396. {
  397. foreach ($recordset as $number => $row)
  398. {
  399. $recordset[$number]['opt'] = unserialize($row['options']);
  400. }
  401. }
  402. }
  403. return $recordset;
  404. }
  405. /*
  406. function: get_accessible_for_user
  407. get list of testprojects, considering user roles.
  408. Remember that user has:
  409. 1. one default role, assigned when user was created
  410. 2. a different role can be assigned for every testproject.
  411. For users roles that has not rigth to modify testprojects
  412. only active testprojects are returned.
  413. args:
  414. user_id
  415. [output_type]: choose the output data structure.
  416. possible values: map, map_of_map
  417. map: key -> test project id
  418. value -> test project name
  419. map_of_map: key -> test project id
  420. value -> array ('name' => test project name,
  421. 'active' => active status)
  422. array_of_map: value -> array with all testproject table fields plus name.
  423. default: map
  424. [order_by]: default: ORDER BY name
  425. rev :
  426. 20071104 - franciscom - added user_id,role_id to remove global coupling
  427. added order_by (BUGID 498)
  428. 20070725 - franciscom - added output_type
  429. 20060312 - franciscom - add nodes_hierarchy on join
  430. */
  431. function get_accessible_for_user($user_id,$output_type='map',$order_by=" ORDER BY name ")
  432. {
  433. $items = array();
  434. // Get default role
  435. $sql = " SELECT id,role_id FROM {$this->tables['users']} where id={$user_id}";
  436. $user_info = $this->db->get_recordset($sql);
  437. $role_id=$user_info[0]['role_id'];
  438. $sql = " SELECT nodes_hierarchy.name,testprojects.*
  439. FROM {$this->tables['nodes_hierarchy']} nodes_hierarchy
  440. JOIN {$this->object_table} testprojects ON nodes_hierarchy.id=testprojects.id
  441. LEFT OUTER JOIN {$this->tables['user_testproject_roles']} user_testproject_roles
  442. ON testprojects.id = user_testproject_roles.testproject_id AND
  443. user_testproject_roles.user_id = {$user_id} WHERE ";
  444. if ($role_id != TL_ROLES_NO_RIGHTS)
  445. {
  446. $sql .= "(role_id IS NULL OR role_id != ".TL_ROLES_NO_RIGHTS.")";
  447. }
  448. else
  449. {
  450. $sql .= "(role_id IS NOT NULL AND role_id != ".TL_ROLES_NO_RIGHTS.")";
  451. }
  452. if (has_rights($this->db,'mgt_modify_product') != 'yes')
  453. {
  454. $sql .= " AND active=1 ";
  455. }
  456. $sql .= $order_by;
  457. if($output_type == 'array_of_map')
  458. {
  459. $items = $this->db->get_recordset($sql);
  460. $this->parseTestProjectRecordset($items);
  461. $do_post_process=0;
  462. }
  463. else
  464. {
  465. $arrTemp = $this->db->fetchRowsIntoMap($sql,'id');
  466. $do_post_process=1;
  467. }
  468. if ($do_post_process && sizeof($arrTemp))
  469. {
  470. switch ($output_type)
  471. {
  472. case 'map':
  473. foreach($arrTemp as $id => $row)
  474. {
  475. $noteActive = '';
  476. if (!$row['active'])
  477. {
  478. $noteActive = TL_INACTIVE_MARKUP;
  479. }
  480. $items[$id] = $noteActive . $row['name'];
  481. }
  482. break;
  483. case 'map_of_map':
  484. foreach($arrTemp as $id => $row)
  485. {
  486. $items[$id] = array( 'name' => $row['name'],
  487. 'active' => $row['active']);
  488. }
  489. break;
  490. }
  491. }
  492. return $items;
  493. }
  494. /*
  495. function: get_subtree
  496. Get subtree that has choosen testproject as root.
  497. Only nodes of type:
  498. testsuite and testcase are explored and retrieved.
  499. args: id: testsuite id
  500. [recursive_mode]: default false
  501. [exclude_testcases]: default: false
  502. [exclude_branches]
  503. [and_not_in_clause]:
  504. returns: map
  505. see tree->get_subtree() for details.
  506. rev : 20080104 - franciscom - added exclude_testcases
  507. */
  508. function get_subtree($id,$recursive_mode=false,$exclude_testcases=false,
  509. $exclude_branches=null, $and_not_in_clause='')
  510. {
  511. $my['options']=array('recursive' => $recursive_mode);
  512. $my['filters'] = array('exclude_node_types' => $this->nt2exclude,
  513. 'exclude_children_of' => $this->nt2exclude_children,
  514. 'exclude_branches' => $exclude_branches,
  515. 'and_not_in_clause' => $and_not_in_clause);
  516. if($exclude_testcases)
  517. {
  518. $my['filters']['exclude_node_types']['testcase']='exclude me';
  519. }
  520. $subtree = $this->tree_manager->get_subtree($id,$my['filters'],$my['options']);
  521. return $subtree;
  522. }
  523. /**
  524. * Displays smarty template to show test project info to users.
  525. *
  526. * @param type $smarty [ref] smarty object
  527. * @param type $id test project
  528. * @param type $sqlResult [default = '']
  529. * @param type $action [default = 'update']
  530. * @param type $modded_item_id [default = 0]
  531. *
  532. * @todo havlatm (20100214): smarty should not be in this class - move code to appropariate page
  533. **/
  534. function show(&$smarty,$guiObj,$template_dir,$id,$sqlResult='', $action = 'update',$modded_item_id = 0)
  535. {
  536. $gui = $guiObj;
  537. $gui->modify_tc_rights = has_rights($this->db,"mgt_modify_tc");
  538. $gui->mgt_modify_product = has_rights($this->db,"mgt_modify_product");
  539. $gui->sqlResult = '';
  540. $gui->sqlAction = '';
  541. if($sqlResult)
  542. {
  543. $gui->sqlResult = $sqlResult;
  544. }
  545. $gui->container_data = $this->get_by_id($id);
  546. $gui->moddedItem = $gui->container_data;
  547. $gui->level = 'testproject';
  548. $gui->page_title = lang_get('testproject');
  549. $gui->refreshTree = false;
  550. $gui->attachmentInfos = getAttachmentInfosFrom($this,$id);
  551. if ($modded_item_id)
  552. {
  553. $gui->moddedItem = $this->get_by_id($modded_item_id);
  554. }
  555. $smarty->assign('gui', $gui);
  556. $smarty->display($template_dir . 'containerView.tpl');
  557. }
  558. /**
  559. * Count testcases without considering active/inactive status.
  560. *
  561. * @param integer $id: test project identifier
  562. * @return integer count of test cases presents on test project.
  563. */
  564. function count_testcases($id)
  565. {
  566. $tcIDs = array();
  567. $this->get_all_testcases_id($id,$tcIDs);
  568. $qty = sizeof($tcIDs);
  569. return $qty;
  570. }
  571. /*
  572. function: gen_combo_test_suites
  573. create array with test suite names
  574. test suites are ordered in parent-child way, means
  575. order on array is creating traversing tree branches, reaching end
  576. of branch, and starting again. (recursive algorithim).
  577. args : $id: test project id
  578. [$exclude_branches]: array with testsuite id to exclude
  579. useful to exclude myself ($id)
  580. [$mode]: dotted -> $level number of dot characters are appended to
  581. the left of test suite name to create an indent effect.
  582. Level indicates on what tree layer testsuite is positioned.
  583. Example:
  584. null
  585. \
  586. id=1 <--- Tree Root = Level 0
  587. |
  588. + ------+
  589. / \ \
  590. id=9 id=2 id=8 <----- Level 1
  591. \
  592. id=3 <----- Level 2
  593. \
  594. id=4 <----- Level 3
  595. key: testsuite id (= node id on tree).
  596. value: every array element is an string, containing testsuite name.
  597. Result example:
  598. 2 .TS1
  599. 3 ..TS2
  600. 9 .20071014-16:22:07 TS1
  601. 10 ..TS2
  602. array -> key: testsuite id (= node id on tree).
  603. value: every array element is a map with the following keys
  604. 'name', 'level'
  605. 2 array(name => 'TS1',level => 1)
  606. 3 array(name => 'TS2',level => 2)
  607. 9 array(name => '20071014-16:22:07 TS1',level =>1)
  608. 10 array(name => 'TS2', level => 2)
  609. returns: map , structure depens on $mode argument.
  610. */
  611. function gen_combo_test_suites($id,$exclude_branches=null,$mode='dotted')
  612. {
  613. $ret = array();
  614. $test_spec = $this->get_subtree($id,!self::RECURSIVE_MODE,self::EXCLUDE_TESTCASES,$exclude_branches);
  615. if(count($test_spec))
  616. {
  617. $ret = $this->_createHierarchyMap($test_spec);
  618. }
  619. return $ret;
  620. }
  621. /**
  622. * Checks a test project name for correctness
  623. *
  624. * @param string $name the name to check
  625. * @return map with keys: status_ok, msg
  626. **/
  627. function checkName($name)
  628. {
  629. $forbidden_pattern = config_get('ereg_forbidden');
  630. $ret['status_ok'] = 1;
  631. $ret['msg'] = 'ok';
  632. if ($name == "")
  633. {
  634. $ret['msg'] = lang_get('info_product_name_empty');
  635. $ret['status_ok'] = 0;
  636. }
  637. // BUGID 0000086
  638. if ($ret['status_ok'] && !check_string($name,$forbidden_pattern))
  639. {
  640. $ret['msg'] = lang_get('string_contains_bad_chars');
  641. $ret['status_ok'] = 0;
  642. }
  643. return $ret;
  644. }
  645. /**
  646. * Checks a test project name for sintax correctness
  647. *
  648. * @param string $name the name to check
  649. * @return map with keys: status_ok, msg
  650. **/
  651. function checkNameSintax($name)
  652. {
  653. $forbidden_pattern = config_get('ereg_forbidden');
  654. $ret['status_ok'] = 1;
  655. $ret['msg'] = 'ok';
  656. if ($name == "")
  657. {
  658. $ret['msg'] = lang_get('info_product_name_empty');
  659. $ret['status_ok'] = 0;
  660. }
  661. if ($ret['status_ok'] && !check_string($name,$forbidden_pattern))
  662. {
  663. $ret['msg'] = lang_get('string_contains_bad_chars');
  664. $ret['status_ok'] = 0;
  665. }
  666. return $ret;
  667. }
  668. /**
  669. * Checks is there is another testproject with different id but same name
  670. *
  671. **/
  672. function checkNameExistence($name,$id=0)
  673. {
  674. $check_op['msg'] = '';
  675. $check_op['status_ok'] = 1;
  676. if($this->get_by_name($name,"testprojects.id <> {$id}") )
  677. {
  678. $check_op['msg'] = sprintf(lang_get('error_product_name_duplicate'),$name);
  679. $check_op['status_ok'] = 0;
  680. }
  681. return $check_op;
  682. }
  683. /**
  684. * Checks is there is another testproject with different id but same prefix
  685. *
  686. **/
  687. function checkTestCasePrefixExistence($prefix,$id=0)
  688. {
  689. $check_op['msg'] = '';
  690. $check_op['status_ok'] = 1;
  691. $sql = " SELECT id FROM {$this->object_table} " .
  692. " WHERE prefix='" . $this->db->prepare_string($prefix) . "'";
  693. " AND id <> {$id}";
  694. $rs = $this->db->get_recordset($sql);
  695. if(!is_null($rs))
  696. {
  697. $check_op['msg'] = sprintf(lang_get('error_tcase_prefix_exists'),$prefix);
  698. $check_op['status_ok'] = 0;
  699. }
  700. return $check_op;
  701. }
  702. /**
  703. * allow activate or deactivate a test project
  704. *
  705. * @param integer $id test project ID
  706. * @param integer $status 1=active || 0=inactive
  707. */
  708. function activateTestProject($id, $status)
  709. {
  710. $sql = "UPDATE {$this->tables['testprojects']} SET active=" . $status . " WHERE id=" . $id;
  711. $result = $this->db->exec_query($sql);
  712. return $result ? 1 : 0;
  713. }
  714. /** @TODO add description */
  715. function formatTcPrefix($str)
  716. {
  717. // limit tcasePrefix len.
  718. $fstr = trim($str);
  719. if(tlStringLen($fstr) > self::TESTCASE_PREFIX_MAXLEN)
  720. {
  721. $tcprefix = substr($fstr,self::TESTCASE_PREFIX_MAXLEN);
  722. }
  723. return $fstr;
  724. }
  725. /*
  726. args : id: test project
  727. returns: null if query fails
  728. string
  729. */
  730. function getTestCasePrefix($id)
  731. {
  732. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  733. $ret=null;
  734. $sql = "/* $debugMsg */ SELECT prefix FROM {$this->object_table} WHERE id = {$id}";
  735. $ret = $this->db->fetchOneValue($sql);
  736. return ($ret);
  737. }
  738. /*
  739. args: id: test project
  740. returns: null if query fails
  741. a new test case number
  742. */
  743. function generateTestCaseNumber($id)
  744. {
  745. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  746. $ret=null;
  747. $sql = "/* $debugMsg */ UPDATE {$this->object_table} " .
  748. " SET tc_counter=tc_counter+1 WHERE id = {$id}";
  749. $recordset = $this->db->exec_query($sql);
  750. $sql = " SELECT tc_counter FROM {$this->object_table} WHERE id = {$id}";
  751. $recordset = $this->db->get_recordset($sql);
  752. $ret=$recordset[0]['tc_counter'];
  753. return ($ret);
  754. }
  755. /**
  756. * @param integer $id test project ID
  757. */
  758. function setPublicStatus($id,$status)
  759. {
  760. $isPublic = val($status) > 0 ? 1 : 0;
  761. $sql = "UPDATE {$this->object_table} SET is_public={$isPublic} WHERE id={$id}";
  762. $result = $this->db->exec_query($sql);
  763. return $result ? 1 : 0;
  764. }
  765. /* Keywords related methods */
  766. /**
  767. * Adds a new keyword to the given test project
  768. *
  769. * @param int $testprojectID
  770. * @param string $keyword
  771. * @param string $notes
  772. *
  773. **/
  774. public function addKeyword($testprojectID,$keyword,$notes)
  775. {
  776. $kw = new tlKeyword();
  777. $kw->initialize(null,$testprojectID,$keyword,$notes);
  778. $op = array('status' => tlKeyword::E_DBERROR, 'id' => -1);
  779. $op['status'] = $kw->writeToDB($this->db);
  780. if ($op['status'] >= tl::OK)
  781. {
  782. $op['id'] = $kw->dbID;
  783. logAuditEvent(TLS("audit_keyword_created",$keyword),"CREATE",$op['id'],"keywords");
  784. }
  785. return $op;
  786. }
  787. /**
  788. * updates the keyword with the given id
  789. *
  790. * @param type $testprojectID
  791. * @param type $id
  792. * @param type $keyword
  793. * @param type $notes
  794. *
  795. **/
  796. function updateKeyword($testprojectID,$id,$keyword,$notes)
  797. {
  798. $kw = new tlKeyword($id);
  799. $kw->initialize($id,$testprojectID,$keyword,$notes);
  800. $result = $kw->writeToDB($this->db);
  801. if ($result >= tl::OK)
  802. logAuditEvent(TLS("audit_keyword_saved",$keyword),"SAVE",$kw->dbID,"keywords");
  803. return $result;
  804. }
  805. /**
  806. * gets the keyword with the given id
  807. *
  808. * @param type $kwid
  809. **/
  810. public function getKeyword($id)
  811. {
  812. return tlKeyword::getByID($this->db,$id);
  813. }
  814. /**
  815. * Gets the keywords of the given test project
  816. *
  817. * @param int $tprojectID the test project id
  818. * @param int $keywordID [default = null] the optional keyword id
  819. *
  820. * @return array, every elemen is map with following structure:
  821. * id
  822. * keyword
  823. * notes
  824. **/
  825. public function getKeywords($testproject_id)
  826. {
  827. $ids = $this->getKeywordIDsFor($testproject_id);
  828. return tlKeyword::getByIDs($this->db,$ids);
  829. }
  830. /**
  831. * Deletes the keyword with the given id
  832. *
  833. * @param int $id the keywordID
  834. * @return int returns 1 on success, 0 else
  835. *
  836. * @todo should we now increment the tcversion also?
  837. **/
  838. function deleteKeyword($id)
  839. {
  840. $result = tl::ERROR;
  841. $keyword = $this->getKeyword($id);
  842. if ($keyword)
  843. {
  844. $result = tlDBObject::deleteObjectFromDB($this->db,$id,"tlKeyword");
  845. }
  846. if ($result >= tl::OK)
  847. {
  848. logAuditEvent(TLS("audit_keyword_deleted",$keyword->name),"DELETE",$id,"keywords");
  849. }
  850. return $result;
  851. }
  852. /**
  853. * delete Keywords
  854. */
  855. function deleteKeywords($testproject_id)
  856. {
  857. $result = tl::OK;
  858. $kwIDs = $this->getKeywordIDsFor($testproject_id);
  859. for($i = 0;$i < sizeof($kwIDs);$i++)
  860. {
  861. $resultKw = $this->deleteKeyword($kwIDs[$i]);
  862. if ($resultKw != tl::OK)
  863. $result = $resultKw;
  864. }
  865. return $result;
  866. }
  867. /**
  868. *
  869. *
  870. */
  871. protected function getKeywordIDsFor($testproject_id)
  872. {
  873. $query = " SELECT id FROM {$this->tables['keywords']} " .
  874. " WHERE testproject_id = {$testproject_id}" .
  875. " ORDER BY keyword ASC";
  876. $keywordIDs = $this->db->fetchColumnsIntoArray($query,'id');
  877. return $keywordIDs;
  878. }
  879. /**
  880. * Exports the given keywords to a XML file
  881. *
  882. * @return strings the generated XML Code
  883. **/
  884. public function exportKeywordsToXML($testproject_id,$bNoXMLHeader = false)
  885. {
  886. $kwIDs = $this->getKeywordIDsFor($testproject_id);
  887. $xmlCode = '';
  888. if (!$bNoXMLHeader)
  889. {
  890. $xmlCode .= TL_XMLEXPORT_HEADER."\n";
  891. }
  892. $xmlCode .= "<keywords>";
  893. for($idx = 0;$idx < sizeof($kwIDs);$idx++)
  894. {
  895. $keyword = new tlKeyword($kwIDs[$idx]);
  896. $keyword->readFromDb($this->db);
  897. $keyword->writeToXML($xmlCode,true);
  898. }
  899. $xmlCode .= "</keywords>";
  900. return $xmlCode;
  901. }
  902. /**
  903. * Exports the given keywords to CSV
  904. *
  905. * @return string the generated CSV code
  906. **/
  907. function exportKeywordsToCSV($testproject_id,$delim = ';')
  908. {
  909. $kwIDs = $this->getKeywordIDsFor($testproject_id);
  910. $csv = null;
  911. for($idx = 0;$idx < sizeof($kwIDs);$idx++)
  912. {
  913. $keyword = new tlKeyword($kwIDs[$idx]);
  914. $keyword->readFromDb($this->db);
  915. $keyword->writeToCSV($csv,$delim);
  916. }
  917. return $csv;
  918. }
  919. function importKeywordsFromCSV($testproject_id,$fileName,$delim = ';')
  920. {
  921. $handle = fopen($fileName,"r");
  922. if ($handle)
  923. {
  924. while($data = fgetcsv($handle, TL_IMPORT_ROW_MAX, $delim))
  925. {
  926. $kw = new tlKeyword();
  927. $kw->initialize(null,$testproject_id,NULL,NULL);
  928. if ($kw->readFromCSV(implode($delim,$data)) >= tl::OK)
  929. {
  930. if ($kw->writeToDB($this->db) >= tl::OK)
  931. logAuditEvent(TLS("audit_keyword_created",$kw->name),"CREATE",$kw->dbID,"keywords");
  932. }
  933. }
  934. fclose($handle);
  935. return tl::OK;
  936. }
  937. else
  938. {
  939. return ERROR;
  940. }
  941. }
  942. /**
  943. * @param $testproject_id
  944. * @param $fileName
  945. */
  946. function importKeywordsFromXMLFile($testproject_id,$fileName)
  947. {
  948. $simpleXMLObj = simplexml_load_file($fileName);
  949. return $this->importKeywordsFromSimpleXML($testproject_id,$simpleXMLObj);
  950. }
  951. /**
  952. * @param $testproject_id
  953. * @param $xmlString
  954. */
  955. function importKeywordsFromXML($testproject_id,$xmlString)
  956. {
  957. $simpleXMLObj = simplexml_load_string($xmlString);
  958. return $this->importKeywordsFromSimpleXML($testproject_id,$simpleXMLObj);
  959. }
  960. /**
  961. * @param $testproject_id
  962. * @param $simpleXMLObj
  963. */
  964. function importKeywordsFromSimpleXML($testproject_id,$simpleXMLObj)
  965. {
  966. $status = tl::OK;
  967. if(!$simpleXMLObj || $simpleXMLObj->getName() != 'keywords')
  968. {
  969. $status = tlKeyword::E_WRONGFORMAT;
  970. }
  971. if( ($status == tl::OK) && $simpleXMLObj->keyword )
  972. {
  973. foreach($simpleXMLObj->keyword as $keyword)
  974. {
  975. $kw = new tlKeyword();
  976. $kw->initialize(null,$testproject_id,NULL,NULL);
  977. $status = tlKeyword::E_WRONGFORMAT;
  978. if ($kw->readFromSimpleXML($keyword) >= tl::OK)
  979. {
  980. $status = tl::OK;
  981. if ($kw->writeToDB($this->db) >= tl::OK)
  982. {
  983. logAuditEvent(TLS("audit_keyword_created",$kw->name),"CREATE",$kw->dbID,"keywords");
  984. }
  985. }
  986. }
  987. }
  988. return $status;
  989. }
  990. /**
  991. * Returns all testproject keywords
  992. *
  993. * @param integer $testproject_id the ID of the testproject
  994. * @return array map: key: keyword_id, value: keyword
  995. */
  996. function get_keywords_map($testproject_id)
  997. {
  998. $keywordMap = null;
  999. $keywords = $this->getKeywords($testproject_id);
  1000. if ($keywords)
  1001. {
  1002. foreach($keywords as $kw)
  1003. {
  1004. $keywordMap[$kw->dbID] = $kw->name;
  1005. }
  1006. }
  1007. return $keywordMap;
  1008. }
  1009. /* END KEYWORDS RELATED */
  1010. /* REQUIREMENTS RELATED */
  1011. /**
  1012. * get list of all SRS for a test project
  1013. *
  1014. * @author Martin Havlat
  1015. * @return associated array List of titles according to IDs
  1016. *
  1017. * @internal
  1018. * rev :
  1019. * 20070104 - franciscom - added [$get_not_empy]
  1020. **/
  1021. function getOptionReqSpec($tproject_id,$get_not_empty=self::GET_EMPTY_REQSPEC)
  1022. {
  1023. $additional_table='';
  1024. $additional_join='';
  1025. if( $get_not_empty )
  1026. {
  1027. $additional_table=", {$this->tables['requirements']} REQ ";
  1028. $additional_join=" AND SRS.id = REQ.srs_id ";
  1029. }
  1030. $sql = " SELECT SRS.id,NH.name AS title " .
  1031. " FROM {$this->tables['req_specs']} SRS, {$this->tables['nodes_hierarchy']} NH " . $additional_table .
  1032. " WHERE testproject_id={$tproject_id} " .
  1033. " AND SRS.id=NH.id " .
  1034. $additional_join .
  1035. " ORDER BY title";
  1036. return $this->db->fetchColumnsIntoMap($sql,'id','title');
  1037. } // function end
  1038. /**
  1039. * TBD
  1040. * @author Francisco Mancardi - francisco.mancardi@gmail.com
  1041. *
  1042. * @internal rev :
  1043. * 20090125 - franciscom
  1044. **/
  1045. function genComboReqSpec($id,$mode='dotted')
  1046. {
  1047. $ret = array();
  1048. $exclude_node_types=array('testplan' => 'exclude_me','testsuite' => 'exclude_me',
  1049. 'testcase'=> 'exclude_me','requirement' => 'exclude_me');
  1050. $my['filters'] = array('exclude_node_types' => $exclude_node_types);
  1051. $subtree = $this->tree_manager->get_subtree($id,$my['filters']);
  1052. if(count($subtree))
  1053. {
  1054. $ret = $this->_createHierarchyMap($subtree);
  1055. }
  1056. return $ret;
  1057. }
  1058. /*
  1059. [$mode]: dotted -> $level number of dot characters are appended to
  1060. the left of item name to create an indent effect.
  1061. Level indicates on what tree layer item is positioned.
  1062. Example:
  1063. null
  1064. \
  1065. id=1 <--- Tree Root = Level 0
  1066. |
  1067. + ------+
  1068. / \ \
  1069. id=9 id=2 id=8 <----- Level 1
  1070. \
  1071. id=3 <----- Level 2
  1072. \
  1073. id=4 <----- Level 3
  1074. key: item id (= node id on tree).
  1075. value: every array element is an string, containing item name.
  1076. Result example:
  1077. 2 .TS1
  1078. 3 ..TS2
  1079. 9 .20071014-16:22:07 TS1
  1080. 10 ..TS2
  1081. array -> key: item id (= node id on tree).
  1082. value: every array element is a map with the following keys
  1083. 'name', 'level'
  1084. 2 array(name => 'TS1',level => 1)
  1085. 3 array(name => 'TS2',level => 2)
  1086. 9 array(name => '20071014-16:22:07 TS1',level =>1)
  1087. 10 array(name => 'TS2', level => 2)
  1088. */
  1089. protected function _createHierarchyMap($array2map,$mode='dotted')
  1090. {
  1091. $hmap=array();
  1092. $the_level = 1;
  1093. $level = array();
  1094. $pivot = $array2map[0];
  1095. foreach($array2map as $elem)
  1096. {
  1097. $current = $elem;
  1098. if ($pivot['id'] == $current['parent_id'])
  1099. {
  1100. $the_level++;
  1101. $level[$current['parent_id']]=$the_level;
  1102. }
  1103. else if ($pivot['parent_id'] != $current['parent_id'])
  1104. {
  1105. $the_level = $level[$current['parent_id']];
  1106. }
  1107. switch($mode)
  1108. {
  1109. case 'dotted':
  1110. $hmap[$current['id']] = str_repeat('.',$the_level) . $current['name'];
  1111. break;
  1112. case 'array':
  1113. $hmap[$current['id']] = array('name' => $current['name'], 'level' =>$the_level);
  1114. break;
  1115. }
  1116. // update pivot
  1117. $level[$current['parent_id']]= $the_level;
  1118. $pivot=$elem;
  1119. }
  1120. return $hmap;
  1121. }
  1122. /**
  1123. * collect information about current list of Requirements Specification
  1124. *
  1125. * @param integer $testproject_id
  1126. * @param string $id optional id of the requirement specification
  1127. *
  1128. * @return mixed
  1129. * null if no srs exits, or no srs exists for id
  1130. * array, where each element is a map with SRS data.
  1131. *
  1132. * map keys:
  1133. * id
  1134. * testproject_id
  1135. * title
  1136. * scope
  1137. * total_req
  1138. * type
  1139. * author_id
  1140. * creation_ts
  1141. * modifier_id
  1142. * modification_ts
  1143. *
  1144. * @author Martin Havlat
  1145. * @internal rev:
  1146. * 20090506 - francisco.mancardi@gruppotesi.com - Requirements Refactoring
  1147. *
  1148. **/
  1149. public function getReqSpec($testproject_id, $id = null, $fields=null,$access_key=null)
  1150. {
  1151. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  1152. $fields2get="RSPEC.id,testproject_id,RSPEC.scope,RSPEC.total_req,RSPEC.type," .
  1153. "RSPEC.author_id,RSPEC.creation_ts,RSPEC.modifier_id," .
  1154. "RSPEC.modification_ts,NH.name AS title";
  1155. $fields = is_null($fields) ? $fields2get : implode(',',$fields);
  1156. $sql = " /* $debugMsg */ SELECT {$fields} FROM {$this->tables['req_specs']} RSPEC, " .
  1157. " {$this->tables['nodes_hierarchy']} NH " .
  1158. " WHERE testproject_id={$testproject_id} AND RSPEC.id=NH.id ";
  1159. if (!is_null($id))
  1160. {
  1161. $sql .= " AND RSPEC.id=" . $id;
  1162. }
  1163. $sql .= " ORDER BY title";
  1164. $rs = is_null($access_key) ? $this->db->get_recordset($sql) : $this->db->fetchRowsIntoMap($sql,$access_key);
  1165. return $rs;
  1166. }
  1167. /**
  1168. * create a new System Requirements Specification
  1169. *
  1170. * @param string $title
  1171. * @param string $scope
  1172. * @param string $countReq
  1173. * @param numeric $testproject_id
  1174. * @param numeric $user_id
  1175. * @param string $type
  1176. *
  1177. * @author Martin Havlat
  1178. *
  1179. * rev: 20071106 - franciscom - changed return type
  1180. */
  1181. function createReqSpec($testproject_id,$title, $scope, $countReq,$user_id,$type = 'n')
  1182. {
  1183. $ignore_case=1;
  1184. $result=array();
  1185. $result['status_ok'] = 0;
  1186. $result['msg'] = 'ko';
  1187. $result['id'] = 0;
  1188. $title=trim($title);
  1189. $chk=$this->check_srs_title($testproject_id,$title,$ignore_case);
  1190. if ($chk['status_ok'])
  1191. {
  1192. $sql = "INSERT INTO {$this->tables['req_specs']} " .
  1193. " (testproject_id, title, scope, type, total_req, author_id, creation_ts)
  1194. VALUES (" . $testproject_id . ",'" . $this->db->prepare_string($title) . "','" .
  1195. $this->db->prepare_string($scope) . "','" . $this->db->prepare_string($type) . "','" .
  1196. $this->db->prepare_string($countReq) . "'," . $this->db->prepare_string($user_id) . ", " .
  1197. $this->db->db_now() . ")";
  1198. if (!$this->db->exec_query($sql))
  1199. {
  1200. $result['msg']=lang_get('error_creating_req_spec');
  1201. }
  1202. else
  1203. {
  1204. $result['id']=$this->db->insert_id($this->tables['req_specs']);
  1205. $result['status_ok'] = 1;
  1206. $result['msg'] = 'ok';
  1207. }
  1208. }
  1209. else
  1210. {
  1211. $result['msg']=$chk['msg'];
  1212. }
  1213. return $result;
  1214. }
  1215. /*
  1216. function: get_srs_by_title
  1217. get srs information using title as access key.
  1218. args : tesproject_id
  1219. title: srs title
  1220. [ignore_case]: control case sensitive search.
  1221. default 0 -> case sensivite search
  1222. returns: map.
  1223. key: srs id
  1224. value: srs info, map with folowing keys:
  1225. id
  1226. testproject_id
  1227. title
  1228. scope
  1229. total_req
  1230. type
  1231. author_id
  1232. creation_ts
  1233. modifier_id
  1234. modification_ts
  1235. */
  1236. public function get_srs_by_title($testproject_id,$title,$ignore_case=0)
  1237. {
  1238. $output=null;
  1239. $title=trim($title);
  1240. $sql = "SELECT * FROM req_specs ";
  1241. if($ignore_case)
  1242. {
  1243. $sql .= " WHERE UPPER(title)='" . strtoupper($this->db->prepare_string($title)) . "'";
  1244. }
  1245. else
  1246. {
  1247. $sql .= " WHERE title='" . $this->db->prepare_string($title) . "'";
  1248. }
  1249. $sql .= " AND testproject_id={$testproject_id}";
  1250. $output = $this->db->fetchRowsIntoMap($sql,'id');
  1251. return $output;
  1252. }
  1253. /*
  1254. function: check_srs_title
  1255. Do checks on srs title, to understand if can be used.
  1256. Checks:
  1257. 1. title is empty ?
  1258. 2. does already exist a srs with this title?
  1259. args : tesproject_id
  1260. title: srs title
  1261. [ignore_case]: control case sensitive search.
  1262. default 0 -> case sensivite search
  1263. returns:
  1264. */
  1265. function check_srs_title($testproject_id,$title,$ignore_case=0)
  1266. {
  1267. $ret['status_ok'] = 1;
  1268. $ret['msg'] = '';
  1269. $title = trim($title);
  1270. if ($title == "")
  1271. {
  1272. $ret['status_ok'] = 0;
  1273. $ret['msg'] = lang_get("warning_empty_req_title");
  1274. }
  1275. if($ret['status_ok'])
  1276. {
  1277. $ret['msg'] = 'ok';
  1278. $rs = $this->get_srs_by_title($testproject_id,$title,$ignore_case);
  1279. if(!is_null($rs))
  1280. {
  1281. $ret['msg'] = lang_get("warning_duplicate_req_title");
  1282. $ret['status_ok'] = 0;
  1283. }
  1284. }
  1285. return $ret;
  1286. }
  1287. /* END REQUIREMENT RELATED */
  1288. // ----------------------------------------------------------------------------------------
  1289. /**
  1290. * Deletes all testproject related role assignments for a given testproject
  1291. *
  1292. * @param integer $tproject_id
  1293. * @return integer tl::OK on success, tl::ERROR else
  1294. **/
  1295. function deleteUserRoles($tproject_id)
  1296. {
  1297. $query = "DELETE FROM {$this->tables['user_testproject_roles']} " .
  1298. " WHERE testproject_id = {$tproject_id}";
  1299. if ($this->db->exec_query($query))
  1300. {
  1301. $testProject = $this->get_by_id($tproject_id);
  1302. if ($testProject)
  1303. {
  1304. logAuditEvent(TLS("audit_all_user_roles_removed_testproject",$testProject['name']),
  1305. "ASSIGN",$tproject_id,"testprojects");
  1306. }
  1307. return tl::OK;
  1308. }
  1309. return tl::ERROR;
  1310. }
  1311. /**
  1312. * Gets all testproject related role assignments
  1313. *
  1314. * @param integer $tproject_id
  1315. * @return array assoc array with keys take from the user_id column
  1316. **/
  1317. function getUserRoleIDs($tproject_id)
  1318. {
  1319. $query = "SELECT user_id,role_id FROM {$this->tables['user_testproject_roles']} " .
  1320. "WHERE testproject_id = {$tproject_id}";
  1321. $roles = $this->db->fetchRowsIntoMap($query,'user_id');
  1322. return $roles;
  1323. }
  1324. /**
  1325. * Inserts a testproject related role for a given user
  1326. *
  1327. * @param integer $userID the id of the user
  1328. * @param integer $tproject_id
  1329. * @param integer $roleID the role id
  1330. *
  1331. * @return integer tl::OK on success, tl::ERROR else
  1332. **/
  1333. function addUserRole($userID,$tproject_id,$roleID)
  1334. {
  1335. $query = "INSERT INTO {$this->tables['user_testproject_roles']} " .
  1336. "(user_id,testproject_id,role_id) VALUES ({$userID},{$tproject_id},{$roleID})";
  1337. if($this->db->exec_query($query))
  1338. {
  1339. $testProject = $this->get_by_id($tproject_id);
  1340. $role = tlRole::getByID($this->db,$roleID,tlRole::TLOBJ_O_GET_DETAIL_MINIMUM);
  1341. $user = tlUser::getByID($this->db,$userID,tlUser::TLOBJ_O_GET_DETAIL_MINIMUM);
  1342. if ($user && $testProject && $role)
  1343. {
  1344. logAuditEvent(TLS("audit_users_roles_added_testproject",$user->getDisplayName(),
  1345. $testProject['name'],$role->name),"ASSIGN",$tproject_id,"testprojects");
  1346. }
  1347. return tl::OK;
  1348. }
  1349. return tl::ERROR;
  1350. }
  1351. /**
  1352. * delete test project from system, deleting all dependent data:
  1353. * keywords, requirements, custom fields, testsuites, testplans,
  1354. * testcases, results, testproject related roles,
  1355. *
  1356. * @param integer $id test project id
  1357. * @return integer status
  1358. *
  1359. */
  1360. function delete($id)
  1361. {
  1362. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  1363. $ret['msg']='ok';
  1364. $ret['status_ok']=1;
  1365. $error = '';
  1366. $reqspec_mgr = new requirement_spec_mgr($this->db);
  1367. //
  1368. // Notes on delete related to Foreing Keys
  1369. // All link tables has to be deleted first
  1370. //
  1371. // req_relations
  1372. //
  1373. // testplan_tcversions
  1374. // testplan_platforms
  1375. // object_keywords
  1376. // user_assignments
  1377. // builds
  1378. // milestones
  1379. //
  1380. // testplans
  1381. // keywords
  1382. // platforms
  1383. // attachtments
  1384. // testcases
  1385. // testsuites
  1386. // inventory
  1387. //
  1388. // testproject
  1389. $this->deleteKeywords($id);
  1390. $this->deleteAttachments($id);
  1391. $reqSpecSet=$reqspec_mgr->get_all_in_testproject($id);
  1392. if( !is_null($reqSpecSet) && count($reqSpecSet) > 0 )
  1393. {
  1394. foreach($reqSpecSet as $reqSpec)
  1395. {
  1396. $reqspec_mgr->delete_deep($reqSpec['id']);
  1397. }
  1398. }
  1399. $tplanSet = $this->get_all_testplans($id);
  1400. if( !is_null($tplanSet) && count($tplanSet) > 0 )
  1401. {
  1402. $tplan_mgr = new testplan($this->db);
  1403. $items=array_keys($tplanSet);
  1404. foreach($items as $key)
  1405. {
  1406. $tplan_mgr->delete($key);
  1407. }
  1408. }
  1409. $platform_mgr = new tlPlatform($this->db,$id);
  1410. $platform_mgr->deleteByTestProject($id);
  1411. $a_sql[] = array("/* $debugMsg */ UPDATE {$this->tables['users']} " .
  1412. " SET default_testproject_id = NULL " .
  1413. " WHERE default_testproject_id = {$id}",
  1414. 'info_resetting_default_project_fails');
  1415. // BUGID 3464
  1416. $inventory_mgr = new tlInventory($id,$this->db);
  1417. $invOpt = array('detailLevel' => 'minimun', 'accessKey' => 'id');
  1418. $inventorySet = $inventory_mgr->getAll($invOpt);
  1419. if( !is_null($inventorySet) )
  1420. {
  1421. foreach($inventorySet as $key => $dummy)
  1422. {
  1423. $inventory_mgr->deleteInventory($key);
  1424. }
  1425. }
  1426. foreach ($a_sql as $oneSQL)
  1427. {
  1428. if (empty($error))
  1429. {
  1430. $sql = $oneSQL[0];
  1431. $result = $this->db->exec_query($sql);
  1432. if (!$result)
  1433. {
  1434. $error .= lang_get($oneSQL[1]);
  1435. }
  1436. }
  1437. }
  1438. if ($this->deleteUserRoles($id) < tl::OK)
  1439. {
  1440. $error .= lang_get('info_deleting_project_roles_fails');
  1441. }
  1442. // ---------------------------------------------------------------------------------------
  1443. // delete product itself and items directly related to it like:
  1444. // custom fields assignments
  1445. // custom fields values ( right now we are not using custom fields on test projects)
  1446. // attachments
  1447. if (empty($error))
  1448. {
  1449. $sql = "/* $debugMsg */ DELETE FROM {$this->tables['cfield_testprojects']} WHERE testproject_id = {$id} ";
  1450. $this->db->exec_query($sql);
  1451. $sql = "/* $debugMsg */ DELETE FROM {$this->object_table} WHERE id = {$id}";
  1452. $result = $this->db->exec_query($sql);
  1453. if ($result)
  1454. {
  1455. $tproject_id_on_session = isset($_SESSION['testprojectID']) ? $_SESSION['testprojectID'] : $id;
  1456. if ($id == $tproject_id_on_session)
  1457. {
  1458. $this->setSessionProject(null);
  1459. }
  1460. }
  1461. else
  1462. {
  1463. $error .= lang_get('info_product_delete_fails');
  1464. }
  1465. }
  1466. if (empty($error))
  1467. {
  1468. // BUGID 3147 - Delete test project with requirements defined crashed with memory exhausted
  1469. $this->tree_manager->delete_subtree_objects($id,$id,'',array('testcase' => 'exclude_tcversion_nodes'));
  1470. $sql = "/* $debugMsg */ DELETE FROM {$this->tables['nodes_hierarchy']} WHERE id = {$id} ";
  1471. $this->db->exec_query($sql);
  1472. }
  1473. if( !empty($error) )
  1474. {
  1475. $ret['msg']=$error;
  1476. $ret['status_ok']=0;
  1477. }
  1478. return $ret;
  1479. }
  1480. /*
  1481. function: get_all_testcases_id
  1482. All testproject testcases node id.
  1483. args :idList: comma-separated list of IDs (should be the projectID, but could
  1484. also be an arbitrary suiteID
  1485. returns: array with testcases node id in parameter tcIDs.
  1486. null is nothing found
  1487. */
  1488. function get_all_testcases_id($idList,&$tcIDs)
  1489. {
  1490. static $tcNodeTypeID;
  1491. static $tsuiteNodeTypeID;
  1492. static $debugMsg;
  1493. if (!$tcNodeTypeID)
  1494. {
  1495. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  1496. $tcNodeTypeID = $this->tree_manager->node_descr_id['testcase'];
  1497. $tsuiteNodeTypeID = $this->tree_manager->node_descr_id['testsuite'];
  1498. }
  1499. $sql = "/* $debugMsg */ SELECT id,node_type_id from {$this->tables['nodes_hierarchy']} " .
  1500. " WHERE parent_id IN ({$idList})";
  1501. $sql .= " AND node_type_id IN ({$tcNodeTypeID},{$tsuiteNodeTypeID}) ";
  1502. $result = $this->db->exec_query($sql);
  1503. if ($result)
  1504. {
  1505. $suiteIDs = array();
  1506. while($row = $this->db->fetch_array($result))
  1507. {
  1508. if ($row['node_type_id'] == $tcNodeTypeID)
  1509. {
  1510. $tcIDs[] = $row['id'];
  1511. }
  1512. $suiteIDs[] = $row['id'];
  1513. }
  1514. if (sizeof($suiteIDs))
  1515. {
  1516. $suiteIDs = implode(",",$suiteIDs);
  1517. $this->get_all_testcases_id($suiteIDs,$tcIDs);
  1518. }
  1519. }
  1520. }
  1521. /*
  1522. function: get_keywords_tcases
  1523. testproject keywords (with related testcase node id),
  1524. that are used on testcases.
  1525. args :testproject_id
  1526. [keyword_id]= 0 -> no filter
  1527. <> 0 -> look only for this keyword
  1528. can be an array.
  1529. returns: map: key: testcase_id
  1530. value: map
  1531. key: keyword_id
  1532. value: testcase_id,keyword_id,keyword
  1533. Example:
  1534. [24] => Array ( [3] => Array( [testcase_id] => 24
  1535. [keyword_id] => 3
  1536. [keyword] => MaxFactor )
  1537. [2] => Array( [testcase_id] => 24
  1538. [keyword_id] => 2
  1539. [keyword] => Terminator ) )
  1540. */
  1541. function get_keywords_tcases($testproject_id, $keyword_id=0, $keyword_filter_type='OR')
  1542. {
  1543. $keyword_filter= '' ;
  1544. $subquery='';
  1545. if( is_array($keyword_id) )
  1546. {
  1547. $keyword_filter = " AND keyword_id IN (" . implode(',',$keyword_id) . ")";
  1548. if($keyword_filter_type == 'AND')
  1549. {
  1550. $subquery = "AND testcase_id IN (" .
  1551. " SELECT FOXDOG.testcase_id FROM
  1552. ( SELECT COUNT(testcase_id) AS HITS,testcase_id
  1553. FROM {$this->tables['keywords']} K, {$this->tables['testcase_keywords']}
  1554. WHERE keyword_id = K.id
  1555. AND testproject_id = {$testproject_id}
  1556. {$keyword_filter}
  1557. GROUP BY testcase_id ) AS FOXDOG " .
  1558. " WHERE FOXDOG.HITS=" . count($keyword_id) . ")";
  1559. $keyword_filter ='';
  1560. }
  1561. }
  1562. else if( $keyword_id > 0 )
  1563. {
  1564. $keyword_filter = " AND keyword_id = {$keyword_id} ";
  1565. }
  1566. $map_keywords = null;
  1567. $sql = " SELECT testcase_id,keyword_id,keyword
  1568. FROM {$this->tables['keywords']} K, {$this->tables['testcase_keywords']}
  1569. WHERE keyword_id = K.id
  1570. AND testproject_id = {$testproject_id}
  1571. {$keyword_filter} {$subquery}
  1572. ORDER BY keyword ASC ";
  1573. $map_keywords = $this->db->fetchMapRowsIntoMap($sql,'testcase_id','keyword_id');
  1574. return($map_keywords);
  1575. } //end function
  1576. /*
  1577. function: get_all_testplans
  1578. args : $testproject_id
  1579. [$filters]: optional map, with optional keys
  1580. [$get_tp_without_tproject_id]
  1581. used just for backward compatibility (TL 1.5)
  1582. default: 0 -> 1.6 and up behaviour
  1583. [$plan_status]
  1584. default: null -> no filter on test plan status
  1585. 1 -> active test plans
  1586. 0 -> inactive test plans
  1587. [$exclude_tplans]: null -> do not apply exclusion
  1588. id -> test plan id to exclude
  1589. [options]:
  1590. returns:
  1591. 20100821 - franciscom - added options
  1592. */
  1593. function get_all_testplans($testproject_id,$filters=null,$options=null)
  1594. {
  1595. $my['options'] = array('fields2get' => 'NH.id,NH.name,notes,active,is_public,testproject_id',
  1596. 'outputType' => null);
  1597. $my['options'] = array_merge($my['options'], (array)$options);
  1598. $forHMLSelect = false;
  1599. if( !is_null($my['options']['outputType']) && $my['options']['outputType'] == 'forHMLSelect')
  1600. {
  1601. $forHMLSelect = true;
  1602. $my['options']['fields2get'] = 'NH.id,NH.name';
  1603. }
  1604. $sql = " SELECT {$my['options']['fields2get']} " .
  1605. " FROM {$this->tables['nodes_hierarchy']} NH,{$this->tables['testplans']} TPLAN";
  1606. $where = " WHERE NH.id=TPLAN.id ";
  1607. $where .= " AND (testproject_id = " . $this->db->prepare_int($testproject_id) . " ";
  1608. if( !is_null($filters) )
  1609. {
  1610. $key2check=array('get_tp_without_tproject_id' => 0, 'plan_status' => null,
  1611. 'tplan2exclude' => null);
  1612. foreach($key2check as $varname => $defValue)
  1613. {
  1614. $$varname=isset($filters[$varname]) ? $filters[$varname] : $defValue;
  1615. }
  1616. $where .= " ) ";
  1617. if(!is_null($plan_status))
  1618. {
  1619. $my_active = to_boolean($plan_status);
  1620. $where .= " AND active = " . $my_active;
  1621. }
  1622. if(!is_null($tplan2exclude))
  1623. {
  1624. $where .= " AND TPLAN.id != {$tplan2exclude} ";
  1625. }
  1626. }
  1627. else
  1628. {
  1629. $where .= ")";
  1630. }
  1631. $sql .= $where . " ORDER BY name";
  1632. if( $forHMLSelect )
  1633. {
  1634. $map = $this->db->fetchColumnsIntoMap($sql,'id','name');
  1635. }
  1636. else
  1637. {
  1638. $map = $this->db->fetchRowsIntoMap($sql,'id');
  1639. }
  1640. return($map);
  1641. }
  1642. /*
  1643. function: check_tplan_name_existence
  1644. args :
  1645. tproject_id:
  1646. tplan_id:
  1647. [case_sensitive]: 1-> do case sensitive search
  1648. default: 0
  1649. returns: 1 -> tplan name exists
  1650. */
  1651. function check_tplan_name_existence($tproject_id,$tplan_name,$case_sensitive=0)
  1652. {
  1653. $sql = " SELECT NH.id, NH.name, testproject_id " .
  1654. " FROM {$this->tables['nodes_hierarchy']} NH, {$this->tables['testplans']} testplans " .
  1655. " WHERE NH.id=testplans.id " .
  1656. " AND testproject_id = {$tproject_id} ";
  1657. if($case_sensitive)
  1658. {
  1659. $sql .= " AND NH.name=";
  1660. }
  1661. else
  1662. {
  1663. $tplan_name=strtoupper($tplan_name);
  1664. $sql .= " AND UPPER(NH.name)=";
  1665. }
  1666. $sql .= "'" . $this->db->prepare_string($tplan_name) . "'";
  1667. $result = $this->db->exec_query($sql);
  1668. $status= $this->db->num_rows($result) ? 1 : 0;
  1669. return $status;
  1670. }
  1671. /*
  1672. function: gen_combo_first_level_test_suites
  1673. create array with test suite names
  1674. args : id: testproject_id
  1675. [mode]
  1676. returns:
  1677. array, every element is a map
  1678. rev :
  1679. 20070219 - franciscom
  1680. fixed bug when there are no children
  1681. */
  1682. function get_first_level_test_suites($tproject_id,$mode='simple')
  1683. {
  1684. $fl=$this->tree_manager->get_children($tproject_id,
  1685. array( 'testcase', 'exclude_me',
  1686. 'testplan' => 'exclude_me',
  1687. 'requirement_spec' => 'exclude_me' ));
  1688. switch ($mode)
  1689. {
  1690. case 'simple':
  1691. break;
  1692. case 'smarty_html_options':
  1693. if( !is_null($fl) && count($fl) > 0)
  1694. {
  1695. foreach($fl as $idx => $map)
  1696. {
  1697. $dummy[$map['id']]=$map['name'];
  1698. }
  1699. $fl=null;
  1700. $fl=$dummy;
  1701. }
  1702. break;
  1703. }
  1704. return($fl);
  1705. }
  1706. /**
  1707. * getTCasesLinkedToAnyTPlan
  1708. *
  1709. * for target test project id ($id) get test case id of
  1710. * every test case that has been assigned at least to one of all test plans
  1711. * belonging to test project.
  1712. *
  1713. * @param int $id test project id
  1714. *
  1715. */
  1716. function getTCasesLinkedToAnyTPlan($id)
  1717. {
  1718. $tplanNodeType = $this->tree_manager->node_descr_id['testplan'];
  1719. // len of lines must be <= 100/110 as stated on development standard guide.
  1720. $sql = " SELECT DISTINCT NHA.parent_id AS testcase_id " .
  1721. " FROM {$this->tables['nodes_hierarchy']} NHA " .
  1722. " JOIN {$this->tables['testplan_tcversions']} ON NHA.id = tcversion_id ";
  1723. // get testplan id for target test???project, to get test case versions linked to testplan.
  1724. $sql .= " JOIN {$this->tables['nodes_hierarchy']} NH ON testplan_id = NH.id " .
  1725. " WHERE NH.node_type_id = {$tplanNodeType} AND NH.parent_id ={$id}";
  1726. $rs = $this->db->fetchRowsIntoMap($sql,'testcase_id');
  1727. return $rs;
  1728. }
  1729. /**
  1730. * getFreeTestCases
  1731. *
  1732. *
  1733. * @param int $id test project id
  1734. * @param $options for future uses.
  1735. */
  1736. function getFreeTestCases($id,$options=null)
  1737. {
  1738. $retval['items']=null;
  1739. $retval['allfree']=false;
  1740. // @TODO here there is a problem $all is undefined!!!
  1741. $all=array();
  1742. $this->get_all_testcases_id($id,$all);
  1743. $linked=array();
  1744. $free=null;
  1745. if(!is_null($all))
  1746. {
  1747. $all=array_flip($all);
  1748. $linked=$this->getTCasesLinkedToAnyTPlan($id);
  1749. $retval['allfree']=is_null($linked);
  1750. $free=$retval['allfree'] ? $all : array_diff_key($all,$linked);
  1751. }
  1752. if( !is_null($free) && count($free) > 0)
  1753. {
  1754. $in_clause=implode(',',array_keys($free));
  1755. $sql = " SELECT MAX(TCV.version) AS version, TCV.tc_external_id, " .
  1756. " NHA.parent_id AS id, NHB.name " .
  1757. " FROM {$this->tables['tcversions']} TCV,{$this->tables['nodes_hierarchy']} NHA, " .
  1758. " {$this->tables['nodes_hierarchy']} NHB " .
  1759. " WHERE NHA.parent_id IN ({$in_clause}) " .
  1760. " AND TCV.id = NHA.id " .
  1761. " AND NHB.id = NHA.parent_id " .
  1762. " GROUP BY NHB.name,NHA.parent_id,TCV.tc_external_id " .
  1763. " ORDER BY NHA.parent_id";
  1764. $retval['items']=$this->db->fetchRowsIntoMap($sql,'id');
  1765. }
  1766. return $retval;
  1767. }
  1768. // -------------------------------------------------------------------------------
  1769. // Custom field related methods
  1770. // -------------------------------------------------------------------------------
  1771. /*
  1772. function: get_linked_custom_fields
  1773. Get custom fields that has been linked to testproject.
  1774. Search can be narrowed by:
  1775. node type
  1776. node id
  1777. Important:
  1778. custom fields id will be sorted based on the sequence number
  1779. that can be specified at User Interface (UI) level, while
  1780. linking is done.
  1781. args : id: testproject id
  1782. [node_type]: default: null -> no filter
  1783. verbose string that identifies a node type.
  1784. (see tree class, method get_available_node_types).
  1785. Example:
  1786. You want linked custom fields , but can be used
  1787. only on testcase -> 'testcase'.
  1788. returns: map.
  1789. key: custom field id
  1790. value: map (custom field definition) with following keys
  1791. id (custom field id)
  1792. name
  1793. label
  1794. type
  1795. possible_values
  1796. default_value
  1797. valid_regexp
  1798. length_min
  1799. length_max
  1800. show_on_design
  1801. enable_on_design
  1802. show_on_execution
  1803. enable_on_execution
  1804. display_order
  1805. */
  1806. function get_linked_custom_fields($id,$node_type=null,$access_key='id')
  1807. {
  1808. $additional_table="";
  1809. $additional_join="";
  1810. if( !is_null($node_type) )
  1811. {
  1812. $hash_descr_id = $this->tree_manager->get_available_node_types();
  1813. $node_type_id=$hash_descr_id[$node_type];
  1814. $additional_table=",{$this->tables['cfield_node_types']} CFNT ";
  1815. $additional_join=" AND CFNT.field_id=CF.id AND CFNT.node_type_id={$node_type_id} ";
  1816. }
  1817. $sql="SELECT CF.*,CFTP.display_order " .
  1818. " FROM {$this->tables['custom_fields']} CF, {$this->tables['cfield_testprojects']} CFTP " .
  1819. $additional_table .
  1820. " WHERE CF.id=CFTP.field_id " .
  1821. " AND CFTP.testproject_id={$id} " .
  1822. $additional_join .
  1823. " ORDER BY CFTP.display_order";
  1824. $map = $this->db->fetchRowsIntoMap($sql,$access_key);
  1825. return($map);
  1826. }
  1827. /*
  1828. function: copy_as
  1829. creates a new test project using an existent one as source.
  1830. args: id: source testproject id
  1831. new_id: destination
  1832. [new_name]: default null.
  1833. != null => set this as the new name
  1834. [copy_options]: default null
  1835. null: do a deep copy => copy following child elements:
  1836. test plans
  1837. builds
  1838. linked tcversions
  1839. milestones
  1840. user_roles
  1841. priorities,
  1842. platforms
  1843. execution assignment.
  1844. != null, a map with keys that controls what child elements to copy
  1845. returns: N/A
  1846. */
  1847. function copy_as($id,$new_id,$user_id,$new_name=null,$options=null)
  1848. {
  1849. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  1850. $my['options'] = array('copy_requirements' => 1,'copy_user_roles' => 1,'copy_platforms' => 1);
  1851. $my['options'] = array_merge($my['options'], (array)$options);
  1852. // get source test project general info
  1853. $rs_source=$this->get_by_id($id);
  1854. if(!is_null($new_name))
  1855. {
  1856. $sql="/* $debugMsg */ UPDATE {$this->tables['nodes_hierarchy']} " .
  1857. "SET name='" . $this->db->prepare_string(trim($new_name)) . "' " .
  1858. "WHERE id={$new_id}";
  1859. $this->db->exec_query($sql);
  1860. }
  1861. // Copy elements that can be used by other elements
  1862. // Custom Field assignments
  1863. $this->copy_cfields_assignments($id,$new_id);
  1864. // Keywords
  1865. $oldNewMappings['keywords'] = $this->copy_keywords($id,$new_id);
  1866. // Platforms
  1867. $oldNewMappings['platforms'] = $this->copy_platforms($id,$new_id);
  1868. // Requirements
  1869. if( $my['options']['copy_requirements'] )
  1870. {
  1871. $oldNewMappings['requirements'] = $this->copy_requirements($id,$new_id);
  1872. }
  1873. // need to get subtree and create a new one
  1874. $filters = array();
  1875. $filters['exclude_node_types'] = array('testplan' => 'exclude_me','requirement_spec' => 'exclude_me');
  1876. $filters['exclude_children_of'] = array('testcase' => 'exclude_me', 'requirement' => 'exclude_me',
  1877. 'testcase_step' => 'exclude_me');
  1878. $elements = $this->tree_manager->get_children($id,$filters['exclude_node_types']);
  1879. // Copy Test Specification
  1880. $item_mgr['testsuites'] = new testsuite($this->db);
  1881. $copyTSuiteOpt = array();
  1882. $copyTSuiteOpt['copyKeywords'] = 1;
  1883. $copyTSuiteOpt['copyRequirements'] = $my['options']['copy_requirements'];
  1884. $oldNewMappings['test_spec'] = array();
  1885. foreach($elements as $piece)
  1886. {
  1887. $op = $item_mgr['testsuites']->copy_to($piece['id'],$new_id,$user_id,$copyTSuiteOpt);
  1888. $oldNewMappings['test_spec'] += $op['mappings'];
  1889. }
  1890. // Copy Test Plans and all related information
  1891. $this->copy_testplans($id,$new_id,$user_id,$oldNewMappings);
  1892. $this->copy_user_roles($id,$new_id);
  1893. } // end function copy_as
  1894. /**
  1895. * function to get an array with all requirement IDs in testproject
  1896. *
  1897. * @param string $IDList commaseparated list of Container-IDs - can be testproject ID or reqspec IDs
  1898. * @return array $reqIDs result IDs
  1899. *
  1900. * @internal revisions:
  1901. * 20100310 - asimon - removed recursion logic
  1902. */
  1903. public function get_all_requirement_ids($IDList) {
  1904. $coupleTypes = array();
  1905. $coupleTypes['target'] = $this->tree_manager->node_descr_id['requirement'];
  1906. $coupleTypes['container'] = $this->tree_manager->node_descr_id['requirement_spec'];
  1907. $reqIDs = array();
  1908. $this->tree_manager->getAllItemsID($IDList,$reqIDs,$coupleTypes);
  1909. return $reqIDs;
  1910. }
  1911. /**
  1912. * uses get_all_requirements_ids() to count all requirements in testproject
  1913. *
  1914. * @param integer $tp_id ID of testproject
  1915. * @return integer count of requirements in given testproject
  1916. */
  1917. public function count_all_requirements($tp_id) {
  1918. return count($this->get_all_requirement_ids($tp_id));
  1919. }
  1920. /**
  1921. * Copy user roles to a new Test Project
  1922. *
  1923. * @param int $source_id original Test Project identificator
  1924. * @param int $target_id new Test Project identificator
  1925. */
  1926. private function copy_user_roles($source_id, $target_id)
  1927. {
  1928. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  1929. $sql = "/* $debugMsg */ SELECT * FROM {$this->tables['user_testproject_roles']} " .
  1930. "WHERE testproject_id={$source_id} ";
  1931. $rs=$this->db->get_recordset($sql);
  1932. if(!is_null($rs))
  1933. {
  1934. foreach($rs as $elem)
  1935. {
  1936. $sql="/* $debugMsg */ INSERT INTO {$this->tables['user_testproject_roles']} " .
  1937. "(testproject_id,user_id,role_id) " .
  1938. "VALUES({$target_id}," . $elem['user_id'] ."," . $elem['role_id'] . ")";
  1939. $this->db->exec_query($sql);
  1940. }
  1941. }
  1942. }
  1943. /**
  1944. * Copy platforms
  1945. *
  1946. * @param int $source_id original Test Project identificator
  1947. * @param int $target_id new Test Project identificator
  1948. */
  1949. private function copy_platforms($source_id, $target_id)
  1950. {
  1951. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  1952. $platform_mgr = new tlPlatform($this->db,$source_id);
  1953. $old_new = null;
  1954. $platformSet = $platform_mgr->getAll();
  1955. if( !is_null($platformSet) )
  1956. {
  1957. $platform_mgr->setTestProjectID($target_id);
  1958. foreach($platformSet as $platform)
  1959. {
  1960. $op = $platform_mgr->create($platform['name'],$platform['notes']);
  1961. $old_new[$platform['id']] = $op['id'];
  1962. }
  1963. }
  1964. return $old_new;
  1965. }
  1966. /**
  1967. * Copy platforms
  1968. *
  1969. * @param int $source_id original Test Project identificator
  1970. * @param int $target_id new Test Project identificator
  1971. */
  1972. private function copy_keywords($source_id, $target_id)
  1973. {
  1974. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  1975. $old_new = null;
  1976. $sql = "/* $debugMsg */ SELECT * FROM {$this->tables['keywords']} " .
  1977. " WHERE testproject_id = {$source_id}";
  1978. $itemSet = $this->db->fetchRowsIntoMap($sql,'id');
  1979. if( !is_null($itemSet) )
  1980. {
  1981. foreach($itemSet as $item)
  1982. {
  1983. $op = $this->addKeyword($target_id,$item['keyword'],$item['notes']);
  1984. $old_new[$item['id']] = $op['id'];
  1985. }
  1986. }
  1987. return $old_new;
  1988. }
  1989. /**
  1990. *
  1991. *
  1992. */
  1993. private function copy_cfields_assignments($source_id, $target_id)
  1994. {
  1995. $debugMsg = 'Class:' . __CLASS__ . ' - Method: ' . __FUNCTION__;
  1996. $sql = "/* $debugMsg */ " .
  1997. " SELECT field_id FROM {$this->tables['cfield_testprojects']} " .
  1998. " WHERE testproject_id = {$source_id}";
  1999. $row_set = $this->db->fetchRowsIntoMap($sql,'field_id');
  2000. if( !is_null($row_set) )
  2001. {
  2002. $cfield_set = array_keys($row_set);
  2003. $this->cfield_mgr->link_to_testproject($target_id,$cfield_set);
  2004. }
  2005. }
  2006. /**
  2007. *
  2008. *
  2009. */
  2010. private function copy_testplans($source_id,$target_id,$user_id,$mappings)
  2011. {
  2012. static $tplanMgr;
  2013. $tplanSet = $this->get_all_testplans($source_id);
  2014. if( !is_null($tplanSet) )
  2015. {
  2016. $keySet = array_keys($tplanSet);
  2017. if( is_null($tplanMgr) )
  2018. {
  2019. $tplanMgr = new testplan($this->db);
  2020. }
  2021. foreach($keySet as $itemID)
  2022. {
  2023. $new_id = $tplanMgr->create($tplanSet[$itemID]['name'],$tplanSet[$itemID]['notes'],
  2024. $target_id,$tplanSet[$itemID]['active'],$tplanSet[$itemID]['is_public']);
  2025. if( $new_id > 0 )
  2026. {
  2027. $tplanMgr->copy_as($itemID,$new_id,null,$target_id,$user_id,null,$mappings);
  2028. }
  2029. }
  2030. }
  2031. }
  2032. /**
  2033. *
  2034. *
  2035. */
  2036. private function copy_requirements($source_id,$target_id,$user_id)
  2037. {
  2038. $mappings = null;
  2039. // need to get subtree and create a new one
  2040. $filters = array();
  2041. $filters['exclude_node_types'] = array('testplan' => 'exclude','testcase' => 'exclude',
  2042. 'testsuite' => 'exclude','requirement' => 'exclude');
  2043. $elements = $this->tree_manager->get_children($source_id,$filters['exclude_node_types']);
  2044. if( !is_null($elements) )
  2045. {
  2046. $mappings = array();
  2047. $reqSpecMgr = new requirement_spec_mgr($this->db);
  2048. $options = array('copy_also' => array('testcase_assignments' => false) );
  2049. foreach($elements as $piece)
  2050. {
  2051. // function copy_to($id, $parent_id, $tproject_id, $user_id,$options = null)
  2052. $op = $reqSpecMgr->copy_to($piece['id'],$target_id,$target_id,$user_id,$options);
  2053. $mappings += $op['mappings'];
  2054. }
  2055. }
  2056. return $mappings;
  2057. }
  2058. } // end class
  2059. ?>