PageRenderTime 71ms CodeModel.GetById 40ms RepoModel.GetById 0ms app.codeStats 0ms

/core/custom_field_api.php

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