PageRenderTime 61ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/MantisBT/core/tag_api.php

https://bitbucket.org/crypticrod/sr_wp_code
PHP | 780 lines | 444 code | 130 blank | 206 comment | 71 complexity | 56601031167a89f584e7b3e3e00d8b94 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. * Tag API
  17. *
  18. * @package CoreAPI
  19. * @subpackage TagAPI
  20. * @author John Reese
  21. * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
  22. * @copyright Copyright (C) 2002 - 2011 MantisBT Team - mantisbt-dev@lists.sourceforge.net
  23. * @link http://www.mantisbt.org
  24. */
  25. /**
  26. * requires bug api
  27. */
  28. require_once( 'bug_api.php' );
  29. /**
  30. * requires history api
  31. */
  32. require_once( 'history_api.php' );
  33. /**
  34. * Determine if a tag exists with the given ID.
  35. * @param integer Tag ID
  36. * @return boolean True if tag exists
  37. */
  38. function tag_exists( $p_tag_id ) {
  39. $c_tag_id = db_prepare_int( $p_tag_id );
  40. $t_tag_table = db_get_table( 'mantis_tag_table' );
  41. $query = "SELECT * FROM $t_tag_table WHERE id=" . db_param();
  42. $result = db_query_bound( $query, Array( $c_tag_id ) );
  43. return db_num_rows( $result ) > 0;
  44. }
  45. /**
  46. * Ensure a tag exists with the given ID.
  47. * @param integer Tag ID
  48. */
  49. function tag_ensure_exists( $p_tag_id ) {
  50. if( !tag_exists( $p_tag_id ) ) {
  51. error_parameters( $p_tag_id );
  52. trigger_error( ERROR_TAG_NOT_FOUND, ERROR );
  53. }
  54. }
  55. /**
  56. * Determine if a given name is unique (not already used).
  57. * Uses a case-insensitive search of the database for existing tags with the same name.
  58. * @param string Tag name
  59. * @return boolean True if name is unique
  60. */
  61. function tag_is_unique( $p_name ) {
  62. $c_name = trim( $p_name );
  63. $t_tag_table = db_get_table( 'mantis_tag_table' );
  64. $query = 'SELECT id FROM ' . $t_tag_table . ' WHERE ' . db_helper_like( 'name' );
  65. $result = db_query_bound( $query, Array( $c_name ) );
  66. return db_num_rows( $result ) == 0;
  67. }
  68. /**
  69. * Ensure that a name is unique.
  70. * @param string Tag name
  71. */
  72. function tag_ensure_unique( $p_name ) {
  73. if( !tag_is_unique( $p_name ) ) {
  74. trigger_error( ERROR_TAG_DUPLICATE, ERROR );
  75. }
  76. }
  77. /**
  78. * Determine if a given name is valid.
  79. *
  80. * Name must not begin with '+' and '-' characters (they are used for
  81. * filters) and must not contain the configured tag separator.
  82. * The matches parameter allows to also receive an array of regex matches,
  83. * which by default only includes the valid tag name itself.
  84. * The prefix parameter is optional, but allows you to prefix the regex
  85. * check, which is useful for filters, etc.
  86. * @param string Tag name
  87. * @param array Array reference for regex matches
  88. * @param string Prefix regex pattern
  89. * @return boolean True if the name is valid
  90. */
  91. function tag_name_is_valid( $p_name, &$p_matches, $p_prefix = '' ) {
  92. $t_separator = config_get( 'tag_separator' );
  93. $t_pattern = "/^$p_prefix([^\+\-{$t_separator}][^{$t_separator}]*)$/";
  94. return preg_match( $t_pattern, $p_name, $p_matches );
  95. }
  96. /**
  97. * Ensure a tag name is valid.
  98. * @param string Tag name
  99. */
  100. function tag_ensure_name_is_valid( $p_name ) {
  101. $t_matches = array();
  102. if( !tag_name_is_valid( $p_name, $t_matches ) ) {
  103. trigger_error( ERROR_TAG_NAME_INVALID, ERROR );
  104. }
  105. }
  106. /**
  107. * Compare two tag rows based on tag name.
  108. * @param array Tag row 1
  109. * @param array Tag row 2
  110. * @return integer -1 when Tag 1 < Tag 2, 1 when Tag 1 > Tag 2, 0 otherwise
  111. */
  112. function tag_cmp_name( $p_tag1, $p_tag2 ) {
  113. return strcasecmp( $p_tag1['name'], $p_tag2['name'] );
  114. }
  115. /**
  116. * Parse a form input string to extract existing and new tags.
  117. * When given a string, parses for tag names separated by configured separator,
  118. * then returns an array of tag rows for each tag. Existing tags get the full
  119. * row of information returned. If the tag does not exist, a row is returned with
  120. * id = -1 and the tag name, and if the name is invalid, a row is returned with
  121. * id = -2 and the tag name. The resulting array is then sorted by tag name.
  122. * @param string Input string to parse
  123. * @return array Rows of tags parsed from input string
  124. */
  125. function tag_parse_string( $p_string ) {
  126. $t_tags = array();
  127. $t_strings = explode( config_get( 'tag_separator' ), $p_string );
  128. foreach( $t_strings as $t_name ) {
  129. $t_name = trim( $t_name );
  130. if( is_blank( $t_name ) ) {
  131. continue;
  132. }
  133. $t_matches = array();
  134. $t_tag_row = tag_get_by_name( $t_name );
  135. if( $t_tag_row !== false ) {
  136. $t_tags[] = $t_tag_row;
  137. } else {
  138. if( tag_name_is_valid( $t_name, $t_matches ) ) {
  139. $t_id = -1;
  140. } else {
  141. $t_id = -2;
  142. }
  143. $t_tags[] = array(
  144. 'id' => $t_id,
  145. 'name' => $t_name,
  146. );
  147. }
  148. }
  149. usort( $t_tags, 'tag_cmp_name' );
  150. return $t_tags;
  151. }
  152. /**
  153. * Parse a filter string to extract existing and new tags.
  154. * When given a string, parses for tag names separated by configured separator,
  155. * then returns an array of tag rows for each tag. Existing tags get the full
  156. * row of information returned. If the tag does not exist, a row is returned with
  157. * id = -1 and the tag name, and if the name is invalid, a row is returned with
  158. * id = -2 and the tag name. The resulting array is then sorted by tag name.
  159. * @param string Filter string to parse
  160. * @return array Rows of tags parsed from filter string
  161. */
  162. function tag_parse_filters( $p_string ) {
  163. $t_tags = array();
  164. $t_prefix = '[+-]{0,1}';
  165. $t_strings = explode( config_get( 'tag_separator' ), $p_string );
  166. foreach( $t_strings as $t_name ) {
  167. $t_name = trim( $t_name );
  168. $t_matches = array();
  169. if( !is_blank( $t_name ) && tag_name_is_valid( $t_name, $t_matches, $t_prefix ) ) {
  170. $t_tag_row = tag_get_by_name( $t_matches[1] );
  171. if( $t_tag_row !== false ) {
  172. $t_filter = utf8_substr( $t_name, 0, 1 );
  173. if( '+' == $t_filter ) {
  174. $t_tag_row['filter'] = 1;
  175. } else if( '-' == $t_filter ) {
  176. $t_tag_row['filter'] = -1;
  177. } else {
  178. $t_tag_row['filter'] = 0;
  179. }
  180. $t_tags[] = $t_tag_row;
  181. }
  182. } else {
  183. continue;
  184. }
  185. }
  186. usort( $t_tags, 'tag_cmp_name' );
  187. return $t_tags;
  188. }
  189. # CRUD
  190. /**
  191. * Return a tag row for the given ID.
  192. * @param integer Tag ID
  193. * @return array Tag row
  194. */
  195. function tag_get( $p_tag_id ) {
  196. tag_ensure_exists( $p_tag_id );
  197. $c_tag_id = db_prepare_int( $p_tag_id );
  198. $t_tag_table = db_get_table( 'mantis_tag_table' );
  199. $query = "SELECT * FROM $t_tag_table
  200. WHERE id=" . db_param();
  201. $result = db_query_bound( $query, Array( $c_tag_id ) );
  202. if( 0 == db_num_rows( $result ) ) {
  203. return false;
  204. }
  205. $row = db_fetch_array( $result );
  206. return $row;
  207. }
  208. /**
  209. * Return a tag row for the given name.
  210. * @param string Tag name
  211. * @return Tag row
  212. */
  213. function tag_get_by_name( $p_name ) {
  214. $t_tag_table = db_get_table( 'mantis_tag_table' );
  215. $query = "SELECT * FROM $t_tag_table
  216. WHERE " . db_helper_like( 'name' );
  217. $result = db_query_bound( $query, Array( $p_name ) );
  218. if( 0 == db_num_rows( $result ) ) {
  219. return false;
  220. }
  221. $row = db_fetch_array( $result );
  222. return $row;
  223. }
  224. /**
  225. * Return a single field from a tag row for the given ID.
  226. * @param integer Tag ID
  227. * @param string Field name
  228. * @return mixed Field value
  229. */
  230. function tag_get_field( $p_tag_id, $p_field_name ) {
  231. $row = tag_get( $p_tag_id );
  232. if( isset( $row[$p_field_name] ) ) {
  233. return $row[$p_field_name];
  234. } else {
  235. error_parameters( $p_field_name );
  236. trigger_error( ERROR_DB_FIELD_NOT_FOUND, WARNING );
  237. return '';
  238. }
  239. }
  240. /**
  241. * Create a tag with the given name, creator, and description.
  242. * Defaults to the currently logged in user, and a blank description.
  243. * @param string Tag name
  244. * @param integer User ID
  245. * @param string Description
  246. * @return integer Tag ID
  247. */
  248. function tag_create( $p_name, $p_user_id = null, $p_description = '' ) {
  249. access_ensure_global_level( config_get( 'tag_create_threshold' ) );
  250. tag_ensure_name_is_valid( $p_name );
  251. tag_ensure_unique( $p_name );
  252. if( null == $p_user_id ) {
  253. $p_used_id = auth_get_current_user_id();
  254. } else {
  255. user_ensure_exists( $p_user_id );
  256. }
  257. $c_user_id = db_prepare_int( $p_user_id );
  258. $c_date_created = db_now();
  259. $t_tag_table = db_get_table( 'mantis_tag_table' );
  260. $query = "INSERT INTO $t_tag_table
  261. ( user_id,
  262. name,
  263. description,
  264. date_created,
  265. date_updated
  266. )
  267. VALUES
  268. ( " . db_param() . ",
  269. " . db_param() . ",
  270. " . db_param() . ",
  271. " . db_param() . ",
  272. " . db_param() . "
  273. )";
  274. db_query_bound( $query, Array( $c_user_id, trim( $p_name ), trim( $p_description ), $c_date_created, $c_date_created ) );
  275. return db_insert_id( $t_tag_table );
  276. }
  277. /**
  278. * Update a tag with given name, creator, and description.
  279. * @param integer Tag ID
  280. * @param string Tag name
  281. * @param integer User ID
  282. * @param string Description
  283. */
  284. function tag_update( $p_tag_id, $p_name, $p_user_id, $p_description ) {
  285. user_ensure_exists( $p_user_id );
  286. if( auth_get_current_user_id() == tag_get_field( $p_tag_id, 'user_id' ) ) {
  287. $t_update_level = config_get( 'tag_edit_own_threshold' );
  288. } else {
  289. $t_update_level = config_get( 'tag_edit_threshold' );
  290. }
  291. access_ensure_global_level( $t_update_level );
  292. tag_ensure_name_is_valid( $p_name );
  293. $t_tag_name = tag_get_field( $p_tag_id, 'name' );
  294. $t_rename = false;
  295. if( utf8_strtolower( $p_name ) != utf8_strtolower( $t_tag_name ) ) {
  296. tag_ensure_unique( $p_name );
  297. $t_rename = true;
  298. }
  299. $c_tag_id = trim( db_prepare_int( $p_tag_id ) );
  300. $c_date_updated = db_now();
  301. $t_tag_table = db_get_table( 'mantis_tag_table' );
  302. $query = "UPDATE $t_tag_table
  303. SET user_id=" . db_param() . ",
  304. name=" . db_param() . ",
  305. description=" . db_param() . ",
  306. date_updated=" . db_param() . "
  307. WHERE id=" . db_param();
  308. db_query_bound( $query, Array( (int)$p_user_id, $p_name, $p_description, $c_date_updated, $c_tag_id ) );
  309. if( $t_rename ) {
  310. $t_bugs = tag_get_bugs_attached( $p_tag_id );
  311. foreach( $t_bugs as $t_bug_id ) {
  312. history_log_event_special( $t_bug_id, TAG_RENAMED, $t_tag_name, $p_name );
  313. }
  314. }
  315. return true;
  316. }
  317. /**
  318. * Delete a tag with the given ID.
  319. * @param integer Tag ID
  320. */
  321. function tag_delete( $p_tag_id ) {
  322. tag_ensure_exists( $p_tag_id );
  323. access_ensure_global_level( config_get( 'tag_edit_threshold' ) );
  324. $t_bugs = tag_get_bugs_attached( $p_tag_id );
  325. foreach( $t_bugs as $t_bug_id ) {
  326. tag_bug_detach( $p_tag_id, $t_bug_id );
  327. }
  328. $c_tag_id = db_prepare_int( $p_tag_id );
  329. $t_tag_table = db_get_table( 'mantis_tag_table' );
  330. $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' );
  331. $query = "DELETE FROM $t_tag_table
  332. WHERE id=" . db_param();
  333. db_query_bound( $query, Array( $c_tag_id ) );
  334. return true;
  335. }
  336. /**
  337. * Gets the candidates for the specified bug. These are existing tags
  338. * that are not associated with the bug already.
  339. *
  340. * @param int $p_bug_id The bug id, if 0 returns all tags.
  341. * @returns The array of tag rows, each with id, name, and description.
  342. */
  343. function tag_get_candidates_for_bug( $p_bug_id ) {
  344. $t_tag_table = db_get_table( 'mantis_tag_table' );
  345. $t_params = array();
  346. if ( 0 != $p_bug_id ) {
  347. $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' );
  348. if ( db_is_mssql() ) {
  349. $t_params[] = $p_bug_id;
  350. $query = "SELECT t.id FROM $t_tag_table t
  351. LEFT JOIN $t_bug_tag_table b ON t.id=b.tag_id
  352. WHERE b.bug_id IS NULL OR b.bug_id != " . db_param();
  353. $result = db_query_bound( $query, $t_params );
  354. $t_subquery_results = array();
  355. while( $row = db_fetch_array( $result ) ) {
  356. $t_subquery_results[] = (int)$row;
  357. }
  358. $query = "SELECT id, name, description FROM $t_tag_table WHERE id IN ( " . implode( ', ', $t_subquery_results ) . ')';
  359. } else {
  360. $query = "SELECT id, name, description FROM $t_tag_table WHERE id IN (
  361. SELECT t.id FROM $t_tag_table t
  362. LEFT JOIN $t_bug_tag_table b ON t.id=b.tag_id
  363. WHERE b.bug_id IS NULL OR b.bug_id != " . db_param() .
  364. ')';
  365. }
  366. $t_params[] = $p_bug_id;
  367. } else {
  368. $query = 'SELECT id, name, description FROM ' . $t_tag_table;
  369. }
  370. $query .= ' ORDER BY name ASC ';
  371. $result = db_query_bound( $query, $t_params );
  372. $t_results_to_return = array();
  373. while( $row = db_fetch_array( $result ) ) {
  374. $t_results_to_return[] = $row;
  375. }
  376. return $t_results_to_return;
  377. }
  378. # Associative
  379. /**
  380. * Determine if a tag is attached to a bug.
  381. * @param integer Tag ID
  382. * @param integer Bug ID
  383. * @return boolean True if the tag is attached
  384. */
  385. function tag_bug_is_attached( $p_tag_id, $p_bug_id ) {
  386. $c_tag_id = db_prepare_int( $p_tag_id );
  387. $c_bug_id = db_prepare_int( $p_bug_id );
  388. $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' );
  389. $query = "SELECT * FROM $t_bug_tag_table
  390. WHERE tag_id=" . db_param() . " AND bug_id=" . db_param();
  391. $result = db_query_bound( $query, Array( $c_tag_id, $c_bug_id ) );
  392. return( db_num_rows( $result ) > 0 );
  393. }
  394. /**
  395. * Return the tag attachment row.
  396. * @param integer Tag ID
  397. * @param integer Bug ID
  398. * @return array Tag attachment row
  399. */
  400. function tag_bug_get_row( $p_tag_id, $p_bug_id ) {
  401. $c_tag_id = db_prepare_int( $p_tag_id );
  402. $c_bug_id = db_prepare_int( $p_bug_id );
  403. $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' );
  404. $query = "SELECT * FROM $t_bug_tag_table
  405. WHERE tag_id=" . db_param() . " AND bug_id=" . db_param();
  406. $result = db_query_bound( $query, Array( $c_tag_id, $c_bug_id ) );
  407. if( db_num_rows( $result ) == 0 ) {
  408. trigger_error( TAG_NOT_ATTACHED, ERROR );
  409. }
  410. return db_fetch_array( $result );
  411. }
  412. /**
  413. * Return an array of tags attached to a given bug sorted by tag name.
  414. * @param Bug ID
  415. * @return array Array of tag rows with attachement information
  416. */
  417. function tag_bug_get_attached( $p_bug_id ) {
  418. $c_bug_id = db_prepare_int( $p_bug_id );
  419. $t_tag_table = db_get_table( 'mantis_tag_table' );
  420. $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' );
  421. $query = "SELECT t.*, b.user_id as user_attached, b.date_attached
  422. FROM $t_tag_table as t
  423. LEFT JOIN $t_bug_tag_table as b
  424. on t.id=b.tag_id
  425. WHERE b.bug_id=" . db_param();
  426. $result = db_query_bound( $query, Array( $c_bug_id ) );
  427. $rows = array();
  428. while( $row = db_fetch_array( $result ) ) {
  429. $rows[] = $row;
  430. }
  431. usort( $rows, 'tag_cmp_name' );
  432. return $rows;
  433. }
  434. /**
  435. * Return an array of bugs that a tag is attached to.
  436. * @param integer Tag ID
  437. * @return array Array of bug ID's.
  438. */
  439. function tag_get_bugs_attached( $p_tag_id ) {
  440. $c_tag_id = db_prepare_int( $p_tag_id );
  441. $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' );
  442. $query = "SELECT bug_id FROM $t_bug_tag_table
  443. WHERE tag_id=" . db_param();
  444. $result = db_query_bound( $query, Array( $c_tag_id ) );
  445. $bugs = array();
  446. while( $row = db_fetch_array( $result ) ) {
  447. $bugs[] = $row['bug_id'];
  448. }
  449. return $bugs;
  450. }
  451. /**
  452. * Attach a tag to a bug.
  453. * @param integer Tag ID
  454. * @param integer Bug ID
  455. * @param integer User ID
  456. */
  457. function tag_bug_attach( $p_tag_id, $p_bug_id, $p_user_id = null ) {
  458. access_ensure_bug_level( config_get( 'tag_attach_threshold' ), $p_bug_id, $p_user_id );
  459. tag_ensure_exists( $p_tag_id );
  460. if( tag_bug_is_attached( $p_tag_id, $p_bug_id ) ) {
  461. trigger_error( TAG_ALREADY_ATTACHED, ERROR );
  462. }
  463. if( null == $p_user_id ) {
  464. $p_used_id = auth_get_current_user_id();
  465. } else {
  466. user_ensure_exists( $p_user_id );
  467. }
  468. $c_tag_id = db_prepare_int( $p_tag_id );
  469. $c_bug_id = db_prepare_int( $p_bug_id );
  470. $c_user_id = db_prepare_int( $p_user_id );
  471. $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' );
  472. $query = "INSERT INTO $t_bug_tag_table
  473. ( tag_id,
  474. bug_id,
  475. user_id,
  476. date_attached
  477. )
  478. VALUES
  479. ( " . db_param() . ",
  480. " . db_param() . ",
  481. " . db_param() . ",
  482. " . db_param() . "
  483. )";
  484. db_query_bound( $query, Array( $c_tag_id, $c_bug_id, $c_user_id, db_now() ) );
  485. $t_tag_name = tag_get_field( $p_tag_id, 'name' );
  486. history_log_event_special( $p_bug_id, TAG_ATTACHED, $t_tag_name );
  487. # updated the last_updated date
  488. bug_update_date( $p_bug_id );
  489. return true;
  490. }
  491. /**
  492. * Detach a tag from a bug.
  493. * @param integer Tag ID
  494. * @param integer Bug ID
  495. * @param boolean Add history entries to bug
  496. * @param integer User Id (or null for current logged in user)
  497. */
  498. function tag_bug_detach( $p_tag_id, $p_bug_id, $p_add_history = true, $p_user_id = null ) {
  499. if( $p_user_id === null ) {
  500. $t_user_id = auth_get_current_user_id();
  501. } else {
  502. $t_user_id = $p_user_id;
  503. }
  504. if( !tag_bug_is_attached( $p_tag_id, $p_bug_id ) ) {
  505. trigger_error( TAG_NOT_ATTACHED, ERROR );
  506. }
  507. $t_tag_row = tag_bug_get_row( $p_tag_id, $p_bug_id);
  508. if( $t_user_id == tag_get_field( $p_tag_id, 'user_id' ) || $t_user_id == $t_tag_row[ 'user_id' ] ) {
  509. $t_detach_level = config_get( 'tag_detach_own_threshold' );
  510. } else {
  511. $t_detach_level = config_get( 'tag_detach_threshold' );
  512. }
  513. access_ensure_bug_level( $t_detach_level, $p_bug_id, $t_user_id );
  514. $c_tag_id = db_prepare_int( $p_tag_id );
  515. $c_bug_id = db_prepare_int( $p_bug_id );
  516. $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' );
  517. $query = "DELETE FROM $t_bug_tag_table
  518. WHERE tag_id=" . db_param() . ' AND bug_id=' . db_param();
  519. db_query_bound( $query, Array( $c_tag_id, $c_bug_id ) );
  520. if( $p_add_history ) {
  521. $t_tag_name = tag_get_field( $p_tag_id, 'name' );
  522. history_log_event_special( $p_bug_id, TAG_DETACHED, $t_tag_name );
  523. }
  524. # updated the last_updated date
  525. bug_update_date( $p_bug_id );
  526. return true;
  527. }
  528. /**
  529. * Detach all tags from a given bug.
  530. * @param integer Bug ID
  531. * @param boolean Add history entries to bug
  532. * @param integer User Id (or null for current logged in user)
  533. */
  534. function tag_bug_detach_all( $p_bug_id, $p_add_history = true, $p_user_id = null ) {
  535. $t_tags = tag_bug_get_attached( $p_bug_id );
  536. foreach( $t_tags as $t_tag_row ) {
  537. tag_bug_detach( $t_tag_row['id'], $p_bug_id, $p_add_history, $p_user_id );
  538. }
  539. }
  540. # Display
  541. /**
  542. * Display a tag hyperlink.
  543. * If a bug ID is passed, the tag link will include a detach link if the
  544. * user has appropriate privileges.
  545. * @param array Tag row
  546. * @param integer Bug ID
  547. */
  548. function tag_display_link( $p_tag_row, $p_bug_id = 0 ) {
  549. static $t_security_token = null;
  550. if( is_null( $t_security_token ) ) {
  551. $t_security_token = htmlspecialchars( form_security_param( 'tag_detach' ) );
  552. }
  553. if( auth_get_current_user_id() == $p_tag_row['user_attached'] || auth_get_current_user_id() == $p_tag_row['user_id'] ) {
  554. $t_detach = config_get( 'tag_detach_own_threshold' );
  555. } else {
  556. $t_detach = config_get( 'tag_detach_threshold' );
  557. }
  558. $t_name = string_display_line( $p_tag_row['name'] );
  559. $t_description = string_display_line( $p_tag_row['description'] );
  560. echo "<a href='tag_view_page.php?tag_id=$p_tag_row[id]' title='$t_description'>$t_name</a>";
  561. if( $p_bug_id > 0 && access_has_bug_level( $t_detach, $p_bug_id ) ) {
  562. $t_tooltip = string_html_specialchars( sprintf( lang_get( 'tag_detach' ), $t_name ) );
  563. echo " <a href='tag_detach.php?bug_id=$p_bug_id&amp;tag_id=$p_tag_row[id]$t_security_token'><img src='images/delete.png' class='delete-icon' title=\"$t_tooltip\" alt=\"X\"/></a>";
  564. }
  565. return true;
  566. }
  567. /**
  568. * Display a list of attached tag hyperlinks separated by the configured hyperlinks.
  569. * @param Bug ID
  570. */
  571. function tag_display_attached( $p_bug_id ) {
  572. $t_tag_rows = tag_bug_get_attached( $p_bug_id );
  573. if( count( $t_tag_rows ) == 0 ) {
  574. echo lang_get( 'tag_none_attached' );
  575. } else {
  576. $i = 0;
  577. foreach( $t_tag_rows as $t_tag ) {
  578. echo( $i > 0 ? config_get( 'tag_separator' ) . ' ' : '' );
  579. tag_display_link( $t_tag, $p_bug_id );
  580. $i++;
  581. }
  582. }
  583. return true;
  584. }
  585. # Statistics
  586. /**
  587. * Get the number of bugs a given tag is attached to.
  588. * @param integer Tag ID
  589. * @return integer Number of attached bugs
  590. */
  591. function tag_stats_attached( $p_tag_id ) {
  592. $c_tag_id = db_prepare_int( $p_tag_id );
  593. $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' );
  594. $query = "SELECT COUNT(*) FROM $t_bug_tag_table
  595. WHERE tag_id=" . db_param();
  596. $result = db_query_bound( $query, Array( $c_tag_id ) );
  597. return db_result( $result );
  598. }
  599. /**
  600. * Get a list of related tags.
  601. * Returns a list of tags that are the most related to the given tag,
  602. * based on the number of times they have been attached to the same bugs.
  603. * Defaults to a list of five tags.
  604. * @param integer Tag ID
  605. * @param integer List size
  606. * @return array Array of tag rows, with share count added
  607. */
  608. function tag_stats_related( $p_tag_id, $p_limit = 5 ) {
  609. $t_bug_table = db_get_table( 'mantis_bug_table' );
  610. $t_tag_table = db_get_table( 'mantis_tag_table' );
  611. $t_bug_tag_table = db_get_table( 'mantis_bug_tag_table' );
  612. $t_project_user_list_table = db_get_table( 'mantis_project_user_list_table' );
  613. $t_user_table = db_get_table( 'mantis_user_table' );
  614. $c_tag_id = db_prepare_int( $p_tag_id );
  615. $c_user_id = auth_get_current_user_id();
  616. $subquery = "SELECT b.id FROM $t_bug_table AS b
  617. LEFT JOIN $t_project_user_list_table AS p
  618. ON p.project_id=b.project_id AND p.user_id=" . db_param() . "
  619. JOIN $t_user_table AS u
  620. ON u.id=" . db_param() . "
  621. JOIN $t_bug_tag_table AS t
  622. ON t.bug_id=b.id
  623. WHERE ( p.access_level>b.view_state OR u.access_level>b.view_state )
  624. AND t.tag_id=" . db_param();
  625. $query = "SELECT * FROM $t_bug_tag_table
  626. WHERE tag_id != " . db_param() . "
  627. AND bug_id IN ( $subquery ) ";
  628. $result = db_query_bound( $query, Array( /*query*/ $c_tag_id, /*subquery*/ $c_user_id, $c_user_id, $c_tag_id ) );
  629. $t_tag_counts = array();
  630. while( $row = db_fetch_array( $result ) ) {
  631. if( !isset( $t_tag_counts[$row['tag_id']] ) ) {
  632. $t_tag_counts[$row['tag_id']] = 1;
  633. } else {
  634. $t_tag_counts[$row['tag_id']]++;
  635. }
  636. }
  637. arsort( $t_tag_counts );
  638. $t_tags = array();
  639. $i = 1;
  640. foreach( $t_tag_counts as $t_tag_id => $t_count ) {
  641. $t_tag_row = tag_get( $t_tag_id );
  642. $t_tag_row['count'] = $t_count;
  643. $t_tags[] = $t_tag_row;
  644. $i++;
  645. if( $i > $p_limit ) {
  646. break;
  647. }
  648. }
  649. return $t_tags;
  650. }