PageRenderTime 67ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/core/filter_api.php

https://github.com/markkimsal/mantisbt
PHP | 4710 lines | 4192 code | 242 blank | 276 comment | 402 complexity | 8724547dee86b404c9fbc7831a69f3c1 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1

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

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