PageRenderTime 63ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 1ms

/MantisBT/core/relationship_api.php

https://bitbucket.org/crypticrod/sr_wp_code
PHP | 864 lines | 520 code | 113 blank | 231 comment | 92 complexity | b32afbef3c2ff57231c578ad65a0fd7f MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, LGPL-2.1, GPL-3.0, LGPL-2.0, AGPL-3.0
  1. <?php
  2. # MantisBT - a php based bugtracking system
  3. # MantisBT is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, either version 2 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # MantisBT is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with MantisBT. If not, see <http://www.gnu.org/licenses/>.
  15. /**
  16. * Relationship API
  17. *
  18. * RELATIONSHIP DEFINITIONS
  19. * * Child/parent relationship:
  20. * the child bug is generated by the parent bug or is directly linked with the parent with the following meaning
  21. * the child bug has to be resolved before resolving the parent bug (the child bug "blocks" the parent bug)
  22. * example: bug A is child bug of bug B. It means: A blocks B and B is blocked by A
  23. * * General relationship:
  24. * two bugs related each other without any hierarchy dependance
  25. * bugs A and B are related
  26. * * Duplicates:
  27. * it's used to mark a bug as duplicate of an other bug already stored in the database
  28. * bug A is marked as duplicate of B. It means: A duplicates B, B has duplicates
  29. *
  30. * Relations are always visible in the email body
  31. * --------------------------------------------------------------------
  32. * ADD NEW RELATIONSHIP
  33. * - Permission: user can update the source bug and at least view the destination bug
  34. * - Action recorded in the history of both the bugs
  35. * - Email notification sent to the users of both the bugs based based on the 'updated' bug notify type.
  36. * --------------------------------------------------------
  37. * DELETE RELATIONSHIP
  38. * - Permission: user can update the source bug and at least view the destination bug
  39. * - Action recorded in the history of both the bugs
  40. * - Email notification sent to the users of both the bugs based based on the 'updated' bug notify type.
  41. * --------------------------------------------------------
  42. * RESOLVE/CLOSE BUGS WITH BLOCKING CHILD BUGS STILL OPEN
  43. * Just a warning is print out on the form when an user attempts to resolve or close a bug with
  44. * related bugs in relation BUG_DEPENDANT still not resolved.
  45. * Anyway the user can force the resolving/closing action.
  46. * --------------------------------------------------------
  47. * EMAIL NOTIFICATION TO PARENT BUGS WHEN CHILDREN BUGS ARE RESOLVED/CLOSED
  48. * Every time a child bug is resolved or closed, an email notification is sent directly to all the handlers
  49. * of the parent bugs. The notification is sent to bugs not already marked as resolved or closed.
  50. * --------------------------------------------------------
  51. * ADD CHILD
  52. * This function gives the opportunity to generate a child bug. In details the function:
  53. * - create a new bug with the same basic information of the parent bug (plus the custom fields)
  54. * - copy all the attachment of the parent bug to the child
  55. * - not copy history, bugnotes, monitoring users
  56. * - set a relationship between parent and child
  57. *
  58. * @package CoreAPI
  59. * @subpackage RelationshipAPI
  60. * @author Marcello Scata' <marcelloscata at users.sourceforge.net> ITALY
  61. * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
  62. * @copyright Copyright (C) 2002 - 2011 MantisBT Team - mantisbt-dev@lists.sourceforge.net
  63. * @link http://www.mantisbt.org
  64. */
  65. /**
  66. * requires collapse_api
  67. */
  68. require_once( 'collapse_api.php' );
  69. /**
  70. * RelationshipData Structure Definition
  71. * @package MantisBT
  72. * @subpackage classes
  73. */
  74. class BugRelationshipData {
  75. var $id;
  76. var $src_bug_id;
  77. var $src_project_id;
  78. var $dest_bug_id;
  79. var $dest_project_id;
  80. var $type;
  81. }
  82. $g_relationships = array();
  83. $g_relationships[BUG_DEPENDANT] = array(
  84. '#forward' => TRUE,
  85. '#complementary' => BUG_BLOCKS,
  86. '#description' => 'dependant_on',
  87. '#notify_added' => 'email_notification_title_for_action_dependant_on_relationship_added',
  88. '#notify_deleted' => 'email_notification_title_for_action_dependant_on_relationship_deleted',
  89. '#edge_style' => array(
  90. 'color' => '#C00000',
  91. 'dir' => 'back',
  92. ),
  93. );
  94. $g_relationships[BUG_BLOCKS] = array(
  95. '#forward' => FALSE,
  96. '#complementary' => BUG_DEPENDANT,
  97. '#description' => 'blocks',
  98. '#notify_added' => 'email_notification_title_for_action_blocks_relationship_added',
  99. '#notify_deleted' => 'email_notification_title_for_action_blocks_relationship_deleted',
  100. '#edge_style' => array(
  101. 'color' => '#C00000',
  102. 'dir' => 'forward',
  103. ),
  104. );
  105. $g_relationships[BUG_DUPLICATE] = array(
  106. '#forward' => TRUE,
  107. '#complementary' => BUG_HAS_DUPLICATE,
  108. '#description' => 'duplicate_of',
  109. '#notify_added' => 'email_notification_title_for_action_duplicate_of_relationship_added',
  110. '#notify_deleted' => 'email_notification_title_for_action_duplicate_of_relationship_deleted',
  111. '#edge_style' => array(
  112. 'style' => 'dashed',
  113. 'color' => '#808080',
  114. ),
  115. );
  116. $g_relationships[BUG_HAS_DUPLICATE] = array(
  117. '#forward' => FALSE,
  118. '#complementary' => BUG_DUPLICATE,
  119. '#description' => 'has_duplicate',
  120. '#notify_added' => 'email_notification_title_for_action_has_duplicate_relationship_added',
  121. '#notify_deleted' => 'email_notification_title_for_action_has_duplicate_relationship_deleted',
  122. );
  123. $g_relationships[BUG_RELATED] = array(
  124. '#forward' => TRUE,
  125. '#complementary' => BUG_RELATED,
  126. '#description' => 'related_to',
  127. '#notify_added' => 'email_notification_title_for_action_related_to_relationship_added',
  128. '#notify_deleted' => 'email_notification_title_for_action_related_to_relationship_deleted',
  129. );
  130. if( file_exists( dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'custom_relationships_inc.php' ) ) {
  131. require_once( dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'custom_relationships_inc.php' );
  132. }
  133. /**
  134. * Return the complementary type of the provided relationship
  135. * @param int $p_relationship_type Relationship type
  136. * @return int Complementary type
  137. */
  138. function relationship_get_complementary_type( $p_relationship_type ) {
  139. global $g_relationships;
  140. if( !isset( $g_relationships[$p_relationship_type] ) ) {
  141. trigger_error( ERROR_GENERIC, ERROR );
  142. }
  143. return $g_relationships[$p_relationship_type]['#complementary'];
  144. }
  145. /**
  146. * Add a new relationship
  147. * @param int $p_src_bug_id Source Bug Id
  148. * @param int $p_dest_bug_id Destination Bug Id
  149. * @param int $p_relationship_type Relationship type
  150. * @return BugRelationshipData Bug Relationship
  151. */
  152. function relationship_add( $p_src_bug_id, $p_dest_bug_id, $p_relationship_type ) {
  153. $t_mantis_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' );
  154. global $g_relationships;
  155. if( $g_relationships[$p_relationship_type]['#forward'] === FALSE ) {
  156. $c_src_bug_id = db_prepare_int( $p_dest_bug_id );
  157. $c_dest_bug_id = db_prepare_int( $p_src_bug_id );
  158. $c_relationship_type = db_prepare_int( relationship_get_complementary_type( $p_relationship_type ) );
  159. } else {
  160. $c_src_bug_id = db_prepare_int( $p_src_bug_id );
  161. $c_dest_bug_id = db_prepare_int( $p_dest_bug_id );
  162. $c_relationship_type = db_prepare_int( $p_relationship_type );
  163. }
  164. $query = "INSERT INTO $t_mantis_bug_relationship_table
  165. ( source_bug_id, destination_bug_id, relationship_type )
  166. VALUES
  167. ( " . db_param() . ',' . db_param() . ',' . db_param() . ')';
  168. $result = db_query_bound( $query, Array( $c_src_bug_id, $c_dest_bug_id, $c_relationship_type ) );
  169. $t_relationship = db_fetch_array( $result );
  170. $t_bug_relationship_data = new BugRelationshipData;
  171. $t_bug_relationship_data->id = $t_relationship['id'];
  172. $t_bug_relationship_data->src_bug_id = $t_relationship['source_bug_id'];
  173. $t_bug_relationship_data->dest_bug_id = $t_relationship['destination_bug_id'];
  174. $t_bug_relationship_data->type = $t_relationship['relationship_type'];
  175. return $t_bug_relationship_data;
  176. }
  177. /**
  178. * Update a relationship
  179. * @param int $p_relationship_id Relationship Id to update
  180. * @param int $p_src_bug_id Source Bug Id
  181. * @param int $p_dest_bug_id Destination Bug Id
  182. * @param int $p_relationship_type Relationship type
  183. * @return BugRelationshipData Bug Relationship
  184. */
  185. function relationship_update( $p_relationship_id, $p_src_bug_id, $p_dest_bug_id, $p_relationship_type ) {
  186. $t_mantis_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' );
  187. global $g_relationships;
  188. if( $g_relationships[$p_relationship_type]['#forward'] === FALSE ) {
  189. $c_src_bug_id = db_prepare_int( $p_dest_bug_id );
  190. $c_dest_bug_id = db_prepare_int( $p_src_bug_id );
  191. $c_relationship_type = db_prepare_int( relationship_get_complementary_type( $p_relationship_type ) );
  192. } else {
  193. $c_src_bug_id = db_prepare_int( $p_src_bug_id );
  194. $c_dest_bug_id = db_prepare_int( $p_dest_bug_id );
  195. $c_relationship_type = db_prepare_int( $p_relationship_type );
  196. }
  197. $c_relationship_id = db_prepare_int( $p_relationship_id );
  198. $query = "UPDATE $t_mantis_bug_relationship_table
  199. SET source_bug_id=" . db_param() . ",
  200. destination_bug_id=" . db_param() . ",
  201. relationship_type=" . db_param() . "
  202. WHERE id=" . db_param();
  203. $result = db_query_bound( $query, array( $c_src_bug_id, $c_dest_bug_id, $c_relationship_type, $c_relationship_id ) );
  204. $t_relationship = db_fetch_array( $result );
  205. $t_bug_relationship_data = new BugRelationshipData;
  206. $t_bug_relationship_data->id = $t_relationship['id'];
  207. $t_bug_relationship_data->src_bug_id = $t_relationship['source_bug_id'];
  208. $t_bug_relationship_data->dest_bug_id = $t_relationship['destination_bug_id'];
  209. $t_bug_relationship_data->type = $t_relationship['relationship_type'];
  210. return $t_bug_relationship_data;
  211. }
  212. /**
  213. * Delete a relationship
  214. * @param int $p_relationship_id Relationship Id to update
  215. */
  216. function relationship_delete( $p_relationship_id ) {
  217. $c_relationship_id = db_prepare_int( $p_relationship_id );
  218. $t_mantis_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' );
  219. $query = "DELETE FROM $t_mantis_bug_relationship_table
  220. WHERE id=" . db_param();
  221. $result = db_query_bound( $query, array( $c_relationship_id ) );
  222. }
  223. /**
  224. * Deletes all the relationships related to a specific bug (both source and destination)
  225. * @param int $p_bug_id Bug Id
  226. */
  227. function relationship_delete_all( $p_bug_id ) {
  228. $c_bug_id = db_prepare_int( $p_bug_id );
  229. $t_mantis_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' );
  230. $query = "DELETE FROM $t_mantis_bug_relationship_table
  231. WHERE source_bug_id=" . db_param() . " OR
  232. destination_bug_id=" . db_param();
  233. $result = db_query_bound( $query, array( $c_bug_id, $c_bug_id ) );
  234. }
  235. /**
  236. * copy all the relationships related to a specific bug to a new bug
  237. * @param int $p_bug_id Source Bug Id
  238. * @param int $p_new_bug_id Destination Bug Id
  239. */
  240. function relationship_copy_all( $p_bug_id, $p_new_bug_id ) {
  241. $c_bug_id = db_prepare_int( $p_bug_id );
  242. $c_new_bug_id = db_prepare_int( $p_new_bug_id );
  243. $t_mantis_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' );
  244. $t_relationship = relationship_get_all_src( $p_bug_id );
  245. $t_relationship_count = count( $t_relationship );
  246. for( $i = 0;$i < $t_relationship_count;$i++ ) {
  247. relationship_add( $p_new_bug_id, $t_relationship[$i]->dest_bug_id, $t_relationship[$i]->type );
  248. }
  249. $t_relationship = relationship_get_all_dest( $p_bug_id );
  250. $t_relationship_count = count( $t_relationship );
  251. for( $i = 0;$i < $t_relationship_count;$i++ ) {
  252. relationship_add( $t_relationship[$i]->src_bug_id, $p_new_bug_id, $t_relationship[$i]->type );
  253. }
  254. return;
  255. }
  256. /**
  257. * get a relationship from id
  258. * @param int $p_relationship_id Relationship ID
  259. * @return null|BugRelationshipData BugRelationshipData object
  260. */
  261. function relationship_get( $p_relationship_id ) {
  262. $t_mantis_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' );
  263. $query = "SELECT *
  264. FROM $t_mantis_bug_relationship_table
  265. WHERE id=" . db_param();
  266. $result = db_query_bound( $query, array( (int) $p_relationship_id ) );
  267. $t_relationship_count = db_num_rows( $result );
  268. if( $t_relationship_count == 1 ) {
  269. $t_relationship = db_fetch_array( $result );
  270. $t_bug_relationship_data = new BugRelationshipData;
  271. $t_bug_relationship_data->id = $t_relationship['id'];
  272. $t_bug_relationship_data->src_bug_id = $t_relationship['source_bug_id'];
  273. $t_bug_relationship_data->dest_bug_id = $t_relationship['destination_bug_id'];
  274. $t_bug_relationship_data->type = $t_relationship['relationship_type'];
  275. } else {
  276. $t_bug_relationship_data = null;
  277. }
  278. return $t_bug_relationship_data;
  279. }
  280. /**
  281. * get all relationships with the given bug as source
  282. * @param int $p_src_bug_id Source Bug id
  283. * @return array Array of BugRelationshipData objects
  284. */
  285. function relationship_get_all_src( $p_src_bug_id ) {
  286. $c_src_bug_id = db_prepare_int( $p_src_bug_id );
  287. $t_mantis_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' );
  288. $t_mantis_bug_table = db_get_table( 'mantis_bug_table' );
  289. $query = "SELECT $t_mantis_bug_relationship_table.id, $t_mantis_bug_relationship_table.relationship_type,
  290. $t_mantis_bug_relationship_table.source_bug_id, $t_mantis_bug_relationship_table.destination_bug_id,
  291. $t_mantis_bug_table.project_id
  292. FROM $t_mantis_bug_relationship_table
  293. INNER JOIN $t_mantis_bug_table ON $t_mantis_bug_relationship_table.destination_bug_id = $t_mantis_bug_table.id
  294. WHERE source_bug_id=" . db_param() . "
  295. ORDER BY relationship_type, $t_mantis_bug_relationship_table.id";
  296. $result = db_query_bound( $query, array( $c_src_bug_id ) );
  297. $t_src_project_id = bug_get_field( $p_src_bug_id, 'project_id' );
  298. $t_bug_relationship_data = array();
  299. $t_relationship_count = db_num_rows( $result );
  300. $t_bug_array = Array();
  301. for( $i = 0;$i < $t_relationship_count;$i++ ) {
  302. $row = db_fetch_array( $result );
  303. $t_bug_relationship_data[$i] = new BugRelationshipData;
  304. $t_bug_relationship_data[$i]->id = $row['id'];
  305. $t_bug_relationship_data[$i]->src_bug_id = $row['source_bug_id'];
  306. $t_bug_relationship_data[$i]->src_project_id = $t_src_project_id;
  307. $t_bug_relationship_data[$i]->dest_bug_id = $row['destination_bug_id'];
  308. $t_bug_relationship_data[$i]->dest_project_id = $row['project_id'];
  309. $t_bug_relationship_data[$i]->type = $row['relationship_type'];
  310. $t_bug_array[] = $row['destination_bug_id'];
  311. }
  312. unset( $t_bug_relationship_data[$t_relationship_count] );
  313. if( !empty( $t_bug_array ) ) {
  314. bug_cache_array_rows( $t_bug_array );
  315. }
  316. return $t_bug_relationship_data;
  317. }
  318. /**
  319. * get all relationships with the given bug as destination
  320. * @param int $p_dest_bug_id Destination Bug id
  321. * @return array Array of BugRelationshipData objects
  322. */
  323. function relationship_get_all_dest( $p_dest_bug_id ) {
  324. $c_dest_bug_id = db_prepare_int( $p_dest_bug_id );
  325. $t_mantis_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' );
  326. $t_mantis_bug_table = db_get_table( 'mantis_bug_table' );
  327. $query = "SELECT $t_mantis_bug_relationship_table.id, $t_mantis_bug_relationship_table.relationship_type,
  328. $t_mantis_bug_relationship_table.source_bug_id, $t_mantis_bug_relationship_table.destination_bug_id,
  329. $t_mantis_bug_table.project_id
  330. FROM $t_mantis_bug_relationship_table
  331. INNER JOIN $t_mantis_bug_table ON $t_mantis_bug_relationship_table.source_bug_id = $t_mantis_bug_table.id
  332. WHERE destination_bug_id=" . db_param() . "
  333. ORDER BY relationship_type, $t_mantis_bug_relationship_table.id";
  334. $result = db_query_bound( $query, Array( $c_dest_bug_id ) );
  335. $t_dest_project_id = bug_get_field( $p_dest_bug_id, 'project_id' );
  336. $t_bug_relationship_data = array();
  337. $t_relationship_count = db_num_rows( $result );
  338. $t_bug_array = Array();
  339. for( $i = 0;$i < $t_relationship_count;$i++ ) {
  340. $row = db_fetch_array( $result );
  341. $t_bug_relationship_data[$i] = new BugRelationshipData;
  342. $t_bug_relationship_data[$i]->id = $row['id'];
  343. $t_bug_relationship_data[$i]->src_bug_id = $row['source_bug_id'];
  344. $t_bug_relationship_data[$i]->src_project_id = $row['project_id'];
  345. $t_bug_relationship_data[$i]->dest_bug_id = $row['destination_bug_id'];
  346. $t_bug_relationship_data[$i]->dest_project_id = $t_dest_project_id;
  347. $t_bug_relationship_data[$i]->type = $row['relationship_type'];
  348. $t_bug_array[] = $row['source_bug_id'];
  349. }
  350. unset( $t_bug_relationship_data[$t_relationship_count] );
  351. if( !empty( $t_bug_array ) ) {
  352. bug_cache_array_rows( $t_bug_array );
  353. }
  354. return $t_bug_relationship_data;
  355. }
  356. /**
  357. * get all relationships associated with the given bug
  358. * @param int $p_bug_id Bug id
  359. * @param bool &$p_is_different_projects Returned Boolean value indicating if some relationships cross project boundaries
  360. * @return array Array of BugRelationshipData objects
  361. */
  362. function relationship_get_all( $p_bug_id, &$p_is_different_projects ) {
  363. $t_src = relationship_get_all_src( $p_bug_id );
  364. $t_dest = relationship_get_all_dest( $p_bug_id );
  365. $t_all = array_merge( $t_src, $t_dest );
  366. $p_is_different_projects = false;
  367. $t_count = count( $t_all );
  368. for( $i = 0;$i < $t_count;$i++ ) {
  369. $p_is_different_projects |= ( $t_all[$i]->src_project_id != $t_all[$i]->dest_project_id );
  370. }
  371. return $t_all;
  372. }
  373. /**
  374. * check if there is a relationship between two bugs
  375. * return id if found 0 otherwise
  376. * @param int $p_src_bug_id Source Bug Id
  377. * @param int $p_dest_bug_id Destination Bug Id
  378. * @return int Relationship ID
  379. */
  380. function relationship_exists( $p_src_bug_id, $p_dest_bug_id ) {
  381. $c_src_bug_id = db_prepare_int( $p_src_bug_id );
  382. $c_dest_bug_id = db_prepare_int( $p_dest_bug_id );
  383. $t_mantis_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' );
  384. $t_query = "SELECT *
  385. FROM $t_mantis_bug_relationship_table
  386. WHERE
  387. (source_bug_id=" . db_param() . "
  388. AND destination_bug_id=" . db_param() . ")
  389. OR
  390. (source_bug_id=" . db_param() . "
  391. AND destination_bug_id=" . db_param() . ')';
  392. $result = db_query_bound( $t_query, array( $c_src_bug_id, $c_dest_bug_id, $c_dest_bug_id, $c_src_bug_id ), 1 );
  393. $t_relationship_count = db_num_rows( $result );
  394. if( $t_relationship_count == 1 ) {
  395. # return the first id
  396. $row = db_fetch_array( $result );
  397. return $row['id'];
  398. } else {
  399. # no relationship found
  400. return 0;
  401. }
  402. }
  403. /**
  404. * check if there is a relationship between two bugs
  405. * return:
  406. * 0 if the relationship is not found
  407. * -1 if the relationship is found and it's of the same type $p_rel_type
  408. * id if the relationship is found and it's of a different time (this means it can be replaced with the new type $p_rel_type
  409. * @param int $p_src_bug_id Source Bug Id
  410. * @param int $p_dest_bug_id Destination Bug Id
  411. * @param int $p_rel_type Relationship Type
  412. * @return int 0, -1 or id
  413. */
  414. function relationship_same_type_exists( $p_src_bug_id, $p_dest_bug_id, $p_rel_type ) {
  415. # Check if there is already a relationship set between them
  416. $t_id_relationship = relationship_exists( $p_src_bug_id, $p_dest_bug_id );
  417. if( $t_id_relationship > 0 ) {
  418. # if there is...
  419. # get all the relationship info
  420. $t_relationship = relationship_get( $t_id_relationship );
  421. if( $t_relationship->src_bug_id == $p_src_bug_id && $t_relationship->dest_bug_id == $p_dest_bug_id ) {
  422. if( $t_relationship->type == $p_rel_type ) {
  423. $t_id_relationship = -1;
  424. }
  425. } else {
  426. if( $t_relationship->type == relationship_get_complementary_type( $p_rel_type ) ) {
  427. $t_id_relationship = -1;
  428. }
  429. }
  430. }
  431. return $t_id_relationship;
  432. }
  433. /**
  434. * retrieve the linked bug id of the relationship: provide src -> return dest; provide dest -> return src
  435. * @param int $p_relationship_id Relationship id
  436. * @param int $p_bug_id Bug Id
  437. * @return int Complementary bug id
  438. */
  439. function relationship_get_linked_bug_id( $p_relationship_id, $p_bug_id ) {
  440. $t_bug_relationship_data = relationship_get( $p_relationship_id );
  441. if( $t_bug_relationship_data->src_bug_id == $p_bug_id ) {
  442. return $t_bug_relationship_data->dest_bug_id;
  443. }
  444. if( $t_bug_relationship_data->dest_bug_id == $p_bug_id ) {
  445. return $t_bug_relationship_data->src_bug_id;
  446. }
  447. trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
  448. }
  449. /**
  450. * get class description of a relationship (source side)
  451. * @param int $p_relationship_type Relationship type
  452. * @return string Relationship description
  453. */
  454. function relationship_get_description_src_side( $p_relationship_type ) {
  455. global $g_relationships;
  456. if( !isset( $g_relationships[$p_relationship_type] ) ) {
  457. trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
  458. }
  459. return lang_get( $g_relationships[$p_relationship_type]['#description'] );
  460. }
  461. /**
  462. * get class description of a relationship (destination side)
  463. * @param int $p_relationship_type Relationship type
  464. * @return string Relationship description
  465. */
  466. function relationship_get_description_dest_side( $p_relationship_type ) {
  467. global $g_relationships;
  468. if( !isset( $g_relationships[$p_relationship_type] ) || !isset( $g_relationships[$g_relationships[$p_relationship_type]['#complementary']] ) ) {
  469. trigger_error( ERROR_RELATIONSHIP_NOT_FOUND, ERROR );
  470. }
  471. return lang_get( $g_relationships[$g_relationships[$p_relationship_type]['#complementary']]['#description'] );
  472. }
  473. /**
  474. * get class description of a relationship as it's stored in the history
  475. * @param int $p_relationship_code Relationship Type
  476. * @return string Relationship description
  477. */
  478. function relationship_get_description_for_history( $p_relationship_code ) {
  479. return relationship_get_description_src_side( $p_relationship_code );
  480. }
  481. /**
  482. * return false if there are child bugs not resolved/closed
  483. * N.B. we don't check if the parent bug is read-only. This is because the answer of this function is indepedent from
  484. * the state of the parent bug itself.
  485. * @param int $p_bug_id Bug id
  486. * @return bool
  487. */
  488. function relationship_can_resolve_bug( $p_bug_id ) {
  489. # retrieve all the relationships in which the bug is the source bug
  490. $t_relationship = relationship_get_all_src( $p_bug_id );
  491. $t_relationship_count = count( $t_relationship );
  492. if( $t_relationship_count == 0 ) {
  493. return true;
  494. }
  495. for( $i = 0;$i < $t_relationship_count;$i++ ) {
  496. # verify if each bug in relation BUG_DEPENDANT is already marked as resolved
  497. if( $t_relationship[$i]->type == BUG_DEPENDANT ) {
  498. $t_dest_bug_id = $t_relationship[$i]->dest_bug_id;
  499. $t_status = bug_get_field( $t_dest_bug_id, 'status' );
  500. if( $t_status < config_get( 'bug_resolved_status_threshold' ) ) {
  501. # the bug is NOT marked as resolved/closed
  502. return false;
  503. }
  504. }
  505. }
  506. return true;
  507. }
  508. /**
  509. * return formatted string with all the details on the requested relationship
  510. * @param int $p_bug_id Bug id
  511. * @param BugRelationshipData $p_relationship Relationsip object
  512. * @param bool $p_html Generate html
  513. * @param bool $p_html_preview ???? generate printable version???
  514. * @param bool $p_show_project Show Project details
  515. * @return string
  516. */
  517. function relationship_get_details( $p_bug_id, $p_relationship, $p_html = false, $p_html_preview = false, $p_show_project = false ) {
  518. $t_summary_wrap_at = utf8_strlen( config_get( 'email_separator2' ) ) - 28;
  519. $t_icon_path = config_get( 'icon_path' );
  520. if( $p_bug_id == $p_relationship->src_bug_id ) {
  521. # root bug is in the src side, related bug in the dest side
  522. $t_related_bug_id = $p_relationship->dest_bug_id;
  523. $t_related_project_name = project_get_name( $p_relationship->dest_project_id );
  524. $t_relationship_descr = relationship_get_description_src_side( $p_relationship->type );
  525. } else {
  526. # root bug is in the dest side, related bug in the src side
  527. $t_related_bug_id = $p_relationship->src_bug_id;
  528. $t_related_project_name = project_get_name( $p_relationship->src_project_id );
  529. $t_relationship_descr = relationship_get_description_dest_side( $p_relationship->type );
  530. }
  531. # related bug not existing...
  532. if( !bug_exists( $t_related_bug_id ) ) {
  533. return '';
  534. }
  535. # user can access to the related bug at least as a viewer
  536. if( !access_has_bug_level( VIEWER, $t_related_bug_id ) ) {
  537. return '';
  538. }
  539. if( $p_html_preview == false ) {
  540. $t_td = '<td>';
  541. } else {
  542. $t_td = '<td class="print">';
  543. }
  544. # get the information from the related bug and prepare the link
  545. $t_bug = bug_get( $t_related_bug_id, false );
  546. $t_status_string = get_enum_element( 'status', $t_bug->status );
  547. $t_resolution_string = get_enum_element( 'resolution', $t_bug->resolution );
  548. $t_relationship_info_html = $t_td . string_no_break( $t_relationship_descr ) . '&#160;</td>';
  549. if( $p_html_preview == false ) {
  550. $t_relationship_info_html .= '<td><a href="' . string_get_bug_view_url( $t_related_bug_id ) . '">' . string_display_line( bug_format_id( $t_related_bug_id ) ) . '</a></td>';
  551. $t_relationship_info_html .= '<td><span class="issue-status" title="' . string_attribute( $t_resolution_string ) . '">' . string_display_line( $t_status_string ) . '</span></td>';
  552. } else {
  553. $t_relationship_info_html .= $t_td . string_display_line( bug_format_id( $t_related_bug_id ) ) . '</td>';
  554. $t_relationship_info_html .= $t_td . string_display_line( $t_status_string ) . '&#160;</td>';
  555. }
  556. $t_relationship_info_text = utf8_str_pad( $t_relationship_descr, 20 );
  557. $t_relationship_info_text .= utf8_str_pad( bug_format_id( $t_related_bug_id ), 8 );
  558. # get the handler name of the related bug
  559. $t_relationship_info_html .= $t_td;
  560. if( $t_bug->handler_id > 0 ) {
  561. $t_relationship_info_html .= string_no_break( prepare_user_name( $t_bug->handler_id ) );
  562. }
  563. $t_relationship_info_html .= '&#160;</td>';
  564. # add project name
  565. if( $p_show_project ) {
  566. $t_relationship_info_html .= $t_td . string_display_line( $t_related_project_name ) . '&#160;</td>';
  567. }
  568. # add summary
  569. if( $p_html == true ) {
  570. $t_relationship_info_html .= $t_td . string_display_line_links( $t_bug->summary );
  571. if( VS_PRIVATE == $t_bug->view_state ) {
  572. $t_relationship_info_html .= sprintf( ' <img src="%s" alt="(%s)" title="%s" />', $t_icon_path . 'protected.gif', lang_get( 'private' ), lang_get( 'private' ) );
  573. }
  574. } else {
  575. if( utf8_strlen( $t_bug->summary ) <= $t_summary_wrap_at ) {
  576. $t_relationship_info_text .= string_email_links( $t_bug->summary );
  577. } else {
  578. $t_relationship_info_text .= utf8_substr( string_email_links( $t_bug->summary ), 0, $t_summary_wrap_at - 3 ) . '...';
  579. }
  580. }
  581. # add delete link if bug not read only and user has access level
  582. if( !bug_is_readonly( $p_bug_id ) && !current_user_is_anonymous() && ( $p_html_preview == false ) ) {
  583. if( access_has_bug_level( config_get( 'update_bug_threshold' ), $p_bug_id ) ) {
  584. $t_relationship_info_html .= ' [<a class="small" href="bug_relationship_delete.php?bug_id=' . $p_bug_id . '&amp;rel_id=' . $p_relationship->id . htmlspecialchars( form_security_param( 'bug_relationship_delete' ) ) . '">' . lang_get( 'delete_link' ) . '</a>]';
  585. }
  586. }
  587. $t_relationship_info_html .= '&#160;</td>';
  588. $t_relationship_info_text .= "\n";
  589. if( $p_html_preview == false ) {
  590. $t_relationship_info_html = '<tr bgcolor="' . get_status_color( $t_bug->status ) . '">' . $t_relationship_info_html . '</tr>' . "\n";
  591. } else {
  592. $t_relationship_info_html = '<tr>' . $t_relationship_info_html . '</tr>';
  593. }
  594. if( $p_html == true ) {
  595. return $t_relationship_info_html;
  596. } else {
  597. return $t_relationship_info_text;
  598. }
  599. }
  600. /**
  601. * print ALL the RELATIONSHIPS OF A SPECIFIC BUG
  602. * @param int $p_bug_id Bug id
  603. * @return string
  604. */
  605. function relationship_get_summary_html( $p_bug_id ) {
  606. $t_summary = '';
  607. $t_show_project = false;
  608. $t_relationship_all = relationship_get_all( $p_bug_id, $t_show_project );
  609. $t_relationship_all_count = count( $t_relationship_all );
  610. # prepare the relationships table
  611. for( $i = 0;$i < $t_relationship_all_count;$i++ ) {
  612. $t_summary .= relationship_get_details( $p_bug_id, $t_relationship_all[$i], true, false, $t_show_project );
  613. }
  614. if( !is_blank( $t_summary ) ) {
  615. if( relationship_can_resolve_bug( $p_bug_id ) == false ) {
  616. $t_summary .= '<tr class="row-2"><td colspan="' . ( 5 + $t_show_project ) . '"><b>' . lang_get( 'relationship_warning_blocking_bugs_not_resolved' ) . '</b></td></tr>';
  617. }
  618. $t_summary = '<table border="0" width="100%" cellpadding="0" cellspacing="1">' . $t_summary . '</table>';
  619. }
  620. return $t_summary;
  621. }
  622. /**
  623. * print ALL the RELATIONSHIPS OF A SPECIFIC BUG
  624. * @param int $p_bug_id Bug id
  625. * @return string
  626. */
  627. function relationship_get_summary_html_preview( $p_bug_id ) {
  628. $t_summary = '';
  629. $t_show_project = false;
  630. $t_relationship_all = relationship_get_all( $p_bug_id, $t_show_project );
  631. $t_relationship_all_count = count( $t_relationship_all );
  632. # prepare the relationships table
  633. for( $i = 0;$i < $t_relationship_all_count;$i++ ) {
  634. $t_summary .= relationship_get_details( $p_bug_id, $t_relationship_all[$i], true, true, $t_show_project );
  635. }
  636. if( !is_blank( $t_summary ) ) {
  637. if( relationship_can_resolve_bug( $p_bug_id ) == false ) {
  638. $t_summary .= '<tr class="print"><td class="print" colspan=' . ( 5 + $t_show_project ) . '><b>' . lang_get( 'relationship_warning_blocking_bugs_not_resolved' ) . '</b></td></tr>';
  639. }
  640. $t_summary = '<table border="0" width="100%" cellpadding="0" cellspacing="1">' . $t_summary . '</table>';
  641. }
  642. return $t_summary;
  643. }
  644. /**
  645. * print ALL the RELATIONSHIPS OF A SPECIFIC BUG in text format (used by email_api.php
  646. * @param int $p_bug_id Bug id
  647. * @return string
  648. */
  649. function relationship_get_summary_text( $p_bug_id ) {
  650. $t_summary = '';
  651. $t_show_project = false;
  652. $t_relationship_all = relationship_get_all( $p_bug_id, $t_show_project );
  653. $t_relationship_all_count = count( $t_relationship_all );
  654. # prepare the relationships table
  655. for( $i = 0;$i < $t_relationship_all_count;$i++ ) {
  656. $t_summary .= relationship_get_details( $p_bug_id, $t_relationship_all[$i], false );
  657. }
  658. return $t_summary;
  659. }
  660. /**
  661. * print HTML relationship listbox
  662. * @param int $p_default_rel_type Relationship Type (default -1)
  663. * @param string $p_select_name List box name (default "rel_type")
  664. * @param bool $p_include_any Include an ANY option in list box (default false)
  665. * @param bool $p_include_none Include a NONE option in list box (default false)
  666. * @param int $p_bug_id Bug id
  667. * @return null
  668. */
  669. function relationship_list_box( $p_default_rel_type = -1, $p_select_name = "rel_type", $p_include_any = false, $p_include_none = false ) {
  670. global $g_relationships;
  671. ?>
  672. <select name="<?php echo $p_select_name?>">
  673. <?php if( $p_include_any ) {?>
  674. <option value="-1" <?php echo( $p_default_rel_type == -1 ? ' selected="selected"' : '' )?>>[<?php echo lang_get( 'any' )?>]</option>
  675. <?php
  676. }
  677. if( $p_include_none ) {?>
  678. <option value="-2" <?php echo( $p_default_rel_type == -2 ? ' selected="selected"' : '' )?>>[<?php echo lang_get( 'none' )?>]</option>
  679. <?php
  680. }
  681. foreach( $g_relationships as $type => $relationship ) {
  682. ?>
  683. <option value="<?php echo $type?>"<?php echo( $p_default_rel_type == $type ? ' selected="selected"' : '' )?>><?php echo lang_get( $relationship['#description'] )?></option>
  684. <?php
  685. }?>
  686. </select>
  687. <?php
  688. }
  689. /**
  690. * print HTML relationship form
  691. * @param int $p_bug_id Bug id
  692. * @return null
  693. */
  694. function relationship_view_box( $p_bug_id ) {
  695. ?>
  696. <br />
  697. <?php collapse_open( 'relationships' );?>
  698. <table class="width100" cellspacing="1">
  699. <tr class="row-2" valign="top">
  700. <td width="15%" class="form-title" colspan="2">
  701. <?php
  702. collapse_icon( 'relationships' );
  703. echo lang_get( 'bug_relationships' );
  704. if( ON == config_get( 'relationship_graph_enable' ) ) {
  705. ?>
  706. <span class="small"><?php print_bracket_link( "bug_relationship_graph.php?bug_id=$p_bug_id&graph=relation", lang_get( 'relation_graph' ) )?></span>
  707. <span class="small"><?php print_bracket_link( "bug_relationship_graph.php?bug_id=$p_bug_id&graph=dependency", lang_get( 'dependency_graph' ) )?></span>
  708. <?php
  709. }
  710. ?>
  711. </td>
  712. </tr>
  713. <?php
  714. # bug not read-only and user authenticated
  715. if( !bug_is_readonly( $p_bug_id ) ) {
  716. # user access level at least updater
  717. if( access_has_bug_level( config_get( 'update_bug_threshold' ), $p_bug_id ) ) {
  718. ?>
  719. <tr class="row-1">
  720. <td class="category"><?php echo lang_get( 'add_new_relationship' )?></td>
  721. <td><?php echo lang_get( 'this_bug' )?>
  722. <form method="post" action="bug_relationship_add.php">
  723. <?php echo form_security_field( 'bug_relationship_add' ) ?>
  724. <input type="hidden" name="src_bug_id" value="<?php echo $p_bug_id?>" size="4" />
  725. <?php relationship_list_box( -1 )?>
  726. <input type="text" name="dest_bug_id" value="" />
  727. <input type="submit" name="add_relationship" class="button" value="<?php echo lang_get( 'add_new_relationship_button' )?>" />
  728. </form>
  729. </td></tr>
  730. <?php
  731. }
  732. }
  733. ?>
  734. <tr>
  735. <td colspan="2"><?php echo relationship_get_summary_html( $p_bug_id )?></td>
  736. </tr>
  737. </table>
  738. <?php collapse_closed( 'relationships' );?>
  739. <table class="width100" cellspacing="1">
  740. <tr>
  741. <td class="form-title">
  742. <?php
  743. collapse_icon( 'relationships' );
  744. echo lang_get( 'bug_relationships' );
  745. ?>
  746. </td>
  747. </tr>
  748. </table>
  749. <?php
  750. collapse_end( 'relationships' );
  751. }