PageRenderTime 50ms CodeModel.GetById 17ms 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

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. * @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-…

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