1<?php
2# MantisBT - A PHP based bugtracking system
3
4# MantisBT is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 2 of the License, or
7# (at your option) any later version.
8#
9# MantisBT is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with MantisBT.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Columns API
19 *
20 * @package CoreAPI
21 * @subpackage ColumnsAPI
22 * @copyright Copyright 2000 - 2002  Kenzaburo Ito - kenito@300baud.org
23 * @copyright Copyright 2002  MantisBT Team - mantisbt-dev@lists.sourceforge.net
24 * @link http://www.mantisbt.org
25 *
26 * @uses access_api.php
27 * @uses bug_api.php
28 * @uses category_api.php
29 * @uses config_api.php
30 * @uses constant_inc.php
31 * @uses custom_field_api.php
32 * @uses date_api.php
33 * @uses error_api.php
34 * @uses event_api.php
35 * @uses file_api.php
36 * @uses helper_api.php
37 * @uses icon_api.php
38 * @uses lang_api.php
39 * @uses prepare_api.php
40 * @uses print_api.php
41 * @uses project_api.php
42 * @uses sponsorship_api.php
43 * @uses string_api.php
44 */
45
46require_api( 'access_api.php' );
47require_api( 'bug_api.php' );
48require_api( 'category_api.php' );
49require_api( 'columns_api.php' );
50require_api( 'config_api.php' );
51require_api( 'constant_inc.php' );
52require_api( 'custom_field_api.php' );
53require_api( 'date_api.php' );
54require_api( 'error_api.php' );
55require_api( 'event_api.php' );
56require_api( 'file_api.php' );
57require_api( 'helper_api.php' );
58require_api( 'icon_api.php' );
59require_api( 'lang_api.php' );
60require_api( 'prepare_api.php' );
61require_api( 'print_api.php' );
62require_api( 'project_api.php' );
63require_api( 'sponsorship_api.php' );
64require_api( 'string_api.php' );
65
66/**
67 * Filters an array of columns based on configuration options.  The filtering can remove
68 * columns whose features are disabled.
69 *
70 * @param array $p_columns The columns proposed for display.
71 * @return array The columns array after removing the disabled features.
72 */
73function columns_filter_disabled( array $p_columns ) {
74	$t_columns = array();
75	$t_enable_profiles = ( config_get( 'enable_profiles' ) == ON );
76
77	foreach ( $p_columns as $t_column ) {
78		switch( $t_column ) {
79			case 'os':
80			case 'os_build':
81			case 'platform':
82				if( ! $t_enable_profiles ) {
83					continue 2;
84				}
85				break;
86
87			case 'eta':
88				if( config_get( 'enable_eta' ) == OFF ) {
89					continue 2;
90				}
91				break;
92
93			case 'projection':
94				if( config_get( 'enable_projection' ) == OFF ) {
95					continue 2;
96				}
97				break;
98
99			case 'build':
100				if( config_get( 'enable_product_build' ) == OFF ) {
101					continue 2;
102				}
103				break;
104
105			case 'sponsorship_total':
106				if( config_get( 'enable_sponsorship' ) == OFF ) {
107					continue 2;
108				}
109				break;
110
111				default:
112				# don't filter
113				break;
114		}
115		$t_columns[] = $t_column;
116	} # continued 2
117
118	return $t_columns;
119}
120
121/**
122 * Get a list of standard columns.
123 * @param boolean $p_enabled_columns_only Default true, if false returns all columns regardless of configuration settings.
124 * @return array of column names
125 */
126function columns_get_standard( $p_enabled_columns_only = true ) {
127	$t_reflection = new ReflectionClass( 'BugData' );
128	$t_columns = $t_reflection->getDefaultProperties();
129
130	$t_columns['selection'] = null;
131	$t_columns['edit'] = null;
132	$t_columns['notes'] = null;
133	$t_columns['tags'] = null;
134
135	# Overdue icon column (icons appears if an issue is beyond due_date)
136	$t_columns['overdue'] = null;
137
138	# The following fields are used internally and don't make sense as columns
139	unset( $t_columns['_stats'] );
140	unset( $t_columns['profile_id'] );
141	unset( $t_columns['sticky'] );
142	unset( $t_columns['loading'] );
143
144	# legacy field
145	unset( $t_columns['duplicate_id'] );
146
147	$t_column_names = array_keys( $t_columns );
148
149	if( $p_enabled_columns_only ) {
150		$t_column_names = columns_filter_disabled( $t_column_names );
151	}
152
153	return $t_column_names;
154}
155
156/**
157 * Allow plugins to define a set of class-based columns, and register/load
158 * them here to be used by columns_api.
159 * @return array Mapping of column name to column object
160 */
161function columns_get_plugin_columns() {
162	static $s_column_array = null;
163
164	if( is_null( $s_column_array ) ) {
165		$s_column_array = array();
166
167		$t_all_plugin_columns = event_signal( 'EVENT_FILTER_COLUMNS' );
168		foreach( $t_all_plugin_columns as $t_plugin => $t_plugin_columns ) {
169			foreach( $t_plugin_columns as $t_callback => $t_plugin_column_array ) {
170				if( is_array( $t_plugin_column_array ) ) {
171					foreach( $t_plugin_column_array as $t_column_item ) {
172						if( is_object( $t_column_item ) && $t_column_item instanceof MantisColumn ) {
173							$t_column_object = $t_column_item;
174						} elseif( class_exists( $t_column_item ) && is_subclass_of( $t_column_item, 'MantisColumn' ) ) {
175							$t_column_object = new $t_column_item();
176						} else {
177							continue;
178						}
179						$t_column_name = mb_strtolower( $t_plugin . '_' . $t_column_object->column );
180						$s_column_array[$t_column_name] = $t_column_object;
181					}
182				}
183			}
184		}
185	}
186
187	return $s_column_array;
188}
189
190/**
191 * Get all columns for existing custom_fields
192 * @return string Array of column names
193 */
194function columns_get_custom_fields() {
195	static $t_col_names = null;
196	if( isset( $t_col_names ) ) {
197		return $t_col_names;
198	}
199
200	$t_all_cfids = custom_field_get_ids();
201	$t_col_names = array();
202	foreach( $t_all_cfids as $t_id ) {
203		$t_col_names[] = column_get_custom_field_column_name( $t_id );
204	}
205	return $t_col_names;
206}
207
208/**
209 * Get all columns active for the current system.
210 * This includes standard, custom fields, and plugin columns.
211 * Columns for disabled modules are removed from this list, according to system
212 * configuration.
213 *
214 * This function cannot check on current user/project, so it can be used by core
215 * in potential unlogged-in scenarios.
216 * @return array Array of column names
217 */
218function columns_get_all_active_columns() {
219	$t_columns = array_merge(
220			columns_get_standard(),
221			array_keys( columns_get_plugin_columns() ),
222			columns_get_custom_fields()
223			);
224	return columns_filter_disabled( $t_columns );
225}
226
227/**
228 * Returns true if the specified $p_column is a plugin column.
229 * @param string $p_column A column name.
230 * @return boolean
231 */
232function column_is_plugin_column( $p_column ) {
233	$t_plugin_columns = columns_get_plugin_columns();
234	return isset( $t_plugin_columns[$p_column] );
235}
236
237/**
238 * Get all accessible columns for the current project / current user.
239 * @param integer $p_project_id A project identifier.
240 * @return array array of columns
241 * @access public
242 */
243function columns_get_all( $p_project_id = null ) {
244	$t_columns = columns_get_standard();
245
246	# add plugin columns
247	$t_columns = array_merge( $t_columns, array_keys( columns_get_plugin_columns() ) );
248
249	# Add project custom fields to the array.  Only add the ones for which the current user has at least read access.
250	if( $p_project_id === null ) {
251		$t_project_id = helper_get_current_project();
252	} else {
253		$t_project_id = $p_project_id;
254	}
255	# Get custom fields from this project and sub-projects
256	$t_projects = user_get_all_accessible_projects( null, $t_project_id );
257	$t_related_custom_field_ids = custom_field_get_linked_ids( $t_projects );
258	foreach( $t_related_custom_field_ids as $t_id ) {
259		$t_cfdef = custom_field_get_definition( $t_id );
260		$t_projects_to_check = array_intersect( $t_projects, custom_field_get_project_ids( $t_id ) );
261		if( access_has_any_project_level( (int)$t_cfdef['access_level_r'], $t_projects_to_check ) ) {
262			$t_columns[] = column_get_custom_field_column_name( $t_id );
263		}
264	}
265
266	return $t_columns;
267}
268
269/**
270 * Checks if the specified column is an extended column.  Extended columns are native columns that are
271 * associated with the issue but are saved in mantis_bug_text_table.
272 * @param string $p_column The column name.
273 * @return boolean true for extended; false otherwise.
274 * @access public
275 */
276function column_is_extended( $p_column ) {
277	switch( $p_column ) {
278		case 'description':
279		case 'steps_to_reproduce':
280		case 'additional_information':
281			return true;
282		default:
283			return false;
284	}
285}
286
287/**
288 * Checks if the specified column is a custom field column.
289 * @param string $p_column The column name.
290 * @return boolean True if its a custom field column
291 */
292function column_is_custom_field( $p_column ) {
293	$t_cf_columns = columns_get_custom_fields();
294	return in_array( $p_column, $t_cf_columns );
295}
296
297/**
298 * Checks if the specified column can be sorted
299 * @param string $p_column The column name.
300 * @return boolean True if the column can be sorted
301 */
302function column_is_sortable( $p_column ) {
303
304	# custom fields are always sortable
305	if( column_is_custom_field( $p_column ) ) {
306		return true;
307	}
308
309	# plugin fields contains a 'sortable' property
310	if( column_is_plugin_column( $p_column ) ) {
311		$t_plugin_columns = columns_get_plugin_columns();
312		$t_plugin_obj = $t_plugin_columns[$p_column];
313		return $t_plugin_obj->sortable;
314	}
315
316	#standard fields: define exceptions here
317	switch( $p_column ) {
318		case 'selection':
319		case 'edit':
320		case 'bugnotes_count':
321		case 'attachment_count':
322		case 'tags':
323		case 'overdue':
324		case 'additional_information':
325		case 'description':
326		case 'notes':
327		case 'steps_to_reproduce':
328			return false;
329	}
330
331	# after all exceptions, return true as default
332	return true;
333}
334
335/**
336 * Given a column name from the array of columns to be included in a view, this method checks if
337 * the column is a custom column and if so returns its name.  Note that for custom fields, then
338 * provided names will have the "custom_" prefix, where the returned ones won't have the prefix.
339 *
340 * @param string $p_column Column name.
341 * @return string The custom field column name or null if the specific column is not a custom field or invalid column.
342 * @access public
343 */
344function column_get_custom_field_name( $p_column ) {
345	if( strncmp( $p_column, 'custom_', 7 ) === 0 ) {
346		return mb_substr( $p_column, 7 );
347	}
348
349	return null;
350}
351
352/**
353 * Returns the name of a column corresponding to a custom field, providing the id as parameter.
354 *
355 * @param integer $p_cf_id	Custom field id
356 * @return string	The column name
357 */
358function column_get_custom_field_column_name( $p_cf_id ) {
359	$t_def = custom_field_get_definition( $p_cf_id );
360	if( $t_def ) {
361		return 'custom_' . $t_def['name'];
362	} else {
363		return null;
364	}
365}
366
367/**
368 * Converts a string of comma separate column names to an array.
369 *
370 * @param string $p_string Comma separate column name (not case sensitive).
371 * @return array The array with all column names lower case.
372 * @access public
373 */
374function columns_string_to_array( $p_string ) {
375	$t_columns = explode( ',', $p_string );
376	$t_count = count( $t_columns );
377
378	for( $i = 0; $i < $t_count; $i++ ) {
379		$t_columns[$i] = trim( $t_columns[$i] );
380	}
381
382	return $t_columns;
383}
384
385/**
386 * Gets the localized title for the specified column.  The column can be native or custom.
387 * The custom fields must contain the 'custom_' prefix.
388 *
389 * @param string $p_column The column name.
390 * @return string The column localized name.
391 * @access public
392 */
393function column_get_title( $p_column ) {
394	$t_custom_field = column_get_custom_field_name( $p_column );
395	if( $t_custom_field !== null ) {
396		$t_field_id = custom_field_get_id_from_name( $t_custom_field );
397
398		if( $t_field_id === false ) {
399			$t_custom_field = '@' . $t_custom_field . '@';
400		} else {
401			$t_def = custom_field_get_definition( $t_field_id );
402			$t_custom_field = lang_get_defaulted( $t_def['name'] );
403		}
404
405		return $t_custom_field;
406	}
407
408	$t_plugin_columns = columns_get_plugin_columns();
409	if( isset( $t_plugin_columns[$p_column] ) ) {
410		$t_column_object = $t_plugin_columns[$p_column];
411		return $t_column_object->title;
412	}
413
414	switch( $p_column ) {
415		case 'attachment_count':
416			return lang_get( 'attachments' );
417		case 'bugnotes_count':
418			return '#';
419		case 'category_id':
420			return lang_get( 'category' );
421		case 'edit':
422			return '';
423		case 'handler_id':
424			return lang_get( 'assigned_to' );
425		case 'last_updated':
426			return lang_get( 'updated' );
427		case 'os_build':
428			return lang_get( 'os_build' );
429		case 'project_id':
430			return lang_get( 'email_project' );
431		case 'reporter_id':
432			return lang_get( 'reporter' );
433		case 'selection':
434			return '';
435		case 'sponsorship_total':
436			return sponsorship_get_currency();
437		case 'version':
438			return lang_get( 'product_version' );
439		case 'view_state':
440			return lang_get( 'view_status' );
441		default:
442			return lang_get_defaulted( $p_column );
443	}
444}
445
446/**
447 * Checks an array of columns for duplicate or invalid fields.
448 *
449 * @param string $p_field_name          The logic name of the array being validated.  Used when triggering errors.
450 * @param array  $p_columns_to_validate The array of columns to validate.
451 * @param array  $p_columns_all         The list of all valid columns.
452 * @return boolean
453 * @access public
454 */
455function columns_ensure_valid( $p_field_name, array $p_columns_to_validate, array $p_columns_all ) {
456	$t_columns_all_lower = array_map( 'mb_strtolower', $p_columns_all );
457
458	# Check for invalid fields
459	foreach( $p_columns_to_validate as $t_column ) {
460		if( !in_array( mb_strtolower( $t_column ), $t_columns_all_lower ) ) {
461			error_parameters( $p_field_name, $t_column );
462			trigger_error( ERROR_COLUMNS_INVALID, ERROR );
463			return false;
464		}
465	}
466
467	# Check for duplicate fields
468	$t_columns_no_duplicates = array();
469	foreach( $p_columns_to_validate as $t_column ) {
470		$t_column_lower = mb_strtolower( $t_column );
471		if( in_array( $t_column, $t_columns_no_duplicates ) ) {
472			error_parameters( $p_field_name, $t_column );
473			trigger_error( ERROR_COLUMNS_DUPLICATE, ERROR );
474		} else {
475			$t_columns_no_duplicates[] = $t_column_lower;
476		}
477	}
478
479	return true;
480}
481
482/**
483 * Validates an array of column names and removes ones that are not valid.  The validation
484 * is not case sensitive.
485 *
486 * @param array $p_columns     The array of column names to be validated.
487 * @param array $p_columns_all The array of all valid column names.
488 * @return array The array of valid column names found in $p_columns.
489 * @access public
490 */
491function columns_remove_invalid( array $p_columns, array $p_columns_all ) {
492	$t_columns_all_lower = array_values( array_map( 'mb_strtolower', $p_columns_all ) );
493	$t_columns = array();
494
495	foreach( $p_columns as $t_column ) {
496		if( in_array( mb_strtolower( $t_column ), $t_columns_all_lower ) ) {
497			$t_columns[] = $t_column;
498		}
499	}
500
501	return $t_columns;
502}
503
504/**
505 * Print table header for selection column
506 *
507 * @param string  $p_sort           Sort.
508 * @param string  $p_dir            Direction.
509 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
510 * @return void
511 * @access public
512 */
513function print_column_title_selection( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
514	echo '<th class="column-selection"> &#160; </th>';
515}
516
517/**
518 * Print table header for edit column
519 * @param string  $p_sort           Sort.
520 * @param string  $p_dir            Direction.
521 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
522 * @return void
523 * @access public
524 */
525function print_column_title_edit( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
526	echo '<th class="column-edit"> &#160; </th>';
527}
528
529/**
530 * Print table header for column ID
531 *
532 * @param string  $p_sort           Sort.
533 * @param string  $p_dir            Direction.
534 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
535 * @return void
536 * @access public
537 */
538function print_column_title_id( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
539	echo '<th class="column-id">';
540	print_view_bug_sort_link( lang_get( 'id' ), 'id', $p_sort, $p_dir, $p_columns_target );
541	print_sort_icon( $p_dir, $p_sort, 'id' );
542	echo '</th>';
543}
544
545/**
546 * Print table header for column project id
547 *
548 * @param string  $p_sort           Sort.
549 * @param string  $p_dir            Direction.
550 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
551 * @return void
552 * @access public
553 */
554function print_column_title_project_id( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
555	echo '<th class="column-project-id">';
556	print_view_bug_sort_link( lang_get( 'email_project' ), 'project_id', $p_sort, $p_dir, $p_columns_target );
557	print_sort_icon( $p_dir, $p_sort, 'project_id' );
558	echo '</th>';
559}
560
561/**
562 * Print table header for column reporter id
563 *
564 * @param string  $p_sort           Sort.
565 * @param string  $p_dir            Direction.
566 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
567 * @return void
568 * @access public
569 */
570function print_column_title_reporter_id( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
571	echo '<th class="column-reporter">';
572	print_view_bug_sort_link( lang_get( 'reporter' ), 'reporter_id', $p_sort, $p_dir, $p_columns_target );
573	print_sort_icon( $p_dir, $p_sort, 'reporter_id' );
574	echo '</th>';
575}
576
577/**
578 * Print table header for column handler id
579 *
580 * @param string  $p_sort           Sort.
581 * @param string  $p_dir            Direction.
582 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
583 * @return void
584 * @access public
585 */
586function print_column_title_handler_id( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
587	echo '<th class="column-assigned-to">';
588	print_view_bug_sort_link( lang_get( 'assigned_to' ), 'handler_id', $p_sort, $p_dir, $p_columns_target );
589	print_sort_icon( $p_dir, $p_sort, 'handler_id' );
590	echo '</th>';
591}
592
593/**
594 * Print table header for column priority
595 *
596 * @param string  $p_sort           Sort.
597 * @param string  $p_dir            Direction.
598 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
599 * @return void
600 * @access public
601 */
602function print_column_title_priority( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
603	echo '<th class="column-priority">';
604	# Use short label only when displaying icons
605	$t_label = lang_get( config_get( 'show_priority_text' ) ? 'priority' : 'priority_abbreviation' );
606	print_view_bug_sort_link( $t_label, 'priority', $p_sort, $p_dir, $p_columns_target );
607	print_sort_icon( $p_dir, $p_sort, 'priority' );
608	echo '</th>';
609}
610
611/**
612 * Print table header for column reproducibility
613 *
614 * @param string  $p_sort           Sort.
615 * @param string  $p_dir            Direction.
616 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
617 * @return void
618 * @access public
619 */
620function print_column_title_reproducibility( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
621	echo '<th class="column-reproducibility">';
622	print_view_bug_sort_link( lang_get( 'reproducibility' ), 'reproducibility', $p_sort, $p_dir, $p_columns_target );
623	print_sort_icon( $p_dir, $p_sort, 'reproducibility' );
624	echo '</th>';
625}
626
627/**
628 * Print table header for column projection
629 *
630 * @param string  $p_sort           Sort.
631 * @param string  $p_dir            Direction.
632 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
633 * @return void
634 * @access public
635 */
636function print_column_title_projection( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
637	echo '<th class="column-projection">';
638	print_view_bug_sort_link( lang_get( 'projection' ), 'projection', $p_sort, $p_dir, $p_columns_target );
639	print_sort_icon( $p_dir, $p_sort, 'projection' );
640	echo '</th>';
641}
642
643/**
644 * Print table header for column ETA
645 *
646 * @param string  $p_sort           Sort.
647 * @param string  $p_dir            Direction.
648 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
649 * @return void
650 * @access public
651 */
652function print_column_title_eta( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
653	echo '<th class="column-eta">';
654	print_view_bug_sort_link( lang_get( 'eta' ), 'eta', $p_sort, $p_dir, $p_columns_target );
655	print_sort_icon( $p_dir, $p_sort, 'eta' );
656	echo '</th>';
657}
658
659/**
660 * Print table header for column resolution
661 *
662 * @param string  $p_sort           Sort.
663 * @param string  $p_dir            Direction.
664 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
665 * @return void
666 * @access public
667 */
668function print_column_title_resolution( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
669	echo '<th class="column-resolution">';
670	print_view_bug_sort_link( lang_get( 'resolution' ), 'resolution', $p_sort, $p_dir, $p_columns_target );
671	print_sort_icon( $p_dir, $p_sort, 'resolution' );
672	echo '</th>';
673}
674
675/**
676 * Print table header for column fixed in version
677 *
678 * @param string  $p_sort           Sort.
679 * @param string  $p_dir            Direction.
680 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
681 * @return void
682 * @access public
683 */
684function print_column_title_fixed_in_version( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
685	echo '<th class="column-fixed-in-version">';
686	print_view_bug_sort_link( lang_get( 'fixed_in_version' ), 'fixed_in_version', $p_sort, $p_dir, $p_columns_target );
687	print_sort_icon( $p_dir, $p_sort, 'fixed_in_version' );
688	echo '</th>';
689}
690
691/**
692 * Print table header for column tags
693 *
694 * @param string  $p_sort           Sort.
695 * @param string  $p_dir            Direction.
696 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
697 * @return void
698 * @access public
699 */
700function print_column_title_tags( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
701	echo '<th class="column-tags">' . lang_get('tags') . '</th>';
702}
703
704/**
705 * Print table header for column target version
706 *
707 * @param string  $p_sort           Sort.
708 * @param string  $p_dir            Direction.
709 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
710 * @return void
711 * @access public
712 */
713function print_column_title_target_version( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
714	echo '<th class="column-target-version">';
715	print_view_bug_sort_link( lang_get( 'target_version' ), 'target_version', $p_sort, $p_dir, $p_columns_target );
716	print_sort_icon( $p_dir, $p_sort, 'target_version' );
717	echo '</th>';
718}
719
720/**
721 * Print table header for column view state
722 *
723 * @param string  $p_sort           Sort.
724 * @param string  $p_dir            Direction.
725 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
726 * @return void
727 * @access public
728 */
729function print_column_title_view_state( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
730	echo '<th class="column-view-state">';
731	$t_view_state_text = lang_get( 'view_status' );
732	$t_view_state_icon = ' ' . icon_get( 'fa-lock', '', $t_view_state_text );
733	print_view_bug_sort_link( $t_view_state_icon, 'view_state', $p_sort, $p_dir, $p_columns_target );
734	print_sort_icon( $p_dir, $p_sort, 'view_state' );
735	echo '</th>';
736}
737
738/**
739 * Print table header for column OS
740 *
741 * @param string  $p_sort           Sort.
742 * @param string  $p_dir            Direction.
743 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
744 * @return void
745 * @access public
746 */
747function print_column_title_os( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
748	echo '<th class="column-os">';
749	print_view_bug_sort_link( lang_get( 'os' ), 'os', $p_sort, $p_dir, $p_columns_target );
750	print_sort_icon( $p_dir, $p_sort, 'os' );
751	echo '</th>';
752}
753
754/**
755 * Print table header for column OS Build
756 *
757 * @param string  $p_sort           Sort.
758 * @param string  $p_dir            Direction.
759 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
760 * @return void
761 * @access public
762 */
763function print_column_title_os_build( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
764	echo '<th class="column-os-build">';
765	print_view_bug_sort_link( lang_get( 'os_build' ), 'os_build', $p_sort, $p_dir, $p_columns_target );
766	print_sort_icon( $p_dir, $p_sort, 'os_build' );
767	echo '</th>';
768}
769
770/**
771 * Print table header for column Build
772 *
773 * @param string  $p_sort           Sort.
774 * @param string  $p_dir            Direction.
775 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
776 * @return void
777 * @access public
778 */
779function print_column_title_build( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
780	if( $p_columns_target != COLUMNS_TARGET_CSV_PAGE ) {
781		echo '<th class="column-build">';
782		print_view_bug_sort_link( lang_get( 'build' ), 'build', $p_sort, $p_dir, $p_columns_target );
783		print_sort_icon( $p_dir, $p_sort, 'build' );
784		echo '</th>';
785	} else {
786		echo lang_get( 'build' );
787	}
788}
789
790/**
791 * Print table header for column Platform
792 *
793 * @param string  $p_sort           Sort.
794 * @param string  $p_dir            Direction.
795 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
796 * @return void
797 * @access public
798 */
799function print_column_title_platform( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
800	echo '<th class="column-platform">';
801	print_view_bug_sort_link( lang_get( 'platform' ), 'platform', $p_sort, $p_dir, $p_columns_target );
802	print_sort_icon( $p_dir, $p_sort, 'platform' );
803	echo '</th>';
804}
805
806/**
807 * Print table header for column version
808 *
809 * @param string  $p_sort           Sort.
810 * @param string  $p_dir            Direction.
811 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
812 * @return void
813 * @access public
814 */
815function print_column_title_version( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
816	echo '<th class="column-version">';
817	print_view_bug_sort_link( lang_get( 'product_version' ), 'version', $p_sort, $p_dir, $p_columns_target );
818	print_sort_icon( $p_dir, $p_sort, 'version' );
819	echo '</th>';
820}
821
822/**
823 * Print table header for column date submitted
824 *
825 * @param string  $p_sort           Sort.
826 * @param string  $p_dir            Direction.
827 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
828 * @return void
829 * @access public
830 */
831function print_column_title_date_submitted( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
832	echo '<th class="column-date-submitted">';
833	print_view_bug_sort_link( lang_get( 'date_submitted' ), 'date_submitted', $p_sort, $p_dir, $p_columns_target );
834	print_sort_icon( $p_dir, $p_sort, 'date_submitted' );
835	echo '</th>';
836}
837
838/**
839 * Print table header for column attachment count
840 *
841 * @param string  $p_sort           Sort.
842 * @param string  $p_dir            Direction.
843 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
844 * @return void
845 * @access public
846 */
847function print_column_title_attachment_count( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
848	$t_attachment_count_text = lang_get( 'attachment_count' );
849	$t_attachment_count_icon = icon_get( 'fa-paperclip', 'blue', $t_attachment_count_text );
850	echo "\t" . '<th class="column-attachments">' . $t_attachment_count_icon . '</th>' . "\n";
851}
852
853/**
854 * Print table header for column category
855 *
856 * @param string  $p_sort           Sort.
857 * @param string  $p_dir            Direction.
858 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
859 * @return void
860 * @access public
861 */
862function print_column_title_category_id( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
863	echo '<th class="column-category">';
864	print_view_bug_sort_link( lang_get( 'category' ), 'category_id', $p_sort, $p_dir, $p_columns_target );
865	print_sort_icon( $p_dir, $p_sort, 'category_id' );
866	echo '</th>';
867}
868
869/**
870 * Prints Category column header
871 * The actual column is 'category_id', this function is just here for backwards
872 * compatibility
873 * @param string  $p_sort           Sort.
874 * @param string  $p_dir            Direction.
875 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
876 * @return void
877 * @access public
878 */
879function print_column_title_category( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
880	trigger_error( ERROR_GENERIC, WARNING );
881	print_column_title_category_id( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE );
882}
883
884/**
885 * Print table header for column sponsorship total
886 *
887 * @param string  $p_sort           Sort.
888 * @param string  $p_dir            Direction.
889 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
890 * @return void
891 * @access public
892 */
893function print_column_title_sponsorship_total( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
894	echo "\t<th class=\"column-sponsorship\">";
895	print_view_bug_sort_link( sponsorship_get_currency(), 'sponsorship_total', $p_sort, $p_dir, $p_columns_target );
896	print_sort_icon( $p_dir, $p_sort, 'sponsorship_total' );
897	echo "</th>\n";
898}
899
900/**
901 * Print table header for column severity
902 *
903 * @param string  $p_sort           Sort.
904 * @param string  $p_dir            Direction.
905 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
906 * @return void
907 * @access public
908 */
909function print_column_title_severity( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
910	echo '<th class="column-severity">';
911	print_view_bug_sort_link( lang_get( 'severity' ), 'severity', $p_sort, $p_dir, $p_columns_target );
912	print_sort_icon( $p_dir, $p_sort, 'severity' );
913	echo '</th>';
914}
915
916/**
917 * Print table header for column status
918 *
919 * @param string  $p_sort           Sort.
920 * @param string  $p_dir            Direction.
921 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
922 * @return void
923 * @access public
924 */
925function print_column_title_status( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
926	echo '<th class="column-status">';
927	print_view_bug_sort_link( lang_get( 'status' ), 'status', $p_sort, $p_dir, $p_columns_target );
928	print_sort_icon( $p_dir, $p_sort, 'status' );
929	echo '</th>';
930}
931
932/**
933 * Print table header for column last updated
934 *
935 * @param string  $p_sort           Sort.
936 * @param string  $p_dir            Direction.
937 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
938 * @return void
939 * @access public
940 */
941function print_column_title_last_updated( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
942	echo '<th class="column-last-modified">';
943	print_view_bug_sort_link( lang_get( 'updated' ), 'last_updated', $p_sort, $p_dir, $p_columns_target );
944	print_sort_icon( $p_dir, $p_sort, 'last_updated' );
945	echo '</th>';
946}
947
948/**
949 * Print table header for column summary
950 *
951 * @param string  $p_sort           Sort.
952 * @param string  $p_dir            Direction.
953 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
954 * @return void
955 * @access public
956 */
957function print_column_title_summary( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
958	echo '<th class="column-summary">';
959	print_view_bug_sort_link( lang_get( 'summary' ), 'summary', $p_sort, $p_dir, $p_columns_target );
960	print_sort_icon( $p_dir, $p_sort, 'summary' );
961	echo '</th>';
962}
963
964/**
965 * Print table header for column bugnotes count
966 *
967 * @param string  $p_sort           Sort.
968 * @param string  $p_dir            Direction.
969 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
970 * @return void
971 * @access public
972 */
973function print_column_title_bugnotes_count( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
974	echo '<th class="column-bugnotes-count">';
975	print_icon( 'fa-comments', 'blue' );
976	echo '</th>';
977}
978
979/**
980 * Print table header for column description
981 *
982 * @param string  $p_sort           Sort.
983 * @param string  $p_dir            Direction.
984 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
985 * @return void
986 * @access public
987 */
988function print_column_title_description( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
989	echo '<th class="column-description">';
990	echo lang_get( 'description' );
991	echo '</th>';
992}
993
994/**
995 * Print table header for notes column.
996 *
997 * @param string  $p_sort           Sort.
998 * @param string  $p_dir            Direction.
999 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1000 * @return void
1001 * @access public
1002 */
1003function print_column_title_notes( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1004	echo '<th class="column-notes">';
1005	echo lang_get( 'bug_notes_title' );
1006	echo '</th>';
1007}
1008
1009/**
1010 * Print table header for column steps to reproduce
1011 *
1012 * @param string  $p_sort           Sort.
1013 * @param string  $p_dir            Direction.
1014 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1015 * @return void
1016 * @access public
1017 */
1018function print_column_title_steps_to_reproduce( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1019	echo '<th class="column-steps-to-reproduce">';
1020	echo lang_get( 'steps_to_reproduce' );
1021	echo '</th>';
1022}
1023
1024/**
1025 * Print table header for column additional information
1026 *
1027 * @param string  $p_sort           Sort.
1028 * @param string  $p_dir            Direction.
1029 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1030 * @return void
1031 * @access public
1032 */
1033function print_column_title_additional_information( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1034	echo '<th class="column-additional-information">';
1035	echo lang_get( 'additional_information' );
1036	echo '</th>';
1037}
1038
1039/**
1040 * Prints Due Date column header
1041 * @param string  $p_sort           Sort.
1042 * @param string  $p_dir            Direction.
1043 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1044 * @return void
1045 * @access public
1046 */
1047function print_column_title_due_date( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1048	echo '<th class="column-due-date">';
1049	print_view_bug_sort_link( lang_get( 'due_date' ), 'due_date', $p_sort, $p_dir, $p_columns_target );
1050	print_sort_icon( $p_dir, $p_sort, 'due_date' );
1051	echo '</th>';
1052}
1053
1054/**
1055 * Print table header for column overdue
1056 *
1057 * @param string  $p_sort           Sort.
1058 * @param string  $p_dir            Direction.
1059 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1060 * @return void
1061 * @access public
1062 */
1063function print_column_title_overdue( $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1064	echo '<th class="column-overdue">';
1065	$t_overdue_text = lang_get( 'overdue' );
1066	$t_overdue_icon = ' ' . icon_get( 'fa-times-circle-o', '', $t_overdue_text );
1067	print_view_bug_sort_link( $t_overdue_icon, 'due_date', $p_sort, $p_dir, $p_columns_target );
1068	print_sort_icon( $p_dir, $p_sort, 'due_date' );
1069	echo '</th>';
1070}
1071
1072/**
1073 * Print table data for column selection
1074 *
1075 * @param BugData $p_bug            BugData object.
1076 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1077 * @return void
1078 * @access public
1079 */
1080function print_column_selection( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1081	global $g_checkboxes_exist;
1082
1083	echo '<td class="column-selection">';
1084	if( COLUMNS_TARGET_PRINT_PAGE == $p_columns_target ||
1085		# check report_bug_threshold for the actions "copy" or "move" into any other project
1086		access_has_any_project_level( 'report_bug_threshold' ) ||
1087		# !TODO: check if any other projects actually exist for the bug to be moved to
1088		access_has_project_level( config_get( 'move_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
1089		# !TODO: factor in $g_auto_set_status_to_assigned == ON
1090		access_has_project_level( config_get( 'update_bug_assign_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
1091		access_has_project_level( config_get( 'update_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
1092		access_has_project_level( config_get( 'delete_bug_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
1093		# !TODO: check to see if the bug actually has any different selectable workflow states
1094		access_has_project_level( config_get( 'update_bug_status_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
1095		access_has_project_level( config_get( 'set_bug_sticky_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
1096		access_has_project_level( config_get( 'change_view_status_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
1097		access_has_project_level( config_get( 'add_bugnote_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
1098		access_has_project_level( config_get( 'tag_attach_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ||
1099		access_has_project_level( config_get( 'roadmap_update_threshold', null, null, $p_bug->project_id ), $p_bug->project_id ) ) {
1100		$g_checkboxes_exist = true;
1101		echo '<div class="checkbox no-padding no-margin"><label>';
1102		printf( '<input type="checkbox" name="bug_arr[]" value="%d" class="ace" />', $p_bug->id );
1103		echo '<span class="lbl"></span>';
1104		echo '</label></div>';
1105	} else {
1106		echo '&#160;';
1107	}
1108	echo '</td>';
1109}
1110
1111/**
1112 * Print column title for a specific custom column.
1113 *
1114 * @param string  $p_column         Active column.
1115 * @param object  $p_column_object  Column object.
1116 * @param string  $p_sort           Sort.
1117 * @param string  $p_dir            Direction.
1118 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1119 * @return void
1120 * @access public
1121 */
1122function print_column_title_plugin( $p_column, $p_column_object, $p_sort, $p_dir, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1123	echo '<th class="column-plugin">';
1124	if( $p_column_object->sortable ) {
1125		print_view_bug_sort_link( string_display_line( $p_column_object->title ), $p_column, $p_sort, $p_dir, $p_columns_target );
1126		print_sort_icon( $p_dir, $p_sort, $p_column );
1127	} else {
1128		echo string_display_line( $p_column_object->title );
1129	}
1130	echo '</th>';
1131}
1132
1133/**
1134 * Print custom column content for a specific bug.
1135 * @param object  $p_column_object  Column object.
1136 * @param BugData $p_bug            BugData object.
1137 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1138 * @return void
1139 * @access public
1140 */
1141function print_column_plugin( $p_column_object, BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1142	if( $p_columns_target != COLUMNS_TARGET_CSV_PAGE ) {
1143		echo '<td class="column-plugin">';
1144		$p_column_object->display( $p_bug, $p_columns_target );
1145		echo '</td>';
1146	} else {
1147		$p_column_object->display( $p_bug, $p_columns_target );
1148	}
1149}
1150
1151/**
1152 * Print column content for column edit
1153 *
1154 * @param BugData $p_bug            BugData object.
1155 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1156 * @return void
1157 * @access public
1158 */
1159function print_column_edit( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1160
1161	echo '<td class="column-edit">';
1162
1163	if( !bug_is_readonly( $p_bug->id ) && access_has_bug_level( config_get( 'update_bug_threshold' ), $p_bug->id ) ) {
1164		echo '<a href="' . string_get_bug_update_url( $p_bug->id ) . '">';
1165		print_icon( 'fa-pencil', 'bigger-130 padding-2 grey', lang_get( 'edit' ) );
1166		echo '</a>';
1167	} else {
1168		echo '&#160;';
1169	}
1170
1171	echo '</td>';
1172}
1173
1174/**
1175 * Print column content for column priority
1176 *
1177 * @param BugData $p_bug            BugData object.
1178 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1179 * @return void
1180 * @access public
1181 */
1182function print_column_priority( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1183	echo '<td class="column-priority">';
1184	if( ON == config_get( 'show_priority_text' ) ) {
1185		print_formatted_priority_string( $p_bug );
1186	} else {
1187		print_status_icon( $p_bug->priority );
1188	}
1189	echo '</td>';
1190}
1191
1192/**
1193 * Print column content for column id
1194 *
1195 * @param BugData $p_bug            BugData object.
1196 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1197 * @return void
1198 * @access public
1199 */
1200function print_column_id( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1201	echo '<td class="column-id">';
1202	print_bug_link( $p_bug->id, false );
1203	echo '</td>';
1204}
1205
1206/**
1207 * Print column content for column sponsorship total
1208 *
1209 * @param BugData $p_bug            BugData object.
1210 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1211 * @return void
1212 * @access public
1213 */
1214function print_column_sponsorship_total( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1215	echo "\t<td class=\"right column-sponsorship\">";
1216
1217	if( $p_bug->sponsorship_total > 0 ) {
1218		$t_sponsorship_amount = sponsorship_format_amount( $p_bug->sponsorship_total );
1219		echo string_no_break( $t_sponsorship_amount );
1220	}
1221
1222	echo "</td>\n";
1223}
1224
1225/**
1226 * Print column content for column bugnotes count
1227 *
1228 * @param BugData $p_bug            BugData object.
1229 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1230 * @return void
1231 * @access public
1232 */
1233function print_column_bugnotes_count( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1234	global $g_filter;
1235
1236	# grab the bugnote count
1237	$t_bugnote_stats = bug_get_bugnote_stats( $p_bug->id );
1238	if( is_array( $t_bugnote_stats ) ) {
1239		$t_bugnote_count = $t_bugnote_stats['count'];
1240		$t_bugnote_updated = $t_bugnote_stats['last_modified'];
1241	} else {
1242		$t_bugnote_count = 0;
1243	}
1244
1245	echo '<td class="column-bugnotes-count">';
1246	if( $t_bugnote_count > 0 ) {
1247		$t_show_in_bold = $t_bugnote_updated > strtotime( '-' . $g_filter[FILTER_PROPERTY_HIGHLIGHT_CHANGED] . ' hours' );
1248		if( $t_show_in_bold ) {
1249			echo '<span class="bold">';
1250		}
1251		print_link( string_get_bug_view_url( $p_bug->id ) . '&nbn=' . $t_bugnote_count . '#bugnotes', $t_bugnote_count );
1252		if( $t_show_in_bold ) {
1253			echo '</span>';
1254		}
1255	} else {
1256		echo '&#160;';
1257	}
1258
1259	echo '</td>';
1260}
1261
1262/**
1263 * Print column content for column attachment count
1264 *
1265 * @param BugData $p_bug            BugData object.
1266 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1267 * @return void
1268 * @access public
1269 */
1270function print_column_attachment_count( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1271
1272	# Check for attachments
1273	$t_attachment_count = 0;
1274	if( file_can_view_bug_attachments( $p_bug->id, null ) ) {
1275		$t_attachment_count = file_bug_attachment_count( $p_bug->id );
1276	}
1277
1278	echo '<td class="column-attachments">';
1279
1280	if( $t_attachment_count > 0 ) {
1281		$t_href = string_get_bug_view_url( $p_bug->id ) . '#attachments';
1282		$t_href_title = sprintf( lang_get( 'view_attachments_for_issue' ), $t_attachment_count, $p_bug->id );
1283		echo '<a href="' . $t_href . '" title="' . $t_href_title . '">' . $t_attachment_count . '</a>';
1284	} else {
1285		echo ' &#160; ';
1286	}
1287
1288	echo "</td>\n";
1289}
1290
1291/**
1292 * Print column content for column category id
1293 *
1294 * @param BugData $p_bug            BugData object.
1295 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1296 * @return void
1297 * @access public
1298 */
1299function print_column_category_id( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1300	global $t_sort, $t_dir;
1301
1302	# grab the project name
1303	$t_project_name = project_get_field( $p_bug->project_id, 'name' );
1304
1305	echo '<td class="column-category">';
1306	echo '<div class="align-left">';
1307
1308	# type project name if viewing 'all projects' or if issue is in a subproject
1309	if( ON == config_get( 'show_bug_project_links' ) && helper_get_current_project() != $p_bug->project_id ) {
1310		echo '<span class="small project">[';
1311		print_view_bug_sort_link( string_display_line( $t_project_name ), 'project_id', $t_sort, $t_dir, $p_columns_target );
1312		echo ']</span>&#160;&#160;';
1313	}
1314
1315	echo string_display_line( category_full_name( $p_bug->category_id, false ) );
1316	echo '</div>';
1317	echo '</td>';
1318}
1319
1320/**
1321 * Print column content for column severity
1322 *
1323 * @param BugData $p_bug            BugData object.
1324 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1325 * @return void
1326 * @access public
1327 */
1328function print_column_severity( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1329	echo '<td class="column-severity">';
1330	print_formatted_severity_string( $p_bug );
1331	echo '</td>';
1332}
1333
1334/**
1335 * Print column content for column eta
1336 *
1337 * @param BugData $p_bug            BugData object.
1338 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1339 * @return void
1340 * @access public
1341 */
1342function print_column_eta( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1343	echo '<td class="column-eta">', get_enum_element( 'eta', $p_bug->eta, auth_get_current_user_id(), $p_bug->project_id ), '</td>';
1344}
1345
1346/**
1347 * Print column content for column projection
1348 *
1349 * @param BugData $p_bug            BugData object.
1350 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1351 * @return void
1352 * @access public
1353 */
1354function print_column_projection( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1355	echo '<td class="column-projection">', get_enum_element( 'projection', $p_bug->projection, auth_get_current_user_id(), $p_bug->project_id ), '</td>';
1356}
1357
1358/**
1359 * Print column content for column reproducibility
1360 *
1361 * @param BugData $p_bug            BugData object.
1362 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1363 * @return void
1364 * @access public
1365 */
1366function print_column_reproducibility( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1367	echo '<td class="column-reproducibility">', get_enum_element( 'reproducibility', $p_bug->reproducibility, auth_get_current_user_id(), $p_bug->project_id ), '</td>';
1368}
1369
1370/**
1371 * Print column content for column resolution
1372 *
1373 * @param BugData $p_bug            BugData object.
1374 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1375 * @return void
1376 * @access public
1377 */
1378function print_column_resolution( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1379	echo '<td class="column-resolution">',
1380		get_enum_element( 'resolution', $p_bug->resolution, auth_get_current_user_id(), $p_bug->project_id ),
1381		'</td>';
1382}
1383
1384/**
1385 * Print column content for column status
1386 *
1387 * @param BugData $p_bug            BugData object.
1388 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1389 * @return void
1390 * @access public
1391 */
1392function print_column_status( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1393	$t_current_user = auth_get_current_user_id();
1394	# choose color based on status
1395	$t_status_css = html_get_status_css_fg( $p_bug->status, $t_current_user, $p_bug->project_id );
1396	echo '<td class="column-status">';
1397	echo '<div class="align-left">';
1398	print_icon( 'fa-square', 'fa-status-box ' . $t_status_css );
1399	printf( ' <span title="%s">%s</span>',
1400		get_enum_element( 'resolution', $p_bug->resolution, $t_current_user, $p_bug->project_id ),
1401		get_enum_element( 'status', $p_bug->status, $t_current_user, $p_bug->project_id )
1402	);
1403
1404	# print handler user next to status
1405	if( $p_bug->handler_id > 0
1406			&& ON == config_get( 'show_assigned_names', null, $t_current_user, $p_bug->project_id )
1407			&& access_can_see_handler_for_bug( $p_bug ) ) {
1408		printf( ' (%s)', prepare_user_name( $p_bug->handler_id ) );
1409	}
1410	echo '</div></td>';
1411}
1412
1413/**
1414 * Print column content for column handler id
1415 *
1416 * @param BugData $p_bug            BugData object.
1417 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1418 * @return void
1419 * @access public
1420 */
1421function print_column_handler_id( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1422	echo '<td class="column-assigned-to">';
1423
1424	# In case of a specific project, if the current user has no access to the field, then it would have been excluded from the
1425	# list of columns to view.  In case of ALL_PROJECTS, then we need to check the access per row.
1426	if( $p_bug->handler_id > 0 && ( helper_get_current_project() != ALL_PROJECTS || access_has_project_level( config_get( 'view_handler_threshold' ), $p_bug->project_id ) ) ) {
1427		echo prepare_user_name( $p_bug->handler_id );
1428	}
1429
1430	echo '</td>';
1431}
1432
1433/**
1434 * Print column content for column reporter id
1435 *
1436 * @param BugData $p_bug            BugData object.
1437 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1438 * @return void
1439 * @access public
1440 */
1441function print_column_reporter_id( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1442	echo '<td class="column-reporter">';
1443	echo prepare_user_name( $p_bug->reporter_id );
1444	echo '</td>';
1445}
1446
1447/**
1448 * Print column content for column project id
1449 *
1450 * @param BugData $p_bug            BugData object.
1451 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1452 * @return void
1453 * @access public
1454 */
1455function print_column_project_id( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1456	echo '<td class="column-project-id">';
1457	echo string_display_line( project_get_name( $p_bug->project_id ) );
1458	echo '</td>';
1459}
1460
1461/**
1462 * Print column content for column last updated
1463 *
1464 * @param BugData $p_bug            BugData object.
1465 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1466 * @return void
1467 * @access public
1468 */
1469function print_column_last_updated( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1470	global $g_filter;
1471
1472	$t_last_updated = string_display_line( date( config_get( 'short_date_format' ), $p_bug->last_updated ) );
1473
1474	echo '<td class="column-last-modified">';
1475	if( $p_bug->last_updated > strtotime( '-' . $g_filter[FILTER_PROPERTY_HIGHLIGHT_CHANGED] . ' hours' ) ) {
1476		printf( '<span class="bold">%s</span>', $t_last_updated );
1477	} else {
1478		echo $t_last_updated;
1479	}
1480	echo '</td>';
1481}
1482
1483/**
1484 * Print column content for column date submitted
1485 *
1486 * @param BugData $p_bug            BugData object.
1487 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1488 * @return void
1489 * @access public
1490 */
1491function print_column_date_submitted( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1492	$t_date_submitted = string_display_line( date( config_get( 'short_date_format' ), $p_bug->date_submitted ) );
1493
1494	echo '<td class="column-date-submitted">', $t_date_submitted, '</td>';
1495}
1496
1497/**
1498 * Print column content for column summary
1499 *
1500 * @param BugData $p_bug            BugData object.
1501 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1502 * @return void
1503 * @access public
1504 */
1505function print_column_summary( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1506	if( $p_columns_target == COLUMNS_TARGET_CSV_PAGE ) {
1507		$t_summary = string_attribute( $p_bug->summary );
1508	} else {
1509		$t_summary = string_display_line_links( $p_bug->summary );
1510	}
1511
1512	$t_bug_url = string_get_bug_view_url( $p_bug->id );
1513	echo '<td class="column-summary"><a href="' . $t_bug_url . '">' . $t_summary . '</a></td>';
1514}
1515
1516/**
1517 * Print column content for column description
1518 *
1519 * @param BugData $p_bug            BugData object.
1520 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1521 * @return void
1522 * @access public
1523 */
1524function print_column_description( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1525	$t_description = string_display_links( $p_bug->description );
1526
1527	echo '<td class="column-description">', $t_description, '</td>';
1528}
1529
1530/**
1531 * Print column content for notes column
1532 *
1533 * @param BugData $p_bug            BugData object.
1534 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1535 * @return void
1536 * @access public
1537 */
1538function print_column_notes( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1539	$t_notes = bugnote_get_all_visible_as_string( $p_bug->id, /* user_bugnote_order */ 'DESC', /* user_bugnote_limit */ 0 );
1540
1541	echo '<td class="column-notes">', string_display_links( $t_notes ), '</td>';
1542}
1543
1544/**
1545 * Print column content for column steps to reproduce
1546 *
1547 * @param BugData $p_bug            BugData object.
1548 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1549 * @return void
1550 * @access public
1551 */
1552function print_column_steps_to_reproduce( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1553	$t_steps_to_reproduce = string_display_links( $p_bug->steps_to_reproduce );
1554
1555	echo '<td class="column-steps-to-reproduce">', $t_steps_to_reproduce, '</td>';
1556}
1557
1558/**
1559 * Print column content for column additional information
1560 *
1561 * @param BugData $p_bug            BugData object.
1562 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1563 * @return void
1564 * @access public
1565 */
1566function print_column_additional_information( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1567	$t_additional_information = string_display_links( $p_bug->additional_information );
1568
1569	echo '<td class="column-additional-information">', $t_additional_information, '</td>';
1570}
1571
1572/**
1573 * Print column content for column target version
1574 *
1575 * @param BugData $p_bug            BugData object.
1576 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1577 * @return void
1578 * @access public
1579 */
1580function print_column_target_version( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1581	echo '<td class="column-target-version">';
1582
1583	# In case of a specific project, if the current user has no access to the field, then it would have been excluded from the
1584	# list of columns to view.  In case of ALL_PROJECTS, then we need to check the access per row.
1585	if( helper_get_current_project() != ALL_PROJECTS || access_has_project_level( config_get( 'roadmap_view_threshold' ), $p_bug->project_id ) ) {
1586		echo string_display_line( $p_bug->target_version );
1587	}
1588
1589	echo '</td>';
1590}
1591
1592/**
1593 * Print column content for view state column
1594 *
1595 * @param BugData $p_bug            BugData object.
1596 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1597 * @return void
1598 * @access public
1599 */
1600function print_column_view_state( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1601
1602	echo '<td class="column-view-state">';
1603
1604	if( VS_PRIVATE == $p_bug->view_state ) {
1605		$t_view_state_text = lang_get( 'private' );
1606		print_icon( 'fa-lock', '', $t_view_state_text );
1607	} else {
1608		echo '&#160;';
1609	}
1610
1611	echo '</td>';
1612}
1613
1614/**
1615 * Print column content for column tags
1616 *
1617 * @param BugData $p_bug            BugData object.
1618 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1619 * @return void
1620 * @access public
1621 */
1622function print_column_tags( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1623	echo '<td class="column-tags">';
1624
1625	if( access_has_bug_level( config_get( 'tag_view_threshold' ), $p_bug->id ) ) {
1626		echo string_display_line( tag_bug_get_all( $p_bug->id ) );
1627	}
1628
1629	echo '</td>';
1630}
1631
1632/**
1633 * Print column content for column due date
1634 *
1635 * @param BugData $p_bug            BugData object.
1636 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1637 * @return void
1638 * @access public
1639 */
1640function print_column_due_date( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1641	if( !access_has_bug_level( config_get( 'due_date_view_threshold' ), $p_bug->id ) ||
1642		date_is_null( $p_bug->due_date )
1643	) {
1644		$t_css = '';
1645		$t_value = '&#160;';
1646	} else {
1647		$t_css = " due-" . bug_overdue_level( $p_bug->id );
1648		$t_value = string_display_line( date( config_get( 'short_date_format' ), $p_bug->due_date ) );
1649	}
1650
1651	printf( '<td class="column-due-date%s">%s</td>', $t_css, $t_value );
1652}
1653
1654/**
1655 * Print column content for column overdue
1656 *
1657 * @param BugData $p_bug            BugData object.
1658 * @param integer $p_columns_target See COLUMNS_TARGET_* in constant_inc.php.
1659 * @return void
1660 * @access public
1661 */
1662function print_column_overdue( BugData $p_bug, $p_columns_target = COLUMNS_TARGET_VIEW_PAGE ) {
1663
1664	echo '<td class="column-overdue">';
1665
1666	if( access_has_bug_level( config_get( 'due_date_view_threshold' ), $p_bug->id ) &&
1667		!date_is_null( $p_bug->due_date )
1668	) {
1669		$t_level = bug_overdue_level( $p_bug->id );
1670		if( $t_level === 0 ) {
1671			$t_icon = 'fa-times-circle-o';
1672			$t_overdue_text_hover = sprintf(
1673				lang_get( 'overdue_since' ),
1674				date( config_get( 'short_date_format' ), $p_bug->due_date )
1675			);
1676		} else {
1677			$t_icon = $t_level === false ? 'fa-info-circle' : 'fa-warning';
1678
1679			$t_duration = $p_bug->due_date - db_now();
1680			if( $t_duration <= SECONDS_PER_DAY ) {
1681				$t_overdue_text_hover = lang_get( 'overdue_one_day' );
1682			} else {
1683				$t_overdue_text_hover = sprintf(
1684					lang_get( 'overdue_days' ),
1685					ceil( $t_duration / SECONDS_PER_DAY )
1686				);
1687			}
1688		}
1689		print_icon( $t_icon, '', string_display_line( $t_overdue_text_hover ) );
1690	} else {
1691		echo '&#160;';
1692	}
1693
1694	echo '</td>';
1695}
1696