PageRenderTime 57ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/core/custom_field_api.php

https://github.com/vincentsels/mantisbt
PHP | 1399 lines | 779 code | 192 blank | 428 comment | 142 complexity | 9aaac98f00991472e3d4d6959a677d25 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  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. * @package CoreAPI
  17. * @subpackage CustomFieldAPI
  18. * @copyright Copyright (C) 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
  19. * @copyright Copyright (C) 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net
  20. * @link http://www.mantisbt.org
  21. */
  22. /**
  23. * requires bug_api
  24. */
  25. require_once( 'bug_api.php' );
  26. /**
  27. * requires helper_api
  28. */
  29. require_once( 'helper_api.php' );
  30. /**
  31. * requires date_api
  32. */
  33. require_once( 'date_api.php' );
  34. # ## Custom Fields API ###
  35. # *******************************************
  36. # TODO
  37. # - add an object to store field data like BugData and UserPrefs ?
  38. # - add caching functions like user, bug, etc
  39. # - make existing api functions use caching functions
  40. # - add functions to return individual db columns for a field definition
  41. # *******************************************
  42. $g_custom_field_types[CUSTOM_FIELD_TYPE_STRING] = 'standard';
  43. $g_custom_field_types[CUSTOM_FIELD_TYPE_NUMERIC] = 'standard';
  44. $g_custom_field_types[CUSTOM_FIELD_TYPE_FLOAT] = 'standard';
  45. $g_custom_field_types[CUSTOM_FIELD_TYPE_ENUM] = 'standard';
  46. $g_custom_field_types[CUSTOM_FIELD_TYPE_EMAIL] = 'standard';
  47. $g_custom_field_types[CUSTOM_FIELD_TYPE_CHECKBOX] = 'standard';
  48. $g_custom_field_types[CUSTOM_FIELD_TYPE_LIST] = 'standard';
  49. $g_custom_field_types[CUSTOM_FIELD_TYPE_MULTILIST] = 'standard';
  50. $g_custom_field_types[CUSTOM_FIELD_TYPE_DATE] = 'standard';
  51. $g_custom_field_types[CUSTOM_FIELD_TYPE_TEMPLATE] = 'standard';
  52. foreach( $g_custom_field_types as $type ) {
  53. require_once( 'cfdefs' . DIRECTORY_SEPARATOR . 'cfdef_' . $type . '.php' );
  54. }
  55. function custom_field_allow_manage_display( $p_type, $p_display ) {
  56. global $g_custom_field_type_definition;
  57. if( isset( $g_custom_field_type_definition[$p_type]['#display_' . $p_display] ) ) {
  58. return $g_custom_field_type_definition[$p_type]['#display_' . $p_display];
  59. }
  60. return false;
  61. }
  62. # ########################################
  63. # SECURITY NOTE: cache globals are initialized here to prevent them
  64. # being spoofed if register_globals is turned on
  65. $g_cache_custom_field = array();
  66. $g_cache_cf_list = NULL;
  67. $g_cache_cf_linked = array();
  68. $g_cache_name_to_id_map = array();
  69. /**
  70. * Cache a custom field row if necessary and return the cached copy
  71. * If the second parameter is true (default), trigger an error
  72. * if the field can't be found. If the second parameter is
  73. * false, return false if the field can't be found.
  74. * @param int $p_field_id integer representing custom field id
  75. * @param bool $p_trigger_errors indicates whether to trigger an error if the field is not found
  76. * @return array array representing custom field
  77. * @access public
  78. */
  79. function custom_field_cache_row( $p_field_id, $p_trigger_errors = true ) {
  80. global $g_cache_custom_field, $g_cache_name_to_id_map;
  81. $c_field_id = db_prepare_int( $p_field_id );
  82. $t_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  83. if( isset( $g_cache_custom_field[$c_field_id] ) ) {
  84. return $g_cache_custom_field[$c_field_id];
  85. }
  86. $query = "SELECT *
  87. FROM $t_custom_field_table
  88. WHERE id=" . db_param();
  89. $result = db_query_bound( $query, Array( $c_field_id ) );
  90. if( 0 == db_num_rows( $result ) ) {
  91. if( $p_trigger_errors ) {
  92. error_parameters( 'Custom ' . $p_field_id );
  93. trigger_error( ERROR_CUSTOM_FIELD_NOT_FOUND, ERROR );
  94. } else {
  95. return false;
  96. }
  97. }
  98. $row = db_fetch_array( $result );
  99. $g_cache_custom_field[$c_field_id] = $row;
  100. $g_cache_name_to_id_map[$row['name']] = $c_field_id;
  101. return $row;
  102. }
  103. /**
  104. * Cache custom fields contained within an array of field id's
  105. * @param array $p_cf_id_array array of custom field id's
  106. * @return null
  107. * @access public
  108. */
  109. function custom_field_cache_array_rows( $p_cf_id_array ) {
  110. global $g_cache_custom_field;
  111. $c_cf_id_array = array();
  112. foreach( $p_cf_id_array as $t_cf_id ) {
  113. if( !isset( $g_cache_custom_field[(int) $t_cf_id] ) ) {
  114. $c_cf_id_array[] = (int) $t_cf_id;
  115. }
  116. }
  117. if( empty( $c_cf_id_array ) ) {
  118. return;
  119. }
  120. $t_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  121. $query = "SELECT *
  122. FROM $t_custom_field_table
  123. WHERE id IN (" . implode( ',', $c_cf_id_array ) . ')';
  124. $result = db_query_bound( $query );
  125. while( $row = db_fetch_array( $result ) ) {
  126. $g_cache_custom_field[(int) $row['id']] = $row;
  127. }
  128. return;
  129. }
  130. /**
  131. * Clear the custom field cache (or just the given id if specified)
  132. * @param int $p_field_id custom field id
  133. * @return bool
  134. * @access public
  135. */
  136. function custom_field_clear_cache( $p_field_id = null ) {
  137. global $g_cache_custom_field, $g_cached_custom_field_lists;
  138. $g_cached_custom_field_lists = null;
  139. if( null === $p_field_id ) {
  140. $g_cache_custom_field = array();
  141. } else {
  142. $c_field_id = db_prepare_int( $p_field_id );
  143. unset( $g_cache_custom_field[$c_field_id] );
  144. }
  145. return true;
  146. }
  147. /**
  148. * Check to see whether the field is included in the given project
  149. * return true if the field is included, false otherwise
  150. * @param int $p_field_id custom field id
  151. * @param int $p_project_id project id
  152. * @return bool
  153. * @access public
  154. */
  155. function custom_field_is_linked( $p_field_id, $p_project_id ) {
  156. global $g_cache_cf_linked;
  157. $c_project_id = db_prepare_int( $p_project_id );
  158. $c_field_id = db_prepare_int( $p_field_id );
  159. if( isset( $g_cache_cf_linked[$c_project_id] ) ) {
  160. if( in_array( $c_field_id, $g_cache_cf_linked[$p_project_id] ) ) {
  161. return true;
  162. }
  163. return false;
  164. }
  165. # figure out if this bug_id/field_id combination exists
  166. $t_custom_field_project_table = db_get_table( 'mantis_custom_field_project_table' );
  167. $query = "SELECT COUNT(*)
  168. FROM $t_custom_field_project_table
  169. WHERE field_id=" . db_param() . " AND
  170. project_id=" . db_param();
  171. $result = db_query_bound( $query, Array( $c_field_id, $c_project_id ) );
  172. $count = db_result( $result );
  173. if( $count > 0 ) {
  174. return true;
  175. } else {
  176. return false;
  177. }
  178. }
  179. /**
  180. * Check to see whether the field id is defined
  181. * return true if the field is defined, false otherwise
  182. * @param int $p_field_id custom field id
  183. * @return bool
  184. * @access public
  185. */
  186. function custom_field_exists( $p_field_id ) {
  187. if( false == custom_field_cache_row( $p_field_id, false ) ) {
  188. return false;
  189. } else {
  190. return true;
  191. }
  192. }
  193. /**
  194. * Return the type of a custom field if it exists.
  195. * @param int $p_field_id custom field id
  196. * @return int custom field type
  197. * @access public
  198. */
  199. function custom_field_type( $p_field_id ) {
  200. $t_field = custom_field_cache_row( $p_field_id, false );
  201. if( $t_field == false ) {
  202. return - 1;
  203. } else {
  204. return $t_field['type'];
  205. }
  206. }
  207. /**
  208. * Check to see whether the field id is defined
  209. * return true if the field is defined, error otherwise
  210. * @param int $p_field_id custom field id
  211. * @return bool
  212. * @access public
  213. */
  214. function custom_field_ensure_exists( $p_field_id ) {
  215. if( custom_field_exists( $p_field_id ) ) {
  216. return true;
  217. } else {
  218. error_parameters( 'Custom ' . $p_field_id );
  219. trigger_error( ERROR_CUSTOM_FIELD_NOT_FOUND, ERROR );
  220. }
  221. }
  222. /**
  223. * Check to see whether the name is unique
  224. * return false if a field with the name already exists, true otherwise
  225. * if an id is specified, then the corresponding record is excluded from the
  226. * uniqueness test.
  227. * @param string $p_name custom field name
  228. * @param int $p_custom_field_id custom field id
  229. * @return bool
  230. * @access public
  231. */
  232. function custom_field_is_name_unique( $p_name, $p_custom_field_id = null ) {
  233. $t_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  234. $query = "SELECT COUNT(*)
  235. FROM $t_custom_field_table
  236. WHERE name=" . db_param();
  237. if( $p_custom_field_id !== null ) {
  238. $c_id = db_prepare_int( $p_custom_field_id );
  239. $query .= ' AND (id <> ' . db_param() . ')';
  240. }
  241. $result = db_query_bound( $query, ( ($p_custom_field_id !== null) ? Array( $p_name, $c_id ) : Array( $p_name ) ) );
  242. $count = db_result( $result );
  243. if( $count > 0 ) {
  244. return false;
  245. } else {
  246. return true;
  247. }
  248. }
  249. /**
  250. * Check to see whether the name is unique
  251. * return true if the name has not been used, error otherwise
  252. * @param string $p_name Custom field name
  253. * @return bool
  254. * @access public
  255. */
  256. function custom_field_ensure_name_unique( $p_name ) {
  257. if( custom_field_is_name_unique( $p_name ) ) {
  258. return true;
  259. } else {
  260. trigger_error( ERROR_CUSTOM_FIELD_NAME_NOT_UNIQUE, ERROR );
  261. }
  262. }
  263. /**
  264. * Return true if the user can read the value of the field for the given bug,
  265. * false otherwise.
  266. * @param int $p_field_id custom field id
  267. * @param int $p_bug_id bug id
  268. * @param int $p_user_id user id
  269. * @return bool
  270. * @access public
  271. */
  272. function custom_field_has_read_access( $p_field_id, $p_bug_id, $p_user_id = null ) {
  273. custom_field_ensure_exists( $p_field_id );
  274. if( null === $p_user_id ) {
  275. $p_user_id = auth_get_current_user_id();
  276. }
  277. $t_access_level_r = custom_field_get_field( $p_field_id, 'access_level_r' );
  278. $t_project_id = bug_get_field( $p_bug_id, 'project_id' );
  279. return access_has_project_level( $t_access_level_r, $t_project_id, $p_user_id );
  280. }
  281. /**
  282. * Return true if the user can read the value of the field for the given project,
  283. * false otherwise.
  284. * @param int $p_field_id custom field id
  285. * @param int $p_project_id bug id
  286. * @param int $p_user_id user id
  287. * @return bool
  288. * @access public
  289. */
  290. function custom_field_has_read_access_by_project_id( $p_field_id, $p_project_id, $p_user_id = null ) {
  291. custom_field_ensure_exists( $p_field_id );
  292. if( null === $p_user_id ) {
  293. $p_user_id = auth_get_current_user_id();
  294. }
  295. $t_access_level_r = custom_field_get_field( $p_field_id, 'access_level_r' );
  296. return access_has_project_level( $t_access_level_r, $p_project_id, $p_user_id );
  297. }
  298. /**
  299. * Return true if the user can modify the value of the field for the given project,
  300. * false otherwise.
  301. * @param int $p_field_id custom field id
  302. * @param int $p_project_id bug id
  303. * @param int $p_user_id user id
  304. * @return bool
  305. * @access public
  306. */
  307. function custom_field_has_write_access_to_project( $p_field_id, $p_project_id, $p_user_id = null ) {
  308. custom_field_ensure_exists( $p_field_id );
  309. if( null === $p_user_id ) {
  310. $p_user_id = auth_get_current_user_id();
  311. }
  312. $t_access_level_rw = custom_field_get_field( $p_field_id, 'access_level_rw' );
  313. return access_has_project_level( $t_access_level_rw, $p_project_id, $p_user_id );
  314. }
  315. /**
  316. * Return true if the user can modify the value of the field for the given bug,
  317. * false otherwise.
  318. * @param int $p_field_id custom field id
  319. * @param int $p_bug_id bug id
  320. * @param int $p_user_id user id
  321. * @return bool
  322. * @access public
  323. */
  324. function custom_field_has_write_access( $p_field_id, $p_bug_id, $p_user_id = null ) {
  325. $t_project_id = bug_get_field( $p_bug_id, 'project_id' );
  326. return( custom_field_has_write_access_to_project( $p_field_id, $t_project_id, $p_user_id ) );
  327. }
  328. /**
  329. * create a new custom field with the name $p_name.
  330. * the definition are the default values and can be changed later.
  331. * return the ID of the new definition.
  332. * @param string $p_name custom field name
  333. * @return int custom field id
  334. * @access public
  335. */
  336. function custom_field_create( $p_name ) {
  337. if( string_contains_scripting_chars( $p_name ) ) {
  338. error_parameters( lang_get( 'custom_field_name' ) );
  339. trigger_error( ERROR_CUSTOM_FIELD_INVALID_PROPERTY, ERROR );
  340. }
  341. $c_name = trim( $p_name );
  342. if( is_blank( $c_name ) ) {
  343. error_parameters( 'name' );
  344. trigger_error( ERROR_EMPTY_FIELD, ERROR );
  345. }
  346. custom_field_ensure_name_unique( $c_name );
  347. $t_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  348. $query = "INSERT INTO $t_custom_field_table
  349. ( name, possible_values )
  350. VALUES
  351. ( " . db_param() . ',' . db_param() . ')';
  352. db_query_bound( $query, Array( $c_name, '' ) );
  353. return db_insert_id( $t_custom_field_table );
  354. }
  355. /**
  356. * Update the field definition.
  357. * return true on success, false on failure
  358. * @param int $p_field_id custom field id
  359. * @param array custom field definition
  360. * @return bool
  361. * @access public
  362. */
  363. function custom_field_update( $p_field_id, $p_def_array ) {
  364. if( string_contains_scripting_chars( $p_def_array['name'] ) ) {
  365. error_parameters( lang_get( 'custom_field_name' ) );
  366. trigger_error( ERROR_CUSTOM_FIELD_INVALID_PROPERTY, ERROR );
  367. }
  368. if( is_blank( $p_def_array['name'] ) ) {
  369. error_parameters( 'name' );
  370. trigger_error( ERROR_EMPTY_FIELD, ERROR );
  371. }
  372. if( $p_def_array['access_level_rw'] < $p_def_array['access_level_r'] ) {
  373. error_parameters(
  374. lang_get( 'custom_field_access_level_r' ) . ', ' .
  375. lang_get( 'custom_field_access_level_rw' )
  376. );
  377. trigger_error( ERROR_CUSTOM_FIELD_INVALID_PROPERTY, ERROR );
  378. }
  379. if ( $p_def_array['length_min'] < 0
  380. || ( $p_def_array['length_max'] != 0 && $p_def_array['length_min'] > $p_def_array['length_max'] )
  381. ) {
  382. error_parameters( lang_get( 'custom_field_length_min' ) . ', ' . lang_get( 'custom_field_length_max' ));
  383. trigger_error( ERROR_CUSTOM_FIELD_INVALID_PROPERTY, ERROR );
  384. }
  385. if( !custom_field_is_name_unique( $p_def_array['name'], $p_field_id ) ) {
  386. trigger_error( ERROR_CUSTOM_FIELD_NAME_NOT_UNIQUE, ERROR );
  387. }
  388. # Build fields update statement
  389. $t_update = '';
  390. foreach( $p_def_array as $field => $value ) {
  391. $t_update .= "$field = " . db_param() . ', ';
  392. $t_params[] = is_bool( $value ) ? db_prepare_bool( $value ) : $value;
  393. }
  394. # If there are fields to update, execute SQL
  395. if( $t_update !== '' ) {
  396. $t_mantis_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  397. $t_query = "
  398. UPDATE $t_mantis_custom_field_table
  399. SET " . rtrim( $t_update, ', ' ) . "
  400. WHERE id = " . db_param();
  401. $t_params[] = $p_field_id;
  402. db_query_bound( $t_query, $t_params );
  403. custom_field_clear_cache( $p_field_id );
  404. # db_query errors on failure so:
  405. return true;
  406. }
  407. return false;
  408. }
  409. /**
  410. * Add a custom field to a project
  411. * return true on success, false on failure or if already added
  412. * @param int $p_field_id custom field id
  413. * @param int $p_project_id project id
  414. * @return bool
  415. * @access public
  416. */
  417. function custom_field_link( $p_field_id, $p_project_id ) {
  418. $c_field_id = db_prepare_int( $p_field_id );
  419. $c_project_id = db_prepare_int( $p_project_id );
  420. custom_field_ensure_exists( $p_field_id );
  421. project_ensure_exists( $p_project_id );
  422. if( custom_field_is_linked( $p_field_id, $p_project_id ) ) {
  423. return false;
  424. }
  425. $t_custom_field_project_table = db_get_table( 'mantis_custom_field_project_table' );
  426. $query = "INSERT INTO $t_custom_field_project_table
  427. ( field_id, project_id )
  428. VALUES
  429. ( " . db_param() . ', ' . db_param() . ')';
  430. db_query_bound( $query, Array( $c_field_id, $c_project_id ) );
  431. # db_query errors on failure so:
  432. return true;
  433. }
  434. /**
  435. * Remove a custom field from a project
  436. * return true on success, false on failure
  437. *
  438. * The values for the custom fields are not deleted. This is to allow for the
  439. * case where a bug is moved to another project that has the field, or the
  440. * field is linked again to the project.
  441. * @param int $p_field_id custom field id
  442. * @param int $p_project_id project id
  443. * @return bool
  444. * @access public
  445. */
  446. function custom_field_unlink( $p_field_id, $p_project_id ) {
  447. $c_field_id = db_prepare_int( $p_field_id );
  448. $c_project_id = db_prepare_int( $p_project_id );
  449. $t_custom_field_project_table = db_get_table( 'mantis_custom_field_project_table' );
  450. $query = "DELETE FROM $t_custom_field_project_table
  451. WHERE field_id = " . db_param() . " AND
  452. project_id = " . db_param();
  453. db_query_bound( $query, Array( $c_field_id, $c_project_id ) );
  454. # db_query errors on failure so:
  455. return true;
  456. }
  457. /**
  458. * Delete the field definition and all associated values and project associations
  459. * return true on success, false on failure
  460. * @param int $p_field_id custom field id
  461. * @return bool
  462. * @access public
  463. */
  464. function custom_field_destroy( $p_field_id ) {
  465. $c_field_id = db_prepare_int( $p_field_id );
  466. # delete all values
  467. $t_custom_field_string_table = db_get_table( 'mantis_custom_field_string_table' );
  468. $query = "DELETE FROM $t_custom_field_string_table
  469. WHERE field_id=" . db_param();
  470. db_query_bound( $query, Array( $c_field_id ) );
  471. # delete all project associations
  472. $t_custom_field_project_table = db_get_table( 'mantis_custom_field_project_table' );
  473. $query = "DELETE FROM $t_custom_field_project_table
  474. WHERE field_id=" . db_param();
  475. db_query_bound( $query, Array( $c_field_id ) );
  476. $t_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  477. # delete the definition
  478. $query = "DELETE FROM $t_custom_field_table
  479. WHERE id=" . db_param();
  480. db_query_bound( $query, Array( $c_field_id ) );
  481. custom_field_clear_cache( $p_field_id );
  482. # db_query errors on failure so:
  483. return true;
  484. }
  485. /**
  486. * Delete all associations of custom fields to the specified project
  487. * return true on success, false on failure
  488. *
  489. * To be called from within project_delete().
  490. * @param int $p_project_id project id
  491. * @return bool
  492. * @access public
  493. */
  494. function custom_field_unlink_all( $p_project_id ) {
  495. $c_project_id = db_prepare_int( $p_project_id );
  496. # delete all project associations
  497. $t_custom_field_project_table = db_get_table( 'mantis_custom_field_project_table' );
  498. $query = "DELETE FROM $t_custom_field_project_table
  499. WHERE project_id=" . db_param();
  500. db_query_bound( $query, Array( $c_project_id ) );
  501. # db_query errors on failure so:
  502. return true;
  503. }
  504. /**
  505. * Delete all custom values associated with the specified bug.
  506. * return true on success, false on failure
  507. *
  508. * To be called from bug_delete().
  509. * @param int $p_bug_id bug id
  510. * @return bool
  511. * @access public
  512. */
  513. function custom_field_delete_all_values( $p_bug_id ) {
  514. $c_bug_id = db_prepare_int( $p_bug_id );
  515. $t_custom_field_string_table = db_get_table( 'mantis_custom_field_string_table' );
  516. $query = "DELETE FROM $t_custom_field_string_table
  517. WHERE bug_id=" . db_param();
  518. db_query_bound( $query, Array( $c_bug_id ) );
  519. # db_query errors on failure so:
  520. return true;
  521. }
  522. /**
  523. * Get the id of the custom field with the specified name.
  524. * false is returned if no custom field found with the specified name.
  525. * @param string $p_field_name custom field name
  526. * @param int $p_truncated_length
  527. * @return bool|int false or custom field id
  528. * @access public
  529. */
  530. function custom_field_get_id_from_name( $p_field_name, $p_truncated_length = null ) {
  531. global $g_cache_name_to_id_map;
  532. if ( is_blank( $p_field_name ) ) {
  533. return false;
  534. }
  535. if ( isset( $g_cache_name_to_id_map[$p_field_name] ) ) {
  536. return $g_cache_name_to_id_map[$p_field_name];
  537. }
  538. $t_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  539. if(( null === $p_truncated_length ) || ( utf8_strlen( $p_field_name ) != $p_truncated_length ) ) {
  540. $query = "SELECT id FROM $t_custom_field_table WHERE name = " . db_param();
  541. $c_field_name = $p_field_name;
  542. } else {
  543. # This is to handle the case where we potentially only have a
  544. # truncated part of the custom field name. This happens when we
  545. # are getting the field from the history logs (as the history's
  546. # field_name column used to be 32 while custom field name is 64).
  547. # This is needed to handle legacy database entries, as any
  548. # history record created after 1.1.0a4 has the correct field
  549. # size (see #8002)
  550. $query = "SELECT id FROM $t_custom_field_table WHERE name LIKE " . db_param();
  551. $c_field_name = $p_field_name . '%';
  552. }
  553. $t_result = db_query_bound( $query, array( $c_field_name ) );
  554. if( db_num_rows( $t_result ) == 0 ) {
  555. $g_cache_name_to_id_map[$p_field_name] = false;
  556. return false;
  557. }
  558. $row = db_fetch_array( $t_result );
  559. $g_cache_name_to_id_map[$p_field_name] = $row['id'];
  560. return $row['id'];
  561. }
  562. /**
  563. * Return an array of ids of custom fields bound to the specified project
  564. *
  565. * The ids will be sorted based on the sequence number associated with the binding
  566. * @param int $p_project_id project id
  567. * @return array
  568. * @access public
  569. */
  570. function custom_field_get_linked_ids( $p_project_id = ALL_PROJECTS ) {
  571. global $g_cache_cf_linked, $g_cache_custom_field;
  572. if( !isset( $g_cache_cf_linked[$p_project_id] ) ) {
  573. $t_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  574. $t_custom_field_project_table = db_get_table( 'mantis_custom_field_project_table' );
  575. if( ALL_PROJECTS == $p_project_id ) {
  576. $t_project_user_list_table = db_get_table( 'mantis_project_user_list_table' );
  577. $t_project_table = db_get_table( 'mantis_project_table' );
  578. $t_user_table = db_get_table( 'mantis_user_table' );
  579. $t_user_id = auth_get_current_user_id();
  580. # Select only the ids of custom fields in projects the user has access to
  581. # - all custom fields in public projects,
  582. # - those in private projects where the user is listed
  583. # - in private projects where the user is implicitly listed
  584. $t_query = "
  585. SELECT DISTINCT cft.id
  586. FROM $t_custom_field_table cft
  587. JOIN $t_custom_field_project_table cfpt ON cfpt.field_id = cft.id
  588. JOIN $t_project_table pt
  589. ON pt.id = cfpt.project_id AND pt.enabled = " . db_prepare_bool( true ) . "
  590. LEFT JOIN $t_project_user_list_table pult
  591. ON pult.project_id = cfpt.project_id AND pult.user_id = " . db_param() . "
  592. , $t_user_table ut
  593. WHERE ut.id = " . db_param() . "
  594. AND ( pt.view_state = " . VS_PUBLIC . "
  595. OR pult.user_id = ut.id
  596. ";
  597. $t_params = array( $t_user_id, $t_user_id );
  598. # Add private access clause and related parameter
  599. $t_private_access = config_get( 'private_project_threshold' );
  600. if( is_array( $t_private_access ) ) {
  601. if( 1 == count( $t_private_access ) ) {
  602. $t_access_clause = '= ' . db_param();
  603. $t_params[] = array_shift( $t_private_access );
  604. } else {
  605. $t_access_clause = 'IN (';
  606. foreach( $t_private_access as $t_elem ) {
  607. $t_access_clause .= db_param() . ',';
  608. $t_params[] = $t_elem;
  609. }
  610. $t_access_clause = rtrim( $t_access_clause, ',') . ')';
  611. }
  612. } else {
  613. $t_access_clause = '>=' . db_param();
  614. $t_params[] = $t_private_access;
  615. }
  616. $t_query .= "OR ( pult.user_id IS NULL AND ut.access_level $t_access_clause ) )";
  617. } else {
  618. if( is_array( $p_project_id ) ) {
  619. if( 1 == count( $p_project_id ) ) {
  620. $t_project_clause = '= ' . db_param();
  621. $t_params[] = array_shift( $p_project_id );
  622. } else {
  623. $t_project_clause = 'IN (';
  624. foreach( $p_project_id as $t_project ) {
  625. $t_project_clause .= db_param() . ',';
  626. $t_params[] = $t_project;
  627. }
  628. $t_project_clause = rtrim( $t_project_clause, ',') . ')';
  629. }
  630. } else {
  631. $t_project_clause = '= ' . db_param();
  632. $t_params[] = $p_project_id;
  633. }
  634. $t_query = "
  635. SELECT cft.id
  636. FROM $t_custom_field_table cft
  637. JOIN $t_custom_field_project_table cfpt ON cfpt.field_id = cft.id
  638. WHERE cfpt.project_id $t_project_clause
  639. ORDER BY sequence ASC, name ASC";
  640. }
  641. $result = db_query_bound( $t_query, $t_params );
  642. $t_row_count = db_num_rows( $result );
  643. $t_ids = array();
  644. for( $i = 0;$i < $t_row_count;$i++ ) {
  645. $row = db_fetch_array( $result );
  646. array_push( $t_ids, $row['id'] );
  647. }
  648. custom_field_cache_array_rows( $t_ids );
  649. $g_cache_cf_linked[$p_project_id] = $t_ids;
  650. } else {
  651. $t_ids = $g_cache_cf_linked[$p_project_id];
  652. }
  653. return $t_ids;
  654. }
  655. /**
  656. * Return an array all custom field ids sorted by name
  657. * @return array
  658. * @access public
  659. */
  660. function custom_field_get_ids() {
  661. global $g_cache_cf_list, $g_cache_custom_field;
  662. if( $g_cache_cf_list === NULL ) {
  663. $t_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  664. $query = "SELECT *
  665. FROM $t_custom_field_table
  666. ORDER BY name ASC";
  667. $result = db_query_bound( $query );
  668. $t_row_count = db_num_rows( $result );
  669. $t_ids = array();
  670. for( $i = 0;$i < $t_row_count;$i++ ) {
  671. $row = db_fetch_array( $result );
  672. $g_cache_custom_field[(int) $row['id']] = $row;
  673. array_push( $t_ids, $row['id'] );
  674. }
  675. $g_cache_cf_list = $t_ids;
  676. } else {
  677. $t_ids = $g_cache_cf_list;
  678. }
  679. return $t_ids;
  680. }
  681. /**
  682. * Return an array of ids of projects related to the specified custom field
  683. * (the array may be empty)
  684. * @param int $p_field_id custom field id
  685. * @return array
  686. * @access public
  687. */
  688. function custom_field_get_project_ids( $p_field_id ) {
  689. $c_field_id = db_prepare_int( $p_field_id );
  690. $t_custom_field_project_table = db_get_table( 'mantis_custom_field_project_table' );
  691. $query = "SELECT project_id
  692. FROM $t_custom_field_project_table
  693. WHERE field_id = " . db_param();
  694. $result = db_query_bound( $query, Array( $c_field_id ) );
  695. $t_row_count = db_num_rows( $result );
  696. $t_ids = array();
  697. for( $i = 0;$i < $t_row_count;$i++ ) {
  698. $row = db_fetch_array( $result );
  699. array_push( $t_ids, $row['project_id'] );
  700. }
  701. return $t_ids;
  702. }
  703. /**
  704. * Return a field definition row for the field or error if the field does not exist
  705. * @param int $p_field_id custom field id
  706. * @return array custom field definition
  707. * @access public
  708. */
  709. function custom_field_get_definition( $p_field_id ) {
  710. return custom_field_cache_row( $p_field_id );
  711. }
  712. /**
  713. * Return a single database field from a custom field definition row for the field
  714. * if the database field does not exist, display a warning and return ''
  715. * @param int $p_field_id custom field id
  716. * @param int $p_field_name custom field name
  717. * @return string
  718. * @access public
  719. */
  720. function custom_field_get_field( $p_field_id, $p_field_name ) {
  721. $row = custom_field_get_definition( $p_field_id );
  722. if( isset( $row[$p_field_name] ) ) {
  723. return $row[$p_field_name];
  724. } else {
  725. error_parameters( $p_field_name );
  726. trigger_error( ERROR_DB_FIELD_NOT_FOUND, WARNING );
  727. return '';
  728. }
  729. }
  730. /**
  731. * Get the value of a custom field for the given bug
  732. * @todo return values are unclear... should we error when access is denied
  733. * and provide an api to check whether it will be?
  734. * @param int $p_field_id custom field id
  735. * @param int $p_bug_id bug id
  736. * @return mixed: value is defined, null: no value is defined, false: read access is denied
  737. * @access public
  738. */
  739. function custom_field_get_value( $p_field_id, $p_bug_id ) {
  740. $c_field_id = db_prepare_int( $p_field_id );
  741. $c_bug_id = db_prepare_int( $p_bug_id );
  742. $row = custom_field_cache_row( $p_field_id );
  743. if( !custom_field_has_read_access( $p_field_id, $p_bug_id, auth_get_current_user_id() ) ) {
  744. return false;
  745. }
  746. $t_custom_field_string_table = db_get_table( 'mantis_custom_field_string_table' );
  747. $query = "SELECT value
  748. FROM $t_custom_field_string_table
  749. WHERE bug_id=" . db_param() . " AND
  750. field_id=" . db_param();
  751. $result = db_query_bound( $query, Array( $c_bug_id, $c_field_id ) );
  752. if( db_num_rows( $result ) > 0 ) {
  753. return custom_field_database_to_value( db_result( $result ), $row['type'] );
  754. } else {
  755. return null;
  756. }
  757. }
  758. /**
  759. * Gets the custom fields array for the given bug readable by specified level.
  760. * Array keys are custom field names. Array is sorted by custom field sequence number;
  761. * Array items are arrays with the next keys:
  762. * 'type', 'value', 'access_level_r'
  763. * @param int $p_bug_id bug id
  764. * @param int $p_user_access_level Access level
  765. * @return array
  766. * @access public
  767. */
  768. function custom_field_get_linked_fields( $p_bug_id, $p_user_access_level ) {
  769. $t_custom_fields = custom_field_get_all_linked_fields( $p_bug_id );
  770. # removing restricted fields
  771. foreach( $t_custom_fields as $t_custom_field_name => $t_custom_field_data ) {
  772. if( $p_user_access_level < $t_custom_field_data['access_level_r'] ) {
  773. unset( $t_custom_fields[$t_custom_field_name] );
  774. }
  775. }
  776. return $t_custom_fields;
  777. }
  778. /**
  779. * Gets the custom fields array for the given bug. Array keys are custom field names.
  780. * Array is sorted by custom field sequence number; Array items are arrays with the next keys:
  781. * 'type', 'value', 'access_level_r'
  782. * @param int $p_bug_id bug id
  783. * @return array
  784. * @access public
  785. */
  786. function custom_field_get_all_linked_fields( $p_bug_id ) {
  787. global $g_cached_custom_field_lists;
  788. if( !is_array( $g_cached_custom_field_lists ) ) {
  789. $g_cached_custom_field_lists = array();
  790. }
  791. # is the list in cache ?
  792. if( !array_key_exists( $p_bug_id, $g_cached_custom_field_lists ) ) {
  793. $t_custom_field_project_table = db_get_table( 'mantis_custom_field_project_table' );
  794. $t_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  795. $t_custom_field_string_table = db_get_table( 'mantis_custom_field_string_table' );
  796. $query = "
  797. SELECT f.name, f.type, f.access_level_r, f.default_value, f.type, s.value
  798. FROM $t_custom_field_project_table p
  799. INNER JOIN $t_custom_field_table f ON f.id = p.field_id
  800. LEFT JOIN $t_custom_field_string_table s
  801. ON s.field_id = p.field_id AND s.bug_id = " . db_param() . "
  802. WHERE p.project_id = " . db_param() . "
  803. ORDER BY p.sequence ASC, f.name ASC";
  804. $t_params = array(
  805. (int)$p_bug_id,
  806. bug_get_field( $p_bug_id, 'project_id' )
  807. );
  808. $result = db_query_bound( $query, $t_params );
  809. $t_row_count = db_num_rows( $result );
  810. $t_custom_fields = array();
  811. for( $i = 0;$i < $t_row_count;++$i ) {
  812. $row = db_fetch_array( $result );
  813. if( is_null( $row['value'] ) ) {
  814. $t_value = $row['default_value'];
  815. } else {
  816. $t_value = custom_field_database_to_value( $row['value'], $row['type'] );
  817. }
  818. $t_custom_fields[$row['name']] = array(
  819. 'type' => $row['type'],
  820. 'value' => $t_value,
  821. 'access_level_r' => $row['access_level_r'],
  822. );
  823. }
  824. $g_cached_custom_field_lists[$p_bug_id] = $t_custom_fields;
  825. }
  826. return $g_cached_custom_field_lists[$p_bug_id];
  827. }
  828. /**
  829. * Gets the sequence number for the specified custom field for the specified
  830. * project. Returns false in case of error.
  831. * @param int $p_field_id custom field id
  832. * @param int $p_project_id project id
  833. * @return int|bool
  834. * @access public
  835. */
  836. function custom_field_get_sequence( $p_field_id, $p_project_id ) {
  837. $c_field_id = db_prepare_int( $p_field_id );
  838. $c_project_id = db_prepare_int( $p_project_id );
  839. $t_custom_field_project_table = db_get_table( 'mantis_custom_field_project_table' );
  840. $query = "SELECT sequence
  841. FROM $t_custom_field_project_table
  842. WHERE field_id=" . db_param() . " AND
  843. project_id=" . db_param();
  844. $result = db_query_bound( $query, Array( $c_field_id, $c_project_id ), 1 );
  845. if( 0 == db_num_rows( $result ) ) {
  846. return false;
  847. }
  848. $t_row = db_fetch_array( $result );
  849. return $t_row['sequence'];
  850. }
  851. /**
  852. * Allows the validation of a custom field value without setting it
  853. * or needing a bug to exist.
  854. * @param int $p_field_id custom field id
  855. * @param string $p_value custom field value
  856. * @return bool
  857. * @access public
  858. */
  859. function custom_field_validate( $p_field_id, $p_value ) {
  860. $c_field_id = db_prepare_int( $p_field_id );
  861. custom_field_ensure_exists( $p_field_id );
  862. $t_custom_field_table = db_get_table( 'mantis_custom_field_table' );
  863. $query = "SELECT name, type, possible_values, valid_regexp,
  864. access_level_rw, length_min, length_max, default_value
  865. FROM $t_custom_field_table
  866. WHERE id=" . db_param();
  867. $result = db_query_bound( $query, Array( $c_field_id ) );
  868. $row = db_fetch_array( $result );
  869. $t_name = $row['name'];
  870. $t_type = $row['type'];
  871. $t_possible_values = $row['possible_values'];
  872. $t_valid_regexp = $row['valid_regexp'];
  873. $t_length_min = $row['length_min'];
  874. $t_length_max = $row['length_max'];
  875. $t_default_value = $row['default_value'];
  876. $t_valid = true;
  877. $t_length = utf8_strlen( $p_value );
  878. switch ($t_type) {
  879. case CUSTOM_FIELD_TYPE_STRING:
  880. case CUSTOM_FIELD_TYPE_TEMPLATE:
  881. # Empty fields are valid
  882. if( $t_length == 0 ) {
  883. break;
  884. }
  885. # Regular expression string validation
  886. if( !is_blank( $t_valid_regexp ) ) {
  887. $t_valid &= preg_match( "/$t_valid_regexp/", $p_value );
  888. }
  889. # Check the length of the string
  890. $t_valid &= ( 0 == $t_length_min ) || ( $t_length >= $t_length_min );
  891. $t_valid &= ( 0 == $t_length_max ) || ( $t_length <= $t_length_max );
  892. break;
  893. case CUSTOM_FIELD_TYPE_NUMERIC:
  894. # Empty fields are valid
  895. if( $t_length == 0 ) {
  896. break;
  897. }
  898. $t_valid &= is_numeric( $p_value );
  899. # Check the length of the number
  900. $t_valid &= ( 0 == $t_length_min ) || ( $t_length >= $t_length_min );
  901. $t_valid &= ( 0 == $t_length_max ) || ( $t_length <= $t_length_max );
  902. break;
  903. case CUSTOM_FIELD_TYPE_FLOAT:
  904. # Empty fields are valid
  905. if( $t_length == 0 ) {
  906. break;
  907. }
  908. # Allow both integer and float numbers
  909. $t_valid &= is_numeric( $p_value ) || is_float( $p_value );
  910. # Check the length of the number
  911. $t_valid &= ( 0 == $t_length_min ) || ( $t_length >= $t_length_min );
  912. $t_valid &= ( 0 == $t_length_max ) || ( $t_length <= $t_length_max );
  913. break;
  914. case CUSTOM_FIELD_TYPE_DATE:
  915. # gpc_get_cf for date returns the value from strftime
  916. # Either false (php >= 5.1) or -1 (php < 5.1) for failure
  917. $t_valid &= ( $p_value == null ) || ( ( $p_value !== false ) && ( $p_value > 0 ) );
  918. break;
  919. case CUSTOM_FIELD_TYPE_CHECKBOX:
  920. case CUSTOM_FIELD_TYPE_MULTILIST:
  921. # Checkbox fields can hold a null value (when no checkboxes are ticked)
  922. if ( $p_value === '' ) {
  923. break;
  924. }
  925. # If checkbox field value is not null then we need to validate it... (note: no "break" statement here!)
  926. $t_values = explode( '|', $p_value );
  927. $t_possible_values = custom_field_prepare_possible_values( $row['possible_values'] );
  928. $t_possible_values = explode( '|', $t_possible_values );
  929. $t_invalid_values = array_diff( $t_values, $t_possible_values );
  930. $t_valid &= ( count( $t_invalid_values ) == 0 );
  931. break;
  932. case CUSTOM_FIELD_TYPE_LIST:
  933. case CUSTOM_FIELD_TYPE_ENUM:
  934. case CUSTOM_FIELD_TYPE_RADIO:
  935. # List fields can be empty (when they are not shown on the form or shown with no default values and never clicked)
  936. if ( is_blank( $p_value ) ) {
  937. break;
  938. }
  939. # If list field value is not empty then we need to validate it... (note: no "break" statement here!)
  940. $t_possible_values = custom_field_prepare_possible_values( $row['possible_values'] );
  941. $t_values_arr = explode( '|', $t_possible_values );
  942. $t_valid &= in_array( $p_value, $t_values_arr );
  943. break;
  944. case CUSTOM_FIELD_TYPE_EMAIL:
  945. if ( $p_value !== '' ) {
  946. $t_valid &= email_is_valid( $p_value );
  947. }
  948. break;
  949. default:
  950. break;
  951. }
  952. return (bool)$t_valid;
  953. }
  954. /**
  955. * $p_possible_values: possible values to be pre-processed. If it has enum values,
  956. * it will be left as is. If it has a method, it will be replaced by the list.
  957. * @param string $p_possible_values
  958. * @return string|array
  959. * @access public
  960. */
  961. function custom_field_prepare_possible_values( $p_possible_values ) {
  962. $t_possible_values = $p_possible_values;
  963. if( !is_blank( $t_possible_values ) && ( $t_possible_values[0] == '=' ) ) {
  964. $t_possible_values = helper_call_custom_function( 'enum_' . utf8_substr( $t_possible_values, 1 ), array() );
  965. }
  966. return $t_possible_values;
  967. }
  968. /**
  969. * Get All Possible Values for a Field.
  970. * @param array $p_field_def custom field definition
  971. * @param int $p_project_id project id
  972. * @return bool|array
  973. * @access public
  974. */
  975. function custom_field_distinct_values( $p_field_def, $p_project_id = ALL_PROJECTS ) {
  976. global $g_custom_field_type_definition;
  977. $c_field_id = $p_field_def['id'];
  978. $c_project_id = db_prepare_int( $p_project_id );
  979. $t_custom_field_string_table = db_get_table( 'mantis_custom_field_string_table' );
  980. $t_mantis_bug_table = db_get_table( 'mantis_bug_table' );
  981. $t_return_arr = array();
  982. # If an enumeration type, we get all possible values, not just used values
  983. if( isset( $g_custom_field_type_definition[$p_field_def['type']]['#function_return_distinct_values'] ) ) {
  984. return call_user_func( $g_custom_field_type_definition[$p_field_def['type']]['#function_return_distinct_values'], $p_field_def );
  985. } else {
  986. $t_from = "$t_custom_field_string_table cfst";
  987. $t_where1 = 'cfst.field_id = ' . db_param();
  988. $t_params[] = $p_field_def['id'];
  989. if( ALL_PROJECTS != $p_project_id ) {
  990. $t_from .= " JOIN $t_mantis_bug_table bt ON bt.id = cfst.bug_id";
  991. $t_where2 = 'AND bt.project_id = ' . db_param();
  992. $t_params[] = $p_project_id;
  993. } else {
  994. $t_where2 = '';
  995. }
  996. $t_query = "
  997. SELECT DISTINCT cfst.value
  998. FROM $t_from
  999. WHERE $t_where1 $t_where2
  1000. ORDER BY cfst.value";
  1001. $t_result = db_query_bound( $t_query, $t_params );
  1002. $t_row_count = db_num_rows( $t_result );
  1003. if( 0 == $t_row_count ) {
  1004. return false;
  1005. }
  1006. for( $i = 0;$i < $t_row_count;$i++ ) {
  1007. $row = db_fetch_array( $t_result );
  1008. if( !is_blank( trim( $row['value'] ) ) ) {
  1009. array_push( $t_return_arr, $row['value'] );
  1010. }
  1011. }
  1012. }
  1013. return $t_return_arr;
  1014. }
  1015. /**
  1016. * Convert the value to save it into the database, depending of the type
  1017. * return value for database
  1018. * @param mixed $p_value
  1019. * @param int $p_type
  1020. * @return mixed
  1021. * @access public
  1022. */
  1023. function custom_field_value_to_database( $p_value, $p_type ) {
  1024. global $g_custom_field_type_definition;
  1025. if( isset( $g_custom_field_type_definition[$p_type]['#function_value_to_database'] ) ) {
  1026. return call_user_func( $g_custom_field_type_definition[$p_type]['#function_value_to_database'], $p_value );
  1027. }
  1028. return $p_value;
  1029. }
  1030. /**
  1031. * Convert the database-value to value, depending of the type
  1032. * return value for further operation
  1033. * @param mixed $p_value
  1034. * @param int $p_type
  1035. * @return mixed
  1036. * @access public
  1037. */
  1038. function custom_field_database_to_value( $p_value, $p_type ) {
  1039. global $g_custom_field_type_definition;
  1040. if( isset( $g_custom_field_type_definition[$p_type]['#function_database_to_value'] ) ) {
  1041. return call_user_func( $g_custom_field_type_definition[$p_type]['#function_database_to_value'], $p_value );
  1042. }
  1043. return $p_value;
  1044. }
  1045. /**
  1046. * Convert the default-value to value depending on the type. For example, in case of date, this
  1047. * would translate 'tomorrow' to tomorrow's date.
  1048. * @param mixed $p_value
  1049. * @param int $p_type
  1050. * @return mixed
  1051. * @access public
  1052. */
  1053. function custom_field_default_to_value( $p_value, $p_type ) {
  1054. global $g_custom_field_type_definition;
  1055. if( isset( $g_custom_field_type_definition[$p_type]['#function_default_to_value'] ) ) {
  1056. return call_user_func( $g_custom_field_type_definition[$p_type]['#function_default_to_value'], $p_value );
  1057. }
  1058. return $p_value;
  1059. }
  1060. /**
  1061. * Set the value of a custom field for a given bug
  1062. * return true on success, false on failure
  1063. * @param int $p_field_id custom field id
  1064. * @param int $p_bug_id bug id
  1065. * @param mixed $p_value
  1066. * @param boolean $p_log create history logs for new values
  1067. * @return bool
  1068. * @access public
  1069. */
  1070. function custom_field_set_value( $p_field_id, $p_bug_id, $p_value, $p_log_insert=true ) {
  1071. $c_field_id = db_prepare_int( $p_field_id );
  1072. $c_bug_id = db_prepare_int( $p_bug_id );
  1073. custom_field_ensure_exists( $p_field_id );
  1074. if ( !custom_field_validate( $p_field_id, $p_value ) )
  1075. return false;
  1076. $t_name = custom_field_get_field( $p_field_id, 'name' );
  1077. $t_type = custom_field_get_field( $p_field_id, 'type' );
  1078. $t_custom_field_string_table = db_get_table( 'mantis_custom_field_string_table' );
  1079. # Determine whether an existing value needs to be updated or a new value inserted
  1080. $query = "SELECT value
  1081. FROM $t_custom_field_string_table
  1082. WHERE field_id=" . db_param() . " AND
  1083. bug_id=" . db_param();
  1084. $result = db_query_bound( $query, Array( $c_field_id, $c_bug_id ) );
  1085. if( db_num_rows( $result ) > 0 ) {
  1086. $query = "UPDATE $t_custom_field_string_table
  1087. SET value=" . db_param() . "
  1088. WHERE field_id=" . db_param() . " AND
  1089. bug_id=" . db_param();
  1090. db_query_bound( $query, Array( custom_field_value_to_database( $p_value, $t_type ), $c_field_id, $c_bug_id ) );
  1091. $row = db_fetch_array( $result );
  1092. history_log_event_direct( $c_bug_id, $t_name, custom_field_database_to_value( $row['value'], $t_type ), $p_value );
  1093. } else {
  1094. $query = "INSERT INTO $t_custom_field_string_table
  1095. ( field_id, bug_id, value )
  1096. VALUES
  1097. ( " . db_param() . ', ' . db_param() . ', ' . db_param() . ')';
  1098. db_query_bound( $query, Array( $c_field_id, $c_bug_id, custom_field_value_to_database( $p_value, $t_type ) ) );
  1099. # Don't log history events for new bug reports or on other special occasions
  1100. if ( $p_log_insert ) {
  1101. history_log_event_direct( $c_bug_id, $t_name, '', $p_value );
  1102. }
  1103. }
  1104. custom_field_clear_cache( $p_field_id );
  1105. # db_query errors on failure so:
  1106. return true;
  1107. }
  1108. /**
  1109. * Sets the sequence number for the specified custom field for the specified
  1110. * project.
  1111. * @param int $p_field_id custom field id
  1112. * @param int $p_project_id project id
  1113. * @param int $p_sequence
  1114. * @return bool
  1115. * @access public
  1116. */
  1117. function custom_field_set_sequence( $p_field_id, $p_project_id, $p_sequence ) {
  1118. $c_field_id = db_prepare_int( $p_field_id );
  1119. $c_project_id = db_prepare_int( $p_project_id );
  1120. $c_sequence = db_prepare_int( $p_sequence );
  1121. $t_custom_field_project_table = db_get_table( 'mantis_custom_field_project_table' );
  1122. $query = "UPDATE $t_custom_field_project_table
  1123. SET sequence=" . db_param() . "
  1124. WHERE field_id=" . db_param() . " AND
  1125. project_id=" . db_param();
  1126. $result = db_query_bound( $query, Array( $c_sequence, $c_field_id, $c_project_id ) );
  1127. custom_field_clear_cache( $p_field_id );
  1128. return true;
  1129. }
  1130. /**
  1131. * Print an input field
  1132. * $p_field_def contains the definition of the custom field (including its field id)
  1133. * $p_bug_id contains the bug where this field belongs to. If it's left
  1134. * away, it'll default to 0 and thus belong to a new (i.e. non-existant) bug
  1135. * @todo This probably belongs in the print_api.php
  1136. * @param array $p_field_def custom field definition
  1137. * @param int $p_bug_id bug id
  1138. * @access public
  1139. */
  1140. function print_custom_field_input( $p_field_def, $p_bug_id = null ) {
  1141. if( null === $p_bug_id ) {
  1142. $t_custom_field_value = custom_field_default_to_value( $p_field_def['default_value'], $p_field_def['type'] );
  1143. } else {
  1144. $t_custom_field_value = custom_field_get_value( $p_field_def['id'], $p_bug_id );
  1145. # If the custom field value is undefined and the field cannot hold a null value, use the default value instead
  1146. if( $t_custom_field_value === null &&
  1147. ( $p_field_def['type'] == CUSTOM_FIELD_TYPE_ENUM ||
  1148. $p_field_def['type'] == CUSTOM_FIELD_TYPE_LIST ||
  1149. $p_field_def['type'] == CUSTOM_FIELD_TYPE_MULTILIST ||
  1150. $p_field_def['type'] == CUSTOM_FIELD_TYPE_RADIO ) ) {
  1151. $t_custom_field_value = custom_field_default_to_value( $p_field_def['default_value'], $p_field_def['type'] );
  1152. }
  1153. }
  1154. global $g_custom_field_type_definition;
  1155. if( isset( $g_custom_field_type_definition[$p_field_def['type']]['#function_print_input'] ) ) {
  1156. call_user_func( $g_custom_field_type_definition[$p_field_def['type']]['#function_print_input'], $p_field_def, $t_custom_field_value );
  1157. } else {
  1158. trigger_error( ERROR_CUSTOM_FIELD_INVALID_DEFINITION, ERROR );
  1159. }
  1160. }
  1161. /**
  1162. * Prepare a string containing a custom field value for display
  1163. * @todo This probably belongs in the string_api.php
  1164. * @param array $p_def contains the definition of the custom field
  1165. * @param int $p_field_id contains the id of the field
  1166. * @param int $p_bug_id contains the bug id to display the custom field value for
  1167. * @return string
  1168. * @access public
  1169. */
  1170. function string_custom_field_value( $p_def, $p_field_id, $p_bug_id ) {
  1171. $t_custom_field_value = custom_field_get_value( $p_field_id, $p_bug_id );
  1172. if( $t_custom_field_value === null ) {
  1173. return '';
  1174. }
  1175. global $g_custom_field_type_definition;
  1176. if( isset( $g_custom_field_type_definition[$p_def['type']]['#function_string_value'] ) ) {
  1177. return call_user_func( $g_custom_field_type_definition[$p_def['type']]['#function_string_value'], $t_custom_field_value , $p_def );
  1178. }
  1179. return string_display_links( $t_custom_field_value );
  1180. }
  1181. /**
  1182. * Print a custom field value for display
  1183. * @todo This probably belongs in the print_api.php
  1184. * @param array $p_def contains the definition of the custom field
  1185. * @param int $p_field_id contains the id of the field
  1186. * @param int $p_bug_id contains the bug id to display the custom field value for
  1187. * @return null
  1188. * @access public
  1189. */
  1190. function print_custom_field_value( $p_def, $p_field_id, $p_bug_id ) {
  1191. echo string_custom_field_value( $p_def, $p_field_id, $p_bug_id );
  1192. }
  1193. /**
  1194. * Prepare a string containing a custom field value for email
  1195. * @todo This probably belongs in the string_api.php
  1196. * @param string $p_value value of custom field
  1197. * @param int $p_type type of custom field
  1198. * @return string value ready for sending via email
  1199. * @access public
  1200. */
  1201. function string_custom_field_value_for_email( $p_value, $p_def ) {
  1202. global $g_custom_field_type_definition;
  1203. if( isset( $g_custom_field_type_definition[$p_def['type']]['#function_string_value_for_email'] ) ) {
  1204. return call_user_func( $g_custom_field_type_definition[$p_def['type']]['#function_string_value_for_email'], $p_value, $p_def );
  1205. }
  1206. return $p_value;
  1207. }