PageRenderTime 49ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/core/filter_api.php

https://github.com/rlerdorf/mantisbt
PHP | 4842 lines | 4326 code | 247 blank | 269 comment | 412 complexity | 050613d29bab1f19940247167e5c88de MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0

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

  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 FilterAPI
  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 current_user_api
  24. */
  25. require_once( 'current_user_api.php' );
  26. /**
  27. * requires user_api
  28. */
  29. require_once( 'user_api.php' );
  30. /**
  31. * requires bug_api
  32. */
  33. require_once( 'bug_api.php' );
  34. /**
  35. * requires collapse_api
  36. */
  37. require_once( 'collapse_api.php' );
  38. /**
  39. * requires relationship_api
  40. */
  41. require_once( 'relationship_api.php' );
  42. /**
  43. * requires tag_api
  44. */
  45. require_once( 'tag_api.php' );
  46. /**
  47. * requires config_filter_defaults_inc
  48. */
  49. require_once( $g_absolute_path . 'config_filter_defaults_inc.php' );
  50. /**
  51. * Allow plugins to define a set of class-based filters, and register/load
  52. * them here to be used by the rest of filter_api.
  53. * @return array Mapping of field name to filter object
  54. */
  55. function filter_get_plugin_filters() {
  56. static $s_field_array = null;
  57. if ( is_null( $s_field_array ) ) {
  58. $s_field_array = array();
  59. $t_all_plugin_filters = event_signal( 'EVENT_FILTER_FIELDS' );
  60. foreach( $t_all_plugin_filters as $t_plugin => $t_plugin_filters ) {
  61. foreach( $t_plugin_filters as $t_callback => $t_plugin_filter_array ) {
  62. if ( is_array( $t_plugin_filter_array ) ) {
  63. foreach( $t_plugin_filter_array as $t_filter_class ) {
  64. if ( class_exists( $t_filter_class ) && is_subclass_of( $t_filter_class, 'MantisFilter' ) ) {
  65. $t_filter_object = new $t_filter_class();
  66. $t_field_name = $t_plugin . '_' . $t_filter_object->field;
  67. $s_field_array[ $t_field_name ] = $t_filter_object;
  68. }
  69. }
  70. }
  71. }
  72. }
  73. }
  74. return $s_field_array;
  75. }
  76. /**
  77. * Get a permalink for the current active filter. The results of using these fields by other users
  78. * can be inconsistent with the original results due to fields like "Myself", "Current Project",
  79. * and due to access level.
  80. * @param array $p_custom_filter
  81. * @return string the search.php?xxxx or an empty string if no criteria applied.
  82. */
  83. function filter_get_url( $p_custom_filter ) {
  84. $t_query = array();
  85. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_PROJECT_ID] ) ) {
  86. $t_project_id = $p_custom_filter[FILTER_PROPERTY_PROJECT_ID];
  87. if( count( $t_project_id ) == 1 && $t_project_id[0] == META_FILTER_CURRENT ) {
  88. $t_project_id = array(
  89. helper_get_current_project(),
  90. );
  91. }
  92. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_PROJECT_ID, $t_project_id );
  93. }
  94. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_FREE_TEXT] ) ) {
  95. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_FREE_TEXT, $p_custom_filter[FILTER_PROPERTY_FREE_TEXT] );
  96. }
  97. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_CATEGORY] ) ) {
  98. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_CATEGORY, $p_custom_filter[FILTER_PROPERTY_CATEGORY] );
  99. }
  100. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_REPORTER_ID] ) ) {
  101. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_REPORTER_ID, $p_custom_filter[FILTER_PROPERTY_REPORTER_ID] );
  102. }
  103. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_STATUS_ID] ) ) {
  104. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_STATUS_ID, $p_custom_filter[FILTER_PROPERTY_STATUS_ID] );
  105. }
  106. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_MONITOR_USER_ID] ) ) {
  107. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_MONITOR_USER_ID, $p_custom_filter[FILTER_PROPERTY_MONITOR_USER_ID] );
  108. }
  109. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_HANDLER_ID] ) ) {
  110. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_HANDLER_ID, $p_custom_filter[FILTER_PROPERTY_HANDLER_ID] );
  111. }
  112. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_NOTE_USER_ID] ) ) {
  113. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_NOTE_USER_ID, $p_custom_filter[FILTER_PROPERTY_NOTE_USER_ID] );
  114. }
  115. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_SEVERITY_ID] ) ) {
  116. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_SEVERITY_ID, $p_custom_filter[FILTER_PROPERTY_SEVERITY_ID] );
  117. }
  118. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_RESOLUTION_ID] ) ) {
  119. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_RESOLUTION_ID, $p_custom_filter[FILTER_PROPERTY_RESOLUTION_ID] );
  120. }
  121. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_PRIORITY_ID] ) ) {
  122. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_PRIORITY_ID, $p_custom_filter[FILTER_PROPERTY_PRIORITY_ID] );
  123. }
  124. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_VIEW_STATE_ID] ) ) {
  125. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_VIEW_STATE_ID, $p_custom_filter[FILTER_PROPERTY_VIEW_STATE_ID] );
  126. }
  127. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_SHOW_STICKY_ISSUES] ) ) {
  128. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_SHOW_STICKY_ISSUES, $p_custom_filter[FILTER_PROPERTY_SHOW_STICKY_ISSUES] );
  129. }
  130. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_PRODUCT_VERSION] ) ) {
  131. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_PRODUCT_VERSION, $p_custom_filter[FILTER_PROPERTY_PRODUCT_VERSION] );
  132. }
  133. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_PRODUCT_BUILD] ) ) {
  134. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_PRODUCT_BUILD, $p_custom_filter[FILTER_PROPERTY_PRODUCT_BUILD] );
  135. }
  136. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_FIXED_IN_VERSION] ) ) {
  137. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_FIXED_IN_VERSION, $p_custom_filter[FILTER_PROPERTY_FIXED_IN_VERSION] );
  138. }
  139. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_TARGET_VERSION] ) ) {
  140. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_TARGET_VERSION, $p_custom_filter[FILTER_PROPERTY_TARGET_VERSION] );
  141. }
  142. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_SORT_FIELD_NAME] ) ) {
  143. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_SORT_FIELD_NAME, $p_custom_filter[FILTER_PROPERTY_SORT_FIELD_NAME] );
  144. }
  145. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_SORT_DIRECTION] ) ) {
  146. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_SORT_DIRECTION, $p_custom_filter[FILTER_PROPERTY_SORT_DIRECTION] );
  147. }
  148. if( !filter_field_is_any( $p_custom_filter[FILTER_SEARCH_ISSUES_PER_PAGE] ) ) {
  149. if( $p_custom_filter[FILTER_SEARCH_ISSUES_PER_PAGE] != config_get( 'default_limit_view' ) ) {
  150. $t_query[] = filter_encode_field_and_value( FILTER_PROPERTY_ISSUES_PER_PAGE, $p_custom_filter[FILTER_SEARCH_ISSUES_PER_PAGE] );
  151. }
  152. }
  153. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_HIGHLIGHT_CHANGED] ) ) {
  154. if( $p_custom_filter[FILTER_PROPERTY_HIGHLIGHT_CHANGED] != config_get( 'default_show_changed' ) ) {
  155. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_HIGHLIGHT_CHANGED, $p_custom_filter[FILTER_PROPERTY_HIGHLIGHT_CHANGED] );
  156. }
  157. }
  158. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_HIDE_STATUS_ID] ) ) {
  159. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_HIDE_STATUS_ID, $p_custom_filter[FILTER_PROPERTY_HIDE_STATUS_ID] );
  160. }
  161. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_NOT_ASSIGNED] ) ) {
  162. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_NOT_ASSIGNED, $p_custom_filter[FILTER_PROPERTY_NOT_ASSIGNED] );
  163. }
  164. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_FILTER_BY_DATE] ) ) {
  165. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_FILTER_BY_DATE, $p_custom_filter[FILTER_PROPERTY_FILTER_BY_DATE] );
  166. # The start and end dates are only applicable if filter by date is set.
  167. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_START_DAY] ) ) {
  168. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_START_DAY, $p_custom_filter[FILTER_PROPERTY_START_DAY] );
  169. }
  170. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_END_DAY] ) ) {
  171. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_END_DAY, $p_custom_filter[FILTER_PROPERTY_END_DAY] );
  172. }
  173. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_START_MONTH] ) ) {
  174. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_START_MONTH, $p_custom_filter[FILTER_PROPERTY_START_MONTH] );
  175. }
  176. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_END_MONTH] ) ) {
  177. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_END_MONTH, $p_custom_filter[FILTER_PROPERTY_END_MONTH] );
  178. }
  179. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_START_YEAR] ) ) {
  180. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_START_YEAR, $p_custom_filter[FILTER_PROPERTY_START_YEAR] );
  181. }
  182. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_END_YEAR] ) ) {
  183. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_END_YEAR, $p_custom_filter[FILTER_PROPERTY_END_YEAR] );
  184. }
  185. }
  186. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_RELATIONSHIP_TYPE] ) ) {
  187. if( $p_custom_filter[FILTER_PROPERTY_RELATIONSHIP_TYPE] != -1 ) {
  188. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_RELATIONSHIP_TYPE, $p_custom_filter[FILTER_PROPERTY_RELATIONSHIP_TYPE] );
  189. }
  190. }
  191. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_RELATIONSHIP_BUG] ) ) {
  192. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_RELATIONSHIP_BUG, $p_custom_filter[FILTER_PROPERTY_RELATIONSHIP_BUG] );
  193. }
  194. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_PLATFORM] ) ) {
  195. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_PLATFORM, $p_custom_filter[FILTER_PROPERTY_PLATFORM] );
  196. }
  197. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_OS] ) ) {
  198. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_OS, $p_custom_filter[FILTER_PROPERTY_OS] );
  199. }
  200. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_OS_BUILD] ) ) {
  201. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_OS_BUILD, $p_custom_filter[FILTER_PROPERTY_OS_BUILD] );
  202. }
  203. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_TAG_STRING] ) ) {
  204. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_TAG_STRING, $p_custom_filter[FILTER_PROPERTY_TAG_STRING] );
  205. }
  206. if( !filter_field_is_any( $p_custom_filter[FILTER_PROPERTY_TAG_SELECT] ) ) {
  207. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_TAG_SELECT, $p_custom_filter[FILTER_PROPERTY_TAG_SELECT] );
  208. }
  209. $t_query[] = filter_encode_field_and_value( FILTER_SEARCH_MATCH_TYPE, $p_custom_filter[FILTER_PROPERTY_MATCH_TYPE] );
  210. if( isset( $p_custom_filter['custom_fields'] ) ) {
  211. foreach( $p_custom_filter['custom_fields'] as $t_custom_field_id => $t_custom_field_values ) {
  212. if( !filter_field_is_any( $t_custom_field_values ) ) {
  213. $t_query[] = filter_encode_field_and_value( 'custom_field_' . $t_custom_field_id, $t_custom_field_values );
  214. }
  215. }
  216. }
  217. # Allow plugins to add filter fields
  218. $t_plugin_filter_array = filter_get_plugin_filters();
  219. foreach( $t_plugin_filter_array as $t_field_name => $t_filter_object ) {
  220. if( !filter_field_is_any( $p_custom_filter[ $t_field_name ] ) ) {
  221. $t_query[] = filter_encode_field_and_value( $t_field_name, $p_custom_filter[ $t_field_name ], $t_filter_object->type );
  222. }
  223. }
  224. if( count( $t_query ) > 0 ) {
  225. $t_query_str = implode( $t_query, '&' );
  226. $t_url = config_get( 'path' ) . 'search.php?' . $t_query_str;
  227. } else {
  228. $t_url = '';
  229. }
  230. return $t_url;
  231. }
  232. /**
  233. * Encodes a field and it's value for the filter URL. This handles the URL encoding
  234. * and arrays.
  235. * @param string $p_field_name The field name.
  236. * @param string $p_field_value The field value (can be an array)
  237. * @return string url encoded string
  238. */
  239. function filter_encode_field_and_value( $p_field_name, $p_field_value, $p_field_type=null ) {
  240. $t_query_array = array();
  241. if( is_array( $p_field_value ) ) {
  242. $t_count = count( $p_field_value );
  243. if( $t_count > 1 || $p_field_type == FILTER_TYPE_MULTI_STRING || $p_field_type == FILTER_TYPE_MULTI_INT ) {
  244. foreach( $p_field_value as $t_value ) {
  245. $t_query_array[] = urlencode( $p_field_name . '[]' ) . '=' . urlencode( $t_value );
  246. }
  247. }
  248. else if( $t_count == 1 ) {
  249. $t_query_array[] = urlencode( $p_field_name ) . '=' . urlencode( $p_field_value[0] );
  250. }
  251. } else {
  252. $t_query_array[] = urlencode( $p_field_name ) . '=' . urlencode( $p_field_value );
  253. }
  254. return implode( $t_query_array, '&' );
  255. }
  256. # ==========================================================================
  257. # GENERAL FUNCTIONS =
  258. # ==========================================================================
  259. /**
  260. * Checks the supplied value to see if it is an ANY value.
  261. * @param string $p_field_value - The value to check.
  262. * @return bool true for "ANY" values and false for others. "ANY" means filter criteria not active.
  263. */
  264. function filter_field_is_any( $p_field_value ) {
  265. if( is_array( $p_field_value ) ) {
  266. if( count( $p_field_value ) == 0 ) {
  267. return true;
  268. }
  269. foreach( $p_field_value as $t_value ) {
  270. if(( META_FILTER_ANY == $t_value ) && ( is_numeric( $t_value ) ) ) {
  271. return true;
  272. }
  273. }
  274. } else {
  275. if( is_string( $p_field_value ) && is_blank( $p_field_value ) ) {
  276. return true;
  277. }
  278. if( is_bool( $p_field_value ) && !$p_field_value ) {
  279. return true;
  280. }
  281. if(( META_FILTER_ANY == $p_field_value ) && ( is_numeric( $p_field_value ) ) ) {
  282. return true;
  283. }
  284. }
  285. return false;
  286. }
  287. /**
  288. * Checks the supplied value to see if it is a NONE value.
  289. * @param string $p_field_value - The value to check.
  290. * @return bool true for "NONE" values and false for others.
  291. * @todo is a check for these necessary? if ( ( $t_filter_value === 'none' ) || ( $t_filter_value === '[none]' ) )
  292. */
  293. function filter_field_is_none( $p_field_value ) {
  294. if( is_array( $p_field_value ) ) {
  295. foreach( $p_field_value as $t_value ) {
  296. if(( META_FILTER_NONE == $t_value ) && ( is_numeric( $t_value ) ) ) {
  297. return true;
  298. }
  299. }
  300. } else {
  301. if( is_string( $p_field_value ) && is_blank( $p_field_value ) ) {
  302. return false;
  303. }
  304. if(( META_FILTER_NONE == $p_field_value ) && ( is_numeric( $p_field_value ) ) ) {
  305. return true;
  306. }
  307. }
  308. return false;
  309. }
  310. /**
  311. * Checks the supplied value to see if it is a MYSELF value.
  312. * @param string $p_field_value - The value to check.
  313. * @return bool true for "MYSELF" values and false for others.
  314. */
  315. function filter_field_is_myself( $p_field_value ) {
  316. return( META_FILTER_MYSELF == $p_field_value ? TRUE : FALSE );
  317. }
  318. /**
  319. * @param $p_count
  320. * @param $p_per_page
  321. * @return int
  322. */
  323. function filter_per_page( $p_filter, $p_count, $p_per_page ) {
  324. $p_per_page = (( NULL == $p_per_page ) ? (int) $p_filter[FILTER_PROPERTY_ISSUES_PER_PAGE] : $p_per_page );
  325. $p_per_page = (( 0 == $p_per_page || -1 == $p_per_page ) ? $p_count : $p_per_page );
  326. return (int) abs( $p_per_page );
  327. }
  328. /**
  329. * Use $p_count and $p_per_page to determine how many pages to split this list up into.
  330. * For the sake of consistency have at least one page, even if it is empty.
  331. * @param $p_count
  332. * @param $p_per_page
  333. * @return $t_page_count
  334. */
  335. function filter_page_count( $p_count, $p_per_page ) {
  336. $t_page_count = ceil( $p_count / $p_per_page );
  337. if( $t_page_count < 1 ) {
  338. $t_page_count = 1;
  339. }
  340. return $t_page_count;
  341. }
  342. /**
  343. * Checks to make sure $p_page_number isn't past the last page.
  344. * and that $p_page_number isn't before the first page
  345. * @param $p_page_number
  346. * @param $p_page_count
  347. */
  348. function filter_valid_page_number( $p_page_number, $p_page_count ) {
  349. if( $p_page_number > $p_page_count ) {
  350. $p_page_number = $p_page_count;
  351. }
  352. if( $p_page_number < 1 ) {
  353. $p_page_number = 1;
  354. }
  355. return $p_page_number;
  356. }
  357. /**
  358. * Figure out the offset into the db query, offset is which record to start querying from
  359. * @param int $p_page_number
  360. * @param int $p_per_page
  361. * @return int
  362. */
  363. function filter_offset( $p_page_number, $p_per_page ) {
  364. return(( (int) $p_page_number -1 ) * (int) $p_per_page );
  365. }
  366. /**
  367. * Make sure that our filters are entirely correct and complete (it is possible that they are not).
  368. * We need to do this to cover cases where we don't have complete control over the filters given.s
  369. * @param array $p_filter_arr
  370. * @return mixed
  371. * @todo function needs to be abstracted
  372. */
  373. function filter_ensure_valid_filter( $p_filter_arr ) {
  374. # extend current filter to add information passed via POST
  375. if( !isset( $p_filter_arr['_version'] ) ) {
  376. $p_filter_arr['_version'] = config_get( 'cookie_version' );
  377. }
  378. $t_cookie_vers = (int) utf8_substr( $p_filter_arr['_version'], 1 );
  379. if( utf8_substr( config_get( 'cookie_version' ), 1 ) > $t_cookie_vers ) {
  380. # if the version is old, update it
  381. $p_filter_arr['_version'] = config_get( 'cookie_version' );
  382. }
  383. if( !isset( $p_filter_arr['_view_type'] ) ) {
  384. $p_filter_arr['_view_type'] = gpc_get_string( 'view_type', 'simple' );
  385. }
  386. if( !isset( $p_filter_arr[FILTER_PROPERTY_ISSUES_PER_PAGE] ) ) {
  387. $p_filter_arr[FILTER_PROPERTY_ISSUES_PER_PAGE] = gpc_get_int( FILTER_PROPERTY_ISSUES_PER_PAGE, config_get( 'default_limit_view' ) );
  388. }
  389. if( !isset( $p_filter_arr[FILTER_PROPERTY_HIGHLIGHT_CHANGED] ) ) {
  390. $p_filter_arr[FILTER_PROPERTY_HIGHLIGHT_CHANGED] = config_get( 'default_show_changed' );
  391. }
  392. if( !isset( $p_filter_arr[FILTER_PROPERTY_SHOW_STICKY_ISSUES] ) ) {
  393. $p_filter_arr[FILTER_PROPERTY_SHOW_STICKY_ISSUES] = gpc_string_to_bool( config_get( 'show_sticky_issues' ) );
  394. }
  395. if( !isset( $p_filter_arr[FILTER_PROPERTY_SORT_FIELD_NAME] ) ) {
  396. $p_filter_arr[FILTER_PROPERTY_SORT_FIELD_NAME] = "last_updated";
  397. }
  398. if( !isset( $p_filter_arr[FILTER_PROPERTY_SORT_DIRECTION] ) ) {
  399. $p_filter_arr[FILTER_PROPERTY_SORT_DIRECTION] = "DESC";
  400. }
  401. if( !isset( $p_filter_arr[FILTER_PROPERTY_PLATFORM] ) ) {
  402. $p_filter_arr[FILTER_PROPERTY_PLATFORM] = array(
  403. 0 => META_FILTER_ANY,
  404. );
  405. }
  406. if( !isset( $p_filter_arr[FILTER_PROPERTY_OS] ) ) {
  407. $p_filter_arr[FILTER_PROPERTY_OS] = array(
  408. 0 => META_FILTER_ANY,
  409. );
  410. }
  411. if( !isset( $p_filter_arr[FILTER_PROPERTY_OS_BUILD] ) ) {
  412. $p_filter_arr[FILTER_PROPERTY_OS_BUILD] = array(
  413. 0 => META_FILTER_ANY,
  414. );
  415. }
  416. if( !isset( $p_filter_arr[FILTER_PROPERTY_PROJECT_ID] ) ) {
  417. $p_filter_arr[FILTER_PROPERTY_PROJECT_ID] = array(
  418. 0 => META_FILTER_CURRENT,
  419. );
  420. }
  421. if( !isset( $p_filter_arr[FILTER_PROPERTY_START_MONTH] ) ) {
  422. $p_filter_arr[FILTER_PROPERTY_START_MONTH] = gpc_get_string( FILTER_PROPERTY_START_MONTH, date( 'm' ) );
  423. }
  424. if( !isset( $p_filter_arr[FILTER_PROPERTY_START_DAY] ) ) {
  425. $p_filter_arr[FILTER_PROPERTY_START_DAY] = gpc_get_string( FILTER_PROPERTY_START_DAY, 1 );
  426. }
  427. if( !isset( $p_filter_arr[FILTER_PROPERTY_START_YEAR] ) ) {
  428. $p_filter_arr[FILTER_PROPERTY_START_YEAR] = gpc_get_string( FILTER_PROPERTY_START_YEAR, date( 'Y' ) );
  429. }
  430. if( !isset( $p_filter_arr[FILTER_PROPERTY_END_MONTH] ) ) {
  431. $p_filter_arr[FILTER_PROPERTY_END_MONTH] = gpc_get_string( FILTER_PROPERTY_END_MONTH, date( 'm' ) );
  432. }
  433. if( !isset( $p_filter_arr[FILTER_PROPERTY_END_DAY] ) ) {
  434. $p_filter_arr[FILTER_PROPERTY_END_DAY] = gpc_get_string( FILTER_PROPERTY_END_DAY, date( 'd' ) );
  435. }
  436. if( !isset( $p_filter_arr[FILTER_PROPERTY_END_YEAR] ) ) {
  437. $p_filter_arr[FILTER_PROPERTY_END_YEAR] = gpc_get_string( FILTER_PROPERTY_END_YEAR, date( 'Y' ) );
  438. }
  439. if( !isset( $p_filter_arr[FILTER_PROPERTY_FREE_TEXT] ) ) {
  440. $p_filter_arr[FILTER_PROPERTY_FREE_TEXT] = '';
  441. }
  442. if( !isset( $p_filter_arr[FILTER_PROPERTY_NOT_ASSIGNED] ) ) {
  443. $p_filter_arr[FILTER_PROPERTY_NOT_ASSIGNED] = gpc_get_bool( FILTER_PROPERTY_NOT_ASSIGNED, false );
  444. }
  445. if( !isset( $p_filter_arr[FILTER_PROPERTY_FILTER_BY_DATE] ) ) {
  446. $p_filter_arr[FILTER_PROPERTY_FILTER_BY_DATE] = gpc_get_bool( FILTER_PROPERTY_FILTER_BY_DATE, false );
  447. }
  448. if( !isset( $p_filter_arr[FILTER_PROPERTY_VIEW_STATE_ID] ) ) {
  449. $p_filter_arr[FILTER_PROPERTY_VIEW_STATE_ID] = gpc_get( FILTER_PROPERTY_VIEW_STATE_ID, '' );
  450. }
  451. else if( filter_field_is_any( $p_filter_arr[FILTER_PROPERTY_VIEW_STATE_ID] ) ) {
  452. $p_filter_arr[FILTER_PROPERTY_VIEW_STATE_ID] = META_FILTER_ANY;
  453. }
  454. if( !isset( $p_filter_arr[FILTER_PROPERTY_RELATIONSHIP_TYPE] ) ) {
  455. $p_filter_arr[FILTER_PROPERTY_RELATIONSHIP_TYPE] = gpc_get_int( FILTER_PROPERTY_RELATIONSHIP_TYPE, -1 );
  456. }
  457. if( !isset( $p_filter_arr[FILTER_PROPERTY_RELATIONSHIP_BUG] ) ) {
  458. $p_filter_arr[FILTER_PROPERTY_RELATIONSHIP_BUG] = gpc_get_int( FILTER_PROPERTY_RELATIONSHIP_BUG, 0 );
  459. }
  460. if( !isset( $p_filter_arr[FILTER_PROPERTY_TARGET_VERSION] ) ) {
  461. $p_filter_arr[FILTER_PROPERTY_TARGET_VERSION] = META_FILTER_ANY;
  462. }
  463. if( !isset( $p_filter_arr[FILTER_PROPERTY_TAG_STRING] ) ) {
  464. $p_filter_arr[FILTER_PROPERTY_TAG_STRING] = gpc_get_string( FILTER_PROPERTY_TAG_STRING, '' );
  465. }
  466. if( !isset( $p_filter_arr[FILTER_PROPERTY_TAG_SELECT] ) ) {
  467. $p_filter_arr[FILTER_PROPERTY_TAG_SELECT] = gpc_get_string( FILTER_PROPERTY_TAG_SELECT, '' );
  468. }
  469. if( !isset( $p_filter_arr[FILTER_PROPERTY_MATCH_TYPE] ) ) {
  470. $p_filter_arr[FILTER_PROPERTY_MATCH_TYPE] = gpc_get_int( FILTER_PROPERTY_MATCH_TYPE, FILTER_MATCH_ALL );
  471. }
  472. # initialize plugin filters
  473. $t_plugin_filters = filter_get_plugin_filters();
  474. foreach( $t_plugin_filters as $t_field_name => $t_filter_object ) {
  475. if( !isset( $p_filter_arr[ $t_field_name ] ) ) {
  476. switch( $t_filter_object->type ) {
  477. case FILTER_TYPE_STRING:
  478. $p_filter_arr[ $t_field_name ] = gpc_get_string( $t_field_name, $t_filter_object->default );
  479. break;
  480. case FILTER_TYPE_INT:
  481. $p_filter_arr[ $t_field_name ] = gpc_get_int( $t_field_name, (int)$t_filter_object->default );
  482. break;
  483. case FILTER_TYPE_BOOLEAN:
  484. $p_filter_arr[ $t_field_name ] = gpc_get_bool( $t_field_name, (bool)$t_filter_object->default );
  485. break;
  486. case FILTER_TYPE_MULTI_STRING:
  487. $p_filter_arr[ $t_field_name ] = gpc_get_string_array( $t_field_name, array( 0 => META_FILTER_ANY ) );
  488. break;
  489. case FILTER_TYPE_MULTI_INT:
  490. $p_filter_arr[ $t_field_name ] = gpc_get_int_array( $t_field_name, array( 0 => META_FILTER_ANY ) );
  491. break;
  492. default:
  493. $p_filter_arr[ $t_field_name ] = META_FILTER_ANY;
  494. }
  495. }
  496. if ( ! $t_filter_object->validate( $p_filter_arr[ $t_field_name ] ) ) {
  497. $p_filter_arr[ $t_field_name ] = $t_filter_object->default;
  498. }
  499. }
  500. $t_custom_fields = custom_field_get_ids();
  501. # @@@ (thraxisp) This should really be the linked ids, but we don't know the project
  502. $f_custom_fields_data = array();
  503. if( is_array( $t_custom_fields ) && ( count( $t_custom_fields ) > 0 ) ) {
  504. foreach( $t_custom_fields as $t_cfid ) {
  505. if( is_array( gpc_get( 'custom_field_' . $t_cfid, null ) ) ) {
  506. $f_custom_fields_data[$t_cfid] = gpc_get_string_array( 'custom_field_' . $t_cfid, META_FILTER_ANY );
  507. } else {
  508. $f_custom_fields_data[$t_cfid] = gpc_get_string( 'custom_field_' . $t_cfid, META_FILTER_ANY );
  509. $f_custom_fields_data[$t_cfid] = array(
  510. $f_custom_fields_data[$t_cfid],
  511. );
  512. }
  513. }
  514. }
  515. # validate sorting
  516. $t_fields = helper_get_columns_to_view();
  517. $t_n_fields = count( $t_fields );
  518. for( $i = 0;$i < $t_n_fields;$i++ ) {
  519. if( isset( $t_fields[$i] ) && in_array( $t_fields[$i], array( 'selection', 'edit', 'bugnotes_count', 'attachment_count' ) ) ) {
  520. unset( $t_fields[$i] );
  521. }
  522. }
  523. $t_sort_fields = explode( ',', $p_filter_arr['sort'] );
  524. $t_dir_fields = explode( ',', $p_filter_arr['dir'] );
  525. for( $i = 0;$i < 2;$i++ ) {
  526. if( isset( $t_sort_fields[$i] ) ) {
  527. $t_drop = false;
  528. $t_sort = $t_sort_fields[$i];
  529. if( strpos( $t_sort, 'custom_' ) === 0 ) {
  530. if( false === custom_field_get_id_from_name( utf8_substr( $t_sort, utf8_strlen( 'custom_' ) ) ) ) {
  531. $t_drop = true;
  532. }
  533. } else {
  534. if( !in_array( $t_sort, $t_fields ) ) {
  535. $t_drop = true;
  536. }
  537. }
  538. if( !in_array( $t_dir_fields[$i], array( "ASC", "DESC" ) ) ) {
  539. $t_drop = true;
  540. }
  541. if( $t_drop ) {
  542. unset( $t_sort_fields[$i] );
  543. unset( $t_dir_fields[$i] );
  544. }
  545. }
  546. }
  547. if( count( $t_sort_fields ) > 0 ) {
  548. $p_filter_arr['sort'] = implode( ',', $t_sort_fields );
  549. $p_filter_arr['dir'] = implode( ',', $t_dir_fields );
  550. } else {
  551. $p_filter_arr['sort'] = "last_updated";
  552. $p_filter_arr['dir'] = "DESC";
  553. }
  554. # validate or filter junk from other fields
  555. $t_multi_select_list = array(
  556. FILTER_PROPERTY_CATEGORY => 'string',
  557. FILTER_PROPERTY_SEVERITY_ID => 'int',
  558. FILTER_PROPERTY_STATUS_ID => 'int',
  559. FILTER_PROPERTY_REPORTER_ID => 'int',
  560. FILTER_PROPERTY_HANDLER_ID => 'int',
  561. FILTER_PROPERTY_NOTE_USER_ID => 'int',
  562. FILTER_PROPERTY_RESOLUTION_ID => 'int',
  563. FILTER_PROPERTY_PRIORITY_ID => 'int',
  564. FILTER_PROPERTY_PRODUCT_BUILD => 'string',
  565. FILTER_PROPERTY_PRODUCT_VERSION => 'string',
  566. FILTER_PROPERTY_HIDE_STATUS_ID => 'int',
  567. FILTER_PROPERTY_FIXED_IN_VERSION => 'string',
  568. FILTER_PROPERTY_TARGET_VERSION => 'string',
  569. FILTER_PROPERTY_MONITOR_USER_ID => 'int',
  570. 'show_profile' => 'int',
  571. );
  572. foreach( $t_multi_select_list as $t_multi_field_name => $t_multi_field_type ) {
  573. if( !isset( $p_filter_arr[$t_multi_field_name] ) ) {
  574. if( FILTER_PROPERTY_HIDE_STATUS_ID == $t_multi_field_name ) {
  575. $p_filter_arr[$t_multi_field_name] = array(
  576. config_get( 'hide_status_default' ),
  577. );
  578. }
  579. else if( 'custom_fields' == $t_multi_field_name ) {
  580. $p_filter_arr[$t_multi_field_name] = array(
  581. $f_custom_fields_data,
  582. );
  583. } else {
  584. $p_filter_arr[$t_multi_field_name] = array(
  585. META_FILTER_ANY,
  586. );
  587. }
  588. } else {
  589. if( !is_array( $p_filter_arr[$t_multi_field_name] ) ) {
  590. $p_filter_arr[$t_multi_field_name] = array(
  591. $p_filter_arr[$t_multi_field_name],
  592. );
  593. }
  594. $t_checked_array = array();
  595. foreach( $p_filter_arr[$t_multi_field_name] as $t_filter_value ) {
  596. $t_filter_value = stripslashes( $t_filter_value );
  597. if(( $t_filter_value === 'any' ) || ( $t_filter_value === '[any]' ) ) {
  598. $t_filter_value = META_FILTER_ANY;
  599. }
  600. if(( $t_filter_value === 'none' ) || ( $t_filter_value === '[none]' ) ) {
  601. $t_filter_value = META_FILTER_NONE;
  602. }
  603. if( 'string' == $t_multi_field_type ) {
  604. $t_checked_array[] = $t_filter_value;
  605. }
  606. else if( 'int' == $t_multi_field_type ) {
  607. $t_checked_array[] = (int)$t_filter_value;
  608. }
  609. else if( 'array' == $t_multi_field_type ) {
  610. $t_checked_array[] = $t_filter_value;
  611. }
  612. }
  613. $p_filter_arr[$t_multi_field_name] = $t_checked_array;
  614. }
  615. }
  616. if( is_array( $t_custom_fields ) && ( count( $t_custom_fields ) > 0 ) ) {
  617. foreach( $t_custom_fields as $t_cfid ) {
  618. if( !isset( $p_filter_arr['custom_fields'][$t_cfid] ) ) {
  619. $p_filter_arr['custom_fields'][$t_cfid] = array(
  620. META_FILTER_ANY,
  621. );
  622. } else {
  623. if( !is_array( $p_filter_arr['custom_fields'][$t_cfid] ) ) {
  624. $p_filter_arr['custom_fields'][$t_cfid] = array(
  625. $p_filter_arr['custom_fields'][$t_cfid],
  626. );
  627. }
  628. $t_checked_array = array();
  629. foreach( $p_filter_arr['custom_fields'][$t_cfid] as $t_filter_value ) {
  630. $t_filter_value = stripslashes( $t_filter_value );
  631. if(( $t_filter_value === 'any' ) || ( $t_filter_value === '[any]' ) ) {
  632. $t_filter_value = META_FILTER_ANY;
  633. }
  634. $t_checked_array[] = $t_filter_value;
  635. }
  636. $p_filter_arr['custom_fields'][$t_cfid] = $t_checked_array;
  637. }
  638. }
  639. }
  640. # all of our filter values are now guaranteed to be there, and correct.
  641. return $p_filter_arr;
  642. }
  643. /**
  644. * Get the standard filter that is to be used when no filter was previously saved.
  645. * When creating specific filters, this can be used as a basis for the filter, where
  646. * specific entries can be overridden.
  647. * @return mixed
  648. */
  649. function filter_get_default() {
  650. $t_hide_status_default = config_get( 'hide_status_default' );
  651. $t_default_show_changed = config_get( 'default_show_changed' );
  652. $t_filter = array(
  653. FILTER_PROPERTY_CATEGORY => Array(
  654. '0' => META_FILTER_ANY,
  655. ),
  656. FILTER_PROPERTY_SEVERITY_ID => Array(
  657. '0' => META_FILTER_ANY,
  658. ),
  659. FILTER_PROPERTY_STATUS_ID => Array(
  660. '0' => META_FILTER_ANY,
  661. ),
  662. FILTER_PROPERTY_HIGHLIGHT_CHANGED => $t_default_show_changed,
  663. FILTER_PROPERTY_REPORTER_ID => Array(
  664. '0' => META_FILTER_ANY,
  665. ),
  666. FILTER_PROPERTY_HANDLER_ID => Array(
  667. '0' => META_FILTER_ANY,
  668. ),
  669. FILTER_PROPERTY_PROJECT_ID => Array(
  670. '0' => META_FILTER_CURRENT,
  671. ),
  672. FILTER_PROPERTY_RESOLUTION_ID => Array(
  673. '0' => META_FILTER_ANY,
  674. ),
  675. FILTER_PROPERTY_PRODUCT_BUILD => Array(
  676. '0' => META_FILTER_ANY,
  677. ),
  678. FILTER_PROPERTY_PRODUCT_VERSION => Array(
  679. '0' => META_FILTER_ANY,
  680. ),
  681. FILTER_PROPERTY_HIDE_STATUS_ID => Array(
  682. '0' => $t_hide_status_default,
  683. ),
  684. FILTER_PROPERTY_MONITOR_USER_ID => Array(
  685. '0' => META_FILTER_ANY,
  686. ),
  687. FILTER_PROPERTY_SORT_FIELD_NAME => 'last_updated',
  688. FILTER_PROPERTY_SORT_DIRECTION => 'DESC',
  689. FILTER_PROPERTY_ISSUES_PER_PAGE => config_get( 'default_limit_view' ),
  690. FILTER_PROPERTY_MATCH_TYPE => FILTER_MATCH_ALL
  691. );
  692. return filter_ensure_valid_filter( $t_filter );
  693. }
  694. /**
  695. * Deserialize filter string
  696. * @param string $p_serialized_filter
  697. * @return mixed $t_filter array
  698. * @see filter_ensure_valid_filter
  699. */
  700. function filter_deserialize( $p_serialized_filter ) {
  701. if( is_blank( $p_serialized_filter ) ) {
  702. return false;
  703. }
  704. # check to see if new cookie is needed
  705. $t_setting_arr = explode( '#', $p_serialized_filter, 2 );
  706. if(( $t_setting_arr[0] == 'v1' ) || ( $t_setting_arr[0] == 'v2' ) || ( $t_setting_arr[0] == 'v3' ) || ( $t_setting_arr[0] == 'v4' ) ) {
  707. # these versions can't be salvaged, they are too old to update
  708. return false;
  709. }
  710. # We shouldn't need to do this anymore, as filters from v5 onwards should cope with changing
  711. # filter indices dynamically
  712. $t_filter_array = array();
  713. if( isset( $t_setting_arr[1] ) ) {
  714. $t_filter_array = unserialize( $t_setting_arr[1] );
  715. } else {
  716. return false;
  717. }
  718. if( $t_filter_array['_version'] != config_get( 'cookie_version' ) ) {
  719. # if the version is not new enough, update it using defaults
  720. return filter_ensure_valid_filter( $t_filter_array );
  721. }
  722. return $t_filter_array;
  723. }
  724. /**
  725. * Check if the filter cookie exists and is of the correct version.
  726. * @return bool
  727. */
  728. function filter_is_cookie_valid() {
  729. $t_view_all_cookie_id = gpc_get_cookie( config_get( 'view_all_cookie' ), '' );
  730. $t_view_all_cookie = filter_db_get_filter( $t_view_all_cookie_id );
  731. # check to see if the cookie does not exist
  732. if( is_blank( $t_view_all_cookie ) ) {
  733. return false;
  734. }
  735. # check to see if new cookie is needed
  736. $t_setting_arr = explode( '#', $t_view_all_cookie, 2 );
  737. if(( $t_setting_arr[0] == 'v1' ) || ( $t_setting_arr[0] == 'v2' ) || ( $t_setting_arr[0] == 'v3' ) || ( $t_setting_arr[0] == 'v4' ) ) {
  738. return false;
  739. }
  740. # We shouldn't need to do this anymore, as filters from v5 onwards should cope with changing
  741. # filter indices dynamically
  742. $t_filter_cookie_arr = array();
  743. if( isset( $t_setting_arr[1] ) ) {
  744. $t_filter_cookie_arr = unserialize( $t_setting_arr[1] );
  745. } else {
  746. return false;
  747. }
  748. if( $t_filter_cookie_arr['_version'] != config_get( 'cookie_version' ) ) {
  749. return false;
  750. }
  751. return true;
  752. }
  753. /**
  754. * Get the array fields specified by $p_filter_id
  755. * using the cached row if it's available
  756. * @param int $p_filter_id
  757. * @return mixed a filter row
  758. */
  759. function filter_get_row( $p_filter_id ) {
  760. return filter_cache_row( $p_filter_id );
  761. }
  762. /**
  763. * Get the value of the filter field specified by filter id and field name
  764. * @param int $p_filter_id
  765. * @param string $p_field_name
  766. * @return string
  767. */
  768. function filter_get_field( $p_filter_id, $p_field_name ) {
  769. $row = filter_get_row( $p_filter_id );
  770. if( isset( $row[$p_field_name] ) ) {
  771. return $row[$p_field_name];
  772. } else {
  773. error_parameters( $p_field_name );
  774. trigger_error( ERROR_DB_FIELD_NOT_FOUND, WARNING );
  775. return '';
  776. }
  777. }
  778. /**
  779. * Add sort parameters to the query clauses
  780. * @param array $p_filter
  781. * @param bool $p_show_sticky
  782. * @param array $p_query_clauses
  783. * @return array $p_query_clauses
  784. */
  785. function filter_get_query_sort_data( &$p_filter, $p_show_sticky, $p_query_clauses ) {
  786. $t_bug_table = db_get_table( 'mantis_bug_table' );
  787. $t_custom_field_string_table = db_get_table( 'mantis_custom_field_string_table' );
  788. # if sort is blank then default the sort and direction. This is to fix the
  789. # symptoms of #3953. Note that even if the main problem is fixed, we may
  790. # have to keep this code for a while to handle filters saved with this blank field.
  791. if( is_blank( $p_filter[FILTER_PROPERTY_SORT_FIELD_NAME] ) ) {
  792. $p_filter[FILTER_PROPERTY_SORT_FIELD_NAME] = 'last_updated';
  793. $p_filter[FILTER_PROPERTY_SORT_DIRECTION] = 'DESC';
  794. }
  795. $p_query_clauses['order'] = array();
  796. $t_sort_fields = explode( ',', $p_filter[FILTER_PROPERTY_SORT_FIELD_NAME] );
  797. $t_dir_fields = explode( ',', $p_filter[FILTER_PROPERTY_SORT_DIRECTION] );
  798. $t_plugin_columns = columns_get_plugin_columns();
  799. if ( gpc_string_to_bool( $p_filter[FILTER_PROPERTY_SHOW_STICKY_ISSUES] ) && ( NULL !== $p_show_sticky ) ) {
  800. $p_query_clauses['order'][] = "$t_bug_table.sticky DESC";
  801. }
  802. $t_count = count( $t_sort_fields );
  803. for( $i = 0;$i < $t_count;$i++ ) {
  804. $c_sort = $t_sort_fields[$i];
  805. $c_dir = 'DESC' == $t_dir_fields[$i] ? 'DESC' : 'ASC';
  806. if( !in_array( $t_sort_fields[$i], array_slice( $t_sort_fields, $i + 1 ) ) ) {
  807. # if sorting by a custom field
  808. if( strpos( $c_sort, 'custom_' ) === 0 ) {
  809. $t_custom_field = utf8_substr( $c_sort, utf8_strlen( 'custom_' ) );
  810. $t_custom_field_id = custom_field_get_id_from_name( $t_custom_field );
  811. $c_cf_alias = 'custom_field_' . $t_custom_field_id;
  812. $t_cf_table_alias = $t_custom_field_string_table . '_' . $t_custom_field_id;
  813. $t_cf_select = "$t_cf_table_alias.value $c_cf_alias";
  814. # check to be sure this field wasn't already added to the query.
  815. if( !in_array( $t_cf_select, $p_query_clauses['select'] ) ) {
  816. $p_query_clauses['select'][] = $t_cf_select;
  817. $p_query_clauses['join'][] = "LEFT JOIN $t_custom_field_string_table $t_cf_table_alias ON $t_bug_table.id = $t_cf_table_alias.bug_id AND $t_cf_table_alias.field_id = $t_custom_field_id";
  818. }
  819. $p_query_clauses['order'][] = "$c_cf_alias $c_dir";
  820. # if sorting by plugin columns
  821. } else if ( isset( $t_plugin_columns[ $t_sort_fields[$i] ] ) ) {
  822. $t_column_object = $t_plugin_columns[ $t_sort_fields[$i] ];
  823. if ( $t_column_object->sortable ) {
  824. $t_clauses = $t_column_object->sortquery( $c_dir );
  825. if ( is_array( $t_clauses ) ) {
  826. if ( isset( $t_clauses['join'] ) ) {
  827. $p_query_clauses['join'][] = $t_clauses['join'];
  828. }
  829. if ( isset( $t_clauses['order'] ) ) {
  830. $p_query_clauses['order'][] = $t_clauses['order'];
  831. }
  832. }
  833. }
  834. # standard column
  835. } else {
  836. if ( 'last_updated' == $c_sort ) {
  837. $c_sort = "last_updated";
  838. }
  839. $p_query_clauses['order'][] = "$t_bug_table.$c_sort $c_dir";
  840. }
  841. }
  842. }
  843. # add basic sorting if necessary
  844. if( !in_array( 'last_updated', $t_sort_fields ) ) {
  845. $p_query_clauses['order'][] = "$t_bug_table.last_updated DESC";
  846. }
  847. if( !in_array( 'date_submitted', $t_sort_fields ) ) {
  848. $p_query_clauses['order'][] = "$t_bug_table.date_submitted DESC";
  849. }
  850. return $p_query_clauses;
  851. }
  852. /**
  853. * Remove any duplicate values in certain elements of query_clauses
  854. * Do not loop over query clauses as some keys may contain valid duplicate values.
  855. * We basically want unique values for just the base query elements select, from, and join
  856. * 'where' and 'where_values' key should not have duplicates as that is handled earlier and applying
  857. * array_unique here could cause problems with the query.
  858. * @param $p_query_clauses
  859. * @return $p_query_clauses
  860. */
  861. function filter_unique_query_clauses( $p_query_clauses ) {
  862. $p_query_clauses['select'] = array_unique( $p_query_clauses['select'] );
  863. $p_query_clauses['from'] = array_unique( $p_query_clauses['from'] );
  864. $p_query_clauses['join'] = array_unique( $p_query_clauses['join'] );
  865. return $p_query_clauses;
  866. }
  867. /**
  868. * Build a query with the query clauses array, query for bug count and return the result
  869. * @param array $p_query_clauses
  870. * @return int
  871. */
  872. function filter_get_bug_count( $p_query_clauses ) {
  873. $t_bug_table = db_get_table( 'mantis_bug_table' );
  874. $p_query_clauses = filter_unique_query_clauses( $p_query_clauses );
  875. $t_select_string = "SELECT Count( DISTINCT $t_bug_table.id ) as idcnt ";
  876. $t_from_string = " FROM " . implode( ', ', $p_query_clauses['from'] );
  877. $t_join_string = (( count( $p_query_clauses['join'] ) > 0 ) ? implode( ' ', $p_query_clauses['join'] ) : '' );
  878. $t_where_string = count( $p_query_clauses['project_where']) > 0 ? 'WHERE '. implode( ' AND ', $p_query_clauses['project_where'] ) : '';
  879. if ( count( $p_query_clauses['where'] ) > 0 ) {
  880. $t_where_string .= ' AND ( ';
  881. $t_where_string .= implode( $p_query_clauses['operator'], $p_query_clauses['where'] );
  882. $t_where_string .= ' ) ';
  883. }
  884. $t_result = db_query_bound( "$t_select_string $t_from_string $t_join_string $t_where_string", $p_query_clauses['where_values'] );
  885. return db_result( $t_result );
  886. }
  887. /**
  888. * @todo Had to make all these parameters required because we can't use
  889. * call-time pass by reference anymore. I really preferred not having
  890. * to pass all the params in if you didn't want to, but I wanted to get
  891. * rid of the errors for now. If we can think of a better way later
  892. * (maybe return an object) that would be great.
  893. *
  894. * @param int $p_page_number the page you want to see (set to the actual page on return)
  895. * @param int $p_per_page the number of bugs to see per page (set to actual on return)
  896. * -1 indicates you want to see all bugs
  897. * null indicates you want to use the value specified in the filter
  898. * @param int $p_page_count you don't need to give a value here, the number of pages will be stored here on return
  899. * @param int $p_bug_count you don't need to give a value here, the number of bugs will be stored here on return
  900. * @param mixed $p_custom_filter Filter to use.
  901. * @param int $p_project_id project id to use in filtering.
  902. * @param int $p_user_id user id to use as current user when filtering.
  903. * @param bool $p_show_sticky get sticky issues only.
  904. */
  905. function filter_get_bug_rows( &$p_page_number, &$p_per_page, &$p_page_count, &$p_bug_count, $p_custom_filter = null, $p_project_id = null, $p_user_id = null, $p_show_sticky = null ) {
  906. log_event( LOG_FILTERING, 'START NEW FILTER QUERY' );
  907. $t_bug_table = db_get_table( 'mantis_bug_table' );
  908. $t_bug_text_table = db_get_table( 'mantis_bug_text_table' );
  909. $t_bugnote_table = db_get_table( 'mantis_bugnote_table' );
  910. $t_category_table = db_get_table( 'mantis_category_table' );
  911. $t_custom_field_string_table = db_get_table( 'mantis_custom_field_string_table' );
  912. $t_bugnote_text_table = db_get_table( 'mantis_bugnote_text_table' );
  913. $t_project_table = db_get_table( 'mantis_project_table' );
  914. $t_bug_monitor_table = db_get_table( 'mantis_bug_monitor_table' );
  915. $t_limit_reporters = config_get( 'limit_reporters' );
  916. $t_bug_relationship_table = db_get_table( 'mantis_bug_relationship_table' );
  917. $t_report_bug_threshold = config_get( 'report_bug_threshold' );
  918. $t_where_param_count = 0;
  919. $t_current_user_id = auth_get_current_user_id();
  920. if ( $p_user_id === null || $p_user_id === 0 ) {
  921. $t_user_id = $t_current_user_id;
  922. } else {
  923. $t_user_id = $p_user_id;
  924. }
  925. $c_user_id = db_prepare_int( $t_user_id );
  926. if( null === $p_project_id ) {
  927. # @@@ If project_id is not specified, then use the project id(s) in the filter if set, otherwise, use current project.
  928. $t_project_id = helper_get_current_project();
  929. } else {
  930. $t_project_id = $p_project_id;
  931. }
  932. if( $p_custom_filter === null ) {
  933. # Prefer current_user_get_bug_filter() over user_get_filter() when applicable since it supports
  934. # cookies set by previous version of the code.
  935. if( $t_user_id == $t_current_user_id ) {
  936. $t_filter = current_user_get_bug_filter();
  937. } else {
  938. $t_filter = user_get_bug_filter( $t_user_id, $t_project_id );
  939. }
  940. } else {
  941. $t_filter = $p_custom_filter;
  942. }
  943. $t_filter = filter_ensure_valid_filter( $t_filter );
  944. if( false === $t_filter ) {
  945. return false;
  946. # signify a need to create a cookie
  947. # @@@ error instead?
  948. }
  949. $t_view_type = $t_filter['_view_type'];
  950. // project query clauses must be AND-ed always, irrespective of how the filter
  951. // clauses are requested by the user ( all matching -> AND, any matching -> OR )
  952. $t_where_clauses = array();
  953. $t_project_where_clauses = array(
  954. "$t_project_table.enabled = " . db_param(),
  955. );
  956. $t_where_params = array(
  957. 1,
  958. );
  959. $t_select_clauses = array(
  960. "$t_bug_table.*",
  961. );
  962. $t_from_clauses = array(
  963. $t_bug_table,
  964. );
  965. $t_join_clauses = array(
  966. "JOIN $t_project_table ON $t_project_table.id = $t_bug_table.project_id",
  967. );
  968. // normalize the project filtering into an array $t_project_ids
  969. if( 'simple' == $t_view_type ) {
  970. log_event( LOG_FILTERING, 'Simple Filter' );
  971. $t_project_ids = array(
  972. $t_project_id,
  973. );
  974. $t_include_sub_projects = true;
  975. } else {
  976. log_event( LOG_FILTERING, 'Advanced Filter' );
  977. if( !is_array( $t_filter[FILTER_PROPERTY_PROJECT_ID] ) ) {
  978. $t_project_ids = array(
  979. db_prepare_int( $t_filter[FILTER_PROPERTY_PROJECT_ID] ),
  980. );
  981. } else {
  982. $t_project_ids = array_map( 'db_prepare_int', $t_filter[FILTER_PROPERTY_PROJECT_ID] );
  983. }
  984. $t_include_sub_projects = (( count( $t_project_ids ) == 1 ) && ( ( $t_project_ids[0] == META_FILTER_CURRENT ) || ( $t_project_ids[0] == ALL_PROJECTS ) ) );
  985. }
  986. log_event( LOG_FILTERING, 'project_ids = @P' . implode( ', @P', $t_project_ids ) );
  987. log_event( LOG_FILTERING, 'include sub-projects = ' . ( $t_include_sub_projects ? '1' : '0' ) );
  988. // if the array has ALL_PROJECTS, then reset the array to only contain ALL_PROJECTS.
  989. // replace META_FILTER_CURRENT with the actualy current project id.
  990. $t_all_projects_found = false;
  991. $t_new_project_ids = array();
  992. foreach( $t_project_ids as $t_pid ) {
  993. if( $t_pid == META_FILTER_CURRENT ) {
  994. $t_pid = $t_project_id;
  995. }
  996. if( $t_pid == ALL_PROJECTS ) {
  997. $t_all_projects_found = true;
  998. log_event( LOG_FILTERING, 'all projects selected' );
  999. break;
  1000. }
  1001. // filter out inaccessible projects.
  1002. if( !access_has_project_level( VIEWER, $t_pid, $t_user_id ) ) {
  1003. continue;
  1004. }
  1005. $t_new_project_ids[] = $t_pid;
  1006. }
  1007. $t_projects_query_required = true;
  1008. if( $t_all_projects_found ) {
  1009. if( user_is_administrator( $t_user_id ) ) {
  1010. log_event( LOG_FILTERING, 'all projects + administrator, hence no project filter.' );
  1011. $t_projects_query_required = false;
  1012. } else {
  1013. $t_project_ids = user_get_accessible_projects( $t_user_id );
  1014. }
  1015. } else {
  1016. $t_project_ids = $t_new_project_ids;
  1017. }
  1018. if( $t_projects_query_required ) {
  1019. // expand project ids to include sub-projects
  1020. if( $t_include_sub_projects ) {
  1021. $t_top_project_ids = $t_project_ids;
  1022. foreach( $t_top_project_ids as $t_pid ) {
  1023. log_event( LOG_FILTERING, 'Getting sub-projects for project id @P' . $t_pid );
  1024. $t_subproject_ids = user_get_all_accessible_subprojects( $t_user_id, $t_pid );
  1025. if (!$t_subproject_ids) continue;
  1026. $t_project_ids = array_merge( $t_project_ids, $t_subproject_ids );
  1027. }
  1028. $t_project_ids = array_unique( $t_project_ids );
  1029. }
  1030. // if no projects are accessible, then return an empty array.
  1031. if( count( $t_project_ids ) == 0 ) {
  1032. log_event( LOG_FILTERING, 'no accessible projects' );
  1033. return array();
  1034. }
  1035. log_event( LOG_FILTERING, 'project_ids after including sub-projects = @P' . implode( ', @P', $t_project_ids ) );
  1036. // this array is to be populated with project ids for which we only want to show public issues. This is due to the limited
  1037. // access of the current user.
  1038. $t_public_only_project_ids = array();
  1039. // this array is populated with project ids that the current user has full access to.
  1040. $t_private_and_public_project_ids = array();
  1041. foreach( $t_project_ids as $t_pid ) {
  1042. $t_access_required_to_view_private_bugs = config_get( 'private_bug_threshold', null, null, $t_pid );
  1043. if( access_has_project_level( $t_access_required_to_view_private_bugs, $t_pid, $t_user_id ) ) {
  1044. $t_private_and_public_project_ids[] = $t_pid;
  1045. } else {
  1046. $t_public_only_project_ids[] = $t_pid;
  1047. }
  1048. }
  1049. log_event( LOG_FILTERING, 'project_ids (with public/private access) = @P' . implode( ', @P', $t_private_and_public_project_ids ) );
  1050. log_event( LOG_FILTERING, 'project_ids (with public access) = @P' . implode( ', @P', $t_public_only_project_ids ) );
  1051. $t_count_private_and_public_project_ids = count( $t_private_and_public_project_ids );
  1052. if( $t_count_private_and_public_project_ids == 1 ) {
  1053. $t_private_and_public_query = "( $t_bug_table.project_id = " . $t_private_and_public_project_ids[0] . " )";
  1054. }
  1055. else if( $t_count_private_and_public_project_ids > 1 ) {
  1056. $t_private_and_public_query = "( $t_bug_table.project_id in (" . implode( ', ', $t_private_and_public_project_ids ) . ") )";
  1057. } else {
  1058. $t_private_and_public_query = null;
  1059. }
  1060. $t_count_public_only_project_ids = count( $t_public_only_project_ids );
  1061. $t_public_view_state_check = "( ( $t_bug_table.view_state = " . VS_PUBLIC . " ) OR ( $t_bug_table.reporter_id = $t_user_id ) )";
  1062. if( $t_count_public_only_project_ids == 1 ) {
  1063. $t_public_only_query = "( ( $t_bug_table.project_id = " . $t_public_only_project_ids[0] . " ) AND $t_public_view_state_check )";
  1064. }
  1065. else if( $t_count_public_only_project_ids > 1 ) {
  1066. $t_public_only_query = "( ( $t_bug_table.project_id in (" . implode( ', ', $t_public_only_project_ids ) . ") ) AND $t_public_view_state_check )";
  1067. } else {
  1068. $t_public_only_query = null;
  1069. }
  1070. // both queries can't be null, so we either have one of them or both.
  1071. if( $t_private_and_public_query === null ) {
  1072. $t_project_query = $t_public_only_query;
  1073. } else if( $t_public_only_query === null ) {
  1074. $t_project_query = $t_private_and_public_query;
  1075. } else {
  1076. $t_project_query = "( $t_public_only_query OR $t_private_and_public_query )";
  1077. }
  1078. log_event( LOG_FILTERING, 'project query = ' . $t_project_query );
  1079. array_push( $t_project_where_clauses, $t_project_query );
  1080. }
  1081. # date filter
  1082. if(( 'on' == $t_filter[FILTER_PROPERTY_FILTER_BY_DATE] ) && is_numeric( $t_filter[FILTER_PROPERTY_START_MONTH] ) && is_numeric( $t_filter[FILTER_PROPERTY_START_DAY] ) && is_numeric( $t_filter[FILTER_PROPERTY_START_YEAR] ) && is_numeric( $t_filter[FILTER_PROPERTY_END_MONTH] ) && is_numeric( $t_filter[FILTER_PROPERTY_END_DAY] ) && is_numeric( $t_filter[FILTER_PROPERTY_END_YEAR] ) ) {
  1083. $t_start_string = $t_filter[FILTER_PROPERTY_START_YEAR] . "-" . $t_filter[FILTER_PROPERTY_START_MONTH] . "-" . $t_filter[FILTER_PROPERTY_START_DAY] . " 00:00:00";
  1084. $t_end_string = $t_filter[FILTER_PROPERTY_END_YEAR] . "-" . $t_filter[FILTER_PROPERTY_END_MONTH] . "-" . $t_filter[FILTER_PROPERTY_END_DAY] . " 23:59:59";
  1085. $t_where_params[] = strtotime( $t_start_string );
  1086. $t_where_params[] = strtotime( $t_end_string );
  1087. array_push( $t_project_where_clauses, "($t_bug_table.date_submitted BETWEEN " . db_param() . " AND " . db_param() . " )" );
  1088. }
  1089. # view state
  1090. $t_view_state = db_prepare_int( $t_filter[FILTER_PROPERTY_VIEW_STATE_ID] );
  1091. if( !filter_field_is_any( $t_filter[FILTER_PROPERTY_VIEW_STATE_ID] ) ) {
  1092. $t_view_state_query = "($t_bug_table.view_state=" . db_param() . ')';
  1093. log_event( LOG_FILTERING, 'view_state query = ' . $t_view_state_query );
  1094. $t_where_params[] = $t_view_state;
  1095. array_push( $t_where_clauses, $t_view_state_query );
  1096. } else {
  1097. log_event( LOG_FILTERING, 'no view_state query' );
  1098. }
  1099. # reporter
  1100. if( !filter_field_is_any( $t_filter[FILTER_PROPERTY_REPORTER_ID] ) ) {
  1101. $t_clauses = array();
  1102. foreach( $t_filter[FILTER_PROPERTY_REPORTER_ID] as $t_filter_member ) {
  1103. if( filter_field_is_none( $t_filter_member ) ) {
  1104. array_push( $t_clauses, "0" );
  1105. } else {
  1106. $c_reporter_id = db_prepare_int( $t_filter_member );
  1107. if( filter_field_is_myself( $c_reporter_id ) ) {
  1108. array_push( $t_clauses, $c_user_id );
  1109. } else {
  1110. array_push( $t_clauses, $c_reporter_id );
  1111. }
  1112. }
  1113. }
  1114. if( 1 < count( $t_clauses ) ) {
  1115. $t_reporter_query = "( $t_bug_table.reporter_id in (" . implode( ', ', $t_clauses ) . ") )";
  1116. } else {
  1117. $t_reporter_query = "( $t_bug_table.reporter_id=$t_clauses[0] )";
  1118. }
  1119. log_event( LOG_FILTERING, 'reporter query = ' . $t_reporter_query );
  1120. array_push( $t_where_clauses, $t_reporter_query );
  1121. } else {
  1122. log_event( LOG_FILTERING, 'no reporter query' );
  1123. }
  1124. # limit reporter
  1125. # @@@ thraxisp - access_has_project_level checks greater than or equal to,
  1126. # this assumed that there aren't any holes above REPORTER where the limit would apply
  1127. #
  1128. if(( ON === $t_limit_reporters ) && ( !access_has_project_level( REPORTER + 1, $t_project_id, $t_user_id ) ) ) {
  1129. $c_reporter_id = $c_user_id;
  1130. $t_where_params[] = $c_reporter_id;
  1131. array_push( $t_where_clauses, "($t_bug_table.reporter_id=" . db_param() . ')' );
  1132. }
  1133. # handler
  1134. if( !filter_field_is_any( $t_filter[FILTER_PROPERTY_HANDLER_ID] ) ) {
  1135. $t_clauses = array();
  1136. foreach( $t_filter[FILTER_PROPERTY_HANDLER_ID] as $t_filter_member ) {
  1137. if( filter_field_is_none( $t_filter_member ) ) {
  1138. array_push( $t_clauses, 0 );
  1139. } else {
  1140. $c_handler_id = db_prepare_int( $t_filter_member );
  1141. if( filter_field_is_myself( $c_handler_id ) ) {
  1142. array_push( $t_clauses, $c_user_id );
  1143. } else {
  1144. array_push( $t_clauses, $c_handler_id );
  1145. }
  1146. }
  1147. }
  1148. if( 1 < count( $t_clauses ) ) {
  1149. $t_handler_query = "( $t_bug_table.handler_id in (" . implode( ', ', $t_clauses ) . ") )"

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