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 * User Preferences API
19 *
20 * @package CoreAPI
21 * @subpackage UserPreferencesAPI
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 authentication_api.php
27 * @uses config_api.php
28 * @uses constant_inc.php
29 * @uses database_api.php
30 * @uses error_api.php
31 * @uses lang_api.php
32 * @uses user_api.php
33 * @uses utility_api.php
34 */
35
36require_api( 'authentication_api.php' );
37require_api( 'config_api.php' );
38require_api( 'constant_inc.php' );
39require_api( 'database_api.php' );
40require_api( 'error_api.php' );
41require_api( 'lang_api.php' );
42require_api( 'user_api.php' );
43require_api( 'utility_api.php' );
44
45/**
46 * Preference Structure Definition
47 */
48class UserPreferences {
49	/**
50	 * Default Profile
51	 */
52	protected $default_profile = null;
53
54	/**
55	 * Default Project for user
56	 */
57	protected $default_project = null;
58
59	/**
60	 * Automatic Refresh delay
61	 */
62	protected $refresh_delay = null;
63
64	/**
65	 * Automatic Redirect delay
66	 */
67	protected $redirect_delay = null;
68
69	/**
70	 * Bugnote order - oldest/newest first
71	 */
72	protected $bugnote_order = null;
73
74	/**
75	 * Receive email on new bugs
76	 */
77	protected $email_on_new = null;
78
79	/**
80	 * Receive email on assigned bugs
81	 */
82	protected $email_on_assigned = null;
83
84	/**
85	 * Receive email on feedback
86	 */
87	protected $email_on_feedback = null;
88
89	/**
90	 * Receive email on resolved bugs
91	 */
92	protected $email_on_resolved = null;
93
94	/**
95	 * Receive email on closed bugs
96	 */
97	protected $email_on_closed = null;
98
99	/**
100	 * Receive email on reopened bugs
101	 */
102	protected $email_on_reopened = null;
103
104	/**
105	 * Receive email on new bugnote
106	 */
107	protected $email_on_bugnote = null;
108
109	/**
110	 * Receive email on bug status change
111	 */
112	protected $email_on_status = null;
113
114	/**
115	 * Receive email on bug priority change
116	 */
117	protected $email_on_priority = null;
118
119	/**
120	 * Minimum Severity on which to trigger email if set to receive
121	 */
122	protected $email_on_new_min_severity = null;
123
124	/**
125	 * Minimum Severity on which to trigger email if set to receive
126	 */
127	protected $email_on_assigned_min_severity = null;
128
129	/**
130	 * Minimum Severity on which to trigger email if set to receive
131	 */
132	protected $email_on_feedback_min_severity = null;
133
134	/**
135	 * Minimum Severity on which to trigger email if set to receive
136	 */
137	protected $email_on_resolved_min_severity = null;
138
139	/**
140	 * Minimum Severity on which to trigger email if set to receive
141	 */
142	protected $email_on_closed_min_severity = null;
143
144	/**
145	 * Minimum Severity on which to trigger email if set to receive
146	 */
147	protected $email_on_reopened_min_severity = null;
148
149	/**
150	 * Minimum Severity on which to trigger email if set to receive
151	 */
152	protected $email_on_bugnote_min_severity = null;
153
154	/**
155	 * Minimum Severity on which to trigger email if set to receive
156	 */
157	protected $email_on_status_min_severity = null;
158
159	/**
160	 * Minimum Severity on which to trigger email if set to receive
161	 */
162	protected $email_on_priority_min_severity = null;
163
164	/**
165	 * Number of bug notes to include in generated emails
166	 */
167	protected $email_bugnote_limit = null;
168
169	/**
170	 * Users language preference
171	 */
172	protected $language = null;
173
174	/**
175	 * User Timezone
176	 */
177	protected $timezone = null;
178
179	/**
180	 * User id
181	 */
182	private $pref_user_id;
183
184	/**
185	 * Project ID
186	 */
187	private $pref_project_id;
188
189	/**
190	 * Default Values - Config Field Mappings
191	 */
192	private static $_default_mapping = array(
193	'default_profile' => array( 'default_profile', 'int' ),
194	'default_project' => array( 'default_project', 'int' ),
195	'refresh_delay' => array( 'default_refresh_delay', 'int' ),
196	'redirect_delay' => array( 'default_redirect_delay', 'int' ),
197	'bugnote_order' => array( 'default_bugnote_order', 'string' ),
198	'email_on_new' => array( 'default_email_on_new', 'int' ),
199	'email_on_assigned' => array(  'default_email_on_assigned', 'int' ),
200	'email_on_feedback' => array(  'default_email_on_feedback', 'int' ),
201	'email_on_resolved' => array(  'default_email_on_resolved', 'int' ),
202	'email_on_closed' => array(  'default_email_on_closed', 'int' ),
203	'email_on_reopened' => array(  'default_email_on_reopened', 'int' ),
204	'email_on_bugnote' => array(  'default_email_on_bugnote', 'int' ),
205	'email_on_status' => array(  'default_email_on_status', 'int' ),
206	'email_on_priority' => array(  'default_email_on_priority', 'int' ),
207	'email_on_new_min_severity' => array(  'default_email_on_new_minimum_severity', 'int' ),
208	'email_on_assigned_min_severity' => array(  'default_email_on_assigned_minimum_severity', 'int' ),
209	'email_on_feedback_min_severity' => array(  'default_email_on_feedback_minimum_severity', 'int' ),
210	'email_on_resolved_min_severity' => array(  'default_email_on_resolved_minimum_severity', 'int' ),
211	'email_on_closed_min_severity' => array(  'default_email_on_closed_minimum_severity', 'int' ),
212	'email_on_reopened_min_severity' => array(  'default_email_on_reopened_minimum_severity', 'int' ),
213	'email_on_bugnote_min_severity' => array(  'default_email_on_bugnote_minimum_severity', 'int' ),
214	'email_on_status_min_severity' => array(  'default_email_on_status_minimum_severity', 'int' ),
215	'email_on_priority_min_severity' => array(  'default_email_on_priority_minimum_severity', 'int' ),
216	'email_bugnote_limit' => array(  'default_email_bugnote_limit', 'int' ),
217	'language' => array(  'default_language', 'string' ),
218	'timezone' => array( 'default_timezone', 'string' ),
219	);
220
221	/**
222	 * Constructor
223	 * @param integer $p_user_id    A valid user identifier.
224	 * @param integer $p_project_id A valid project identifier.
225	 */
226	function __construct( $p_user_id, $p_project_id ) {
227		$this->default_profile = 0;
228		$this->default_project = ALL_PROJECTS;
229
230		$this->pref_user_id = (int)$p_user_id;
231		$this->pref_project_id = (int)$p_project_id;
232	}
233
234	/**
235	 * Overloaded function
236	 * @param string $p_name  The Property name to set.
237	 * @param string $p_value A value to set the property to.
238	 * @return void
239	 * @access private
240	 */
241	public function __set( $p_name, $p_value ) {
242		switch( $p_name ) {
243			case 'timezone':
244				if( $p_value == '' ) {
245					$p_value = null;
246				}
247		}
248		$this->$p_name = $p_value;
249	}
250
251	/**
252	 * Overloaded function
253	 * @param string $p_string Property name.
254	 * @access private
255	 * @return mixed
256	 */
257	public function __get( $p_string ) {
258		if( is_null( $this->$p_string ) ) {
259			$this->$p_string = config_get( self::$_default_mapping[$p_string][0], null, $this->pref_user_id, $this->pref_project_id );
260		}
261		switch( self::$_default_mapping[$p_string][1] ) {
262			case 'int':
263				return (int)($this->$p_string);
264			default:
265				return $this->$p_string;
266		}
267	}
268
269	/**
270	 * Public Get() function
271	 * @param string $p_string Property to get.
272	 * @return mixed
273	 */
274	function Get( $p_string ) {
275		if( is_null( $this->$p_string ) ) {
276			$this->$p_string = config_get( self::$_default_mapping[$p_string][0], null, $this->pref_user_id, $this->pref_project_id );
277		}
278		return $this->$p_string;
279	}
280}
281
282$g_cache_user_pref = array();
283$g_cache_current_user_pref = array();
284
285/**
286 * Cache a user preferences row if necessary and return the cached copy.
287 * If preferences can't be found, will trigger an error if $p_trigger_errors is
288 * true (default), or return false otherwise
289 * @param integer $p_user_id        A valid user identifier.
290 * @param integer $p_project_id     A valid project identifier.
291 * @param boolean $p_trigger_errors Whether to trigger error on failure.
292 * @return boolean|array
293 */
294function user_pref_cache_row( $p_user_id, $p_project_id = ALL_PROJECTS, $p_trigger_errors = true ) {
295	global $g_cache_user_pref;
296
297	if( !isset( $g_cache_user_pref[(int)$p_user_id][(int)$p_project_id] ) ) {
298		user_pref_cache_array_rows( array( $p_user_id ), $p_project_id );
299	}
300
301	$t_row = $g_cache_user_pref[(int)$p_user_id][(int)$p_project_id];
302	if( false === $t_row && $p_trigger_errors ) {
303		trigger_error( ERROR_USER_PREFS_NOT_FOUND, ERROR );
304	}
305	return $t_row;
306}
307
308/**
309 * Cache user preferences for a set of users
310 * @param array   $p_user_id_array An array of valid user identifiers.
311 * @param integer $p_project_id    A valid project identifier.
312 * @return void
313 */
314function user_pref_cache_array_rows( array $p_user_id_array, $p_project_id = ALL_PROJECTS ) {
315	global $g_cache_user_pref;
316	$c_user_id_array = array();
317
318	# identify the user ids that are not cached already.
319	foreach( $p_user_id_array as $t_user_id ) {
320		if( !isset( $g_cache_user_pref[(int)$t_user_id][(int)$p_project_id] ) ) {
321			$c_user_id_array[(int)$t_user_id] = (int)$t_user_id;
322		}
323	}
324
325	# if all users are already cached, then return
326	if( empty( $c_user_id_array ) ) {
327		return;
328	}
329
330	$t_query = new DbQuery( 'SELECT * FROM {user_pref} WHERE user_id IN :user_array AND project_id = :project_id' );
331	$t_query->bind( 'user_array', $c_user_id_array );
332	$t_query->bind( 'project_id', (int)$p_project_id );
333
334	$t_result = $t_query->fetch_all();
335	if( $t_result ) {
336		foreach( $t_result as $t_row ) {
337			$t_user_id = (int)$t_row['user_id'];
338			if( !isset( $g_cache_user_pref[$t_user_id] ) ) {
339				$g_cache_user_pref[$t_user_id] = array();
340			}
341			$g_cache_user_pref[$t_user_id][(int)$p_project_id] = $t_row;
342
343			# remove found users from required set.
344			unset( $c_user_id_array[$t_user_id] );
345		}
346	}
347
348	# cache users that are not found as false (i.e. negative cache)
349	foreach( $c_user_id_array as $t_user_id ) {
350		$g_cache_user_pref[(int)$t_user_id][(int)$p_project_id] = false;
351	}
352}
353
354/**
355 * Clear the user preferences cache (or just the given id if specified)
356 * @param integer $p_user_id    A valid user identifier.
357 * @param integer $p_project_id A valid project identifier.
358 * @return boolean
359 */
360function user_pref_clear_cache( $p_user_id = null, $p_project_id = null ) {
361	global $g_cache_user_pref;
362
363	if( null === $p_user_id ) {
364		$g_cache_user_pref = array();
365	} else if( null === $p_project_id ) {
366		unset( $g_cache_user_pref[(int)$p_user_id] );
367	} else {
368		unset( $g_cache_user_pref[(int)$p_user_id][(int)$p_project_id] );
369	}
370
371	return true;
372}
373
374/**
375 * return true if the user has preferences assigned for the given project,
376 * false otherwise
377 * @param integer $p_user_id    A valid user identifier.
378 * @param integer $p_project_id A valid project identifier.
379 * @return boolean
380 */
381function user_pref_exists( $p_user_id, $p_project_id = ALL_PROJECTS ) {
382	if( false === user_pref_cache_row( $p_user_id, $p_project_id, false ) ) {
383		return false;
384	} else {
385		return true;
386	}
387}
388
389/**
390 * Backwards compatibility wrapper for user_pref_db_insert()
391 * @deprecated	Use user_pref_db_insert()
392 */
393function user_pref_insert( $p_user_id, $p_project_id, UserPreferences $p_prefs ) {
394	user_ensure_unprotected( $p_user_id );
395	user_pref_db_insert( $p_user_id, $p_project_id, $p_prefs );
396}
397
398/**
399 * perform an insert of a preference object into the DB
400 * @param integer         $p_user_id    A valid user identifier.
401 * @param integer         $p_project_id A valid project identifier.
402 * @param UserPreferences $p_prefs      A UserPrefences Object.
403 * @return boolean
404 */
405function user_pref_db_insert( $p_user_id, $p_project_id, UserPreferences $p_prefs ) {
406	static $s_vars;
407	$c_user_id = (int)$p_user_id;
408	$c_project_id = (int)$p_project_id;
409
410	if( $s_vars == null ) {
411		$s_vars = getClassProperties( 'UserPreferences', 'protected' );
412	}
413
414	$t_values = array();
415	$t_values[] = $c_user_id;
416	$t_values[] = $c_project_id;
417	foreach( $s_vars as $t_var => $t_val ) {
418		array_push( $t_values, $p_prefs->Get( $t_var ) );
419	}
420
421	$t_columns = 'user_id, project_id, ' . implode( ', ', array_keys( $s_vars ) );
422	$t_query = new DBQuery( 'INSERT INTO {user_pref} (' . $t_columns . ') VALUES :values' );
423	$t_query->bind( 'values', $t_values );
424	$t_query->execute();
425
426	return true;
427}
428
429/**
430 * Backwards compatibility wrapper for user_pref_db_update()
431 * @deprecated	Use user_pref_db_update()
432 */
433function user_pref_update( $p_user_id, $p_project_id, UserPreferences $p_prefs ) {
434	user_ensure_unprotected( $p_user_id );
435	user_pref_db_update($p_user_id, $p_project_id, $p_prefs );
436	user_pref_clear_cache( $p_user_id, $p_project_id );
437}
438
439/**
440 * perform an update of a preference object into the DB
441 * @param integer         $p_user_id    A valid user identifier.
442 * @param integer         $p_project_id A valid project identifier.
443 * @param UserPreferences $p_prefs      A UserPrefences Object.
444 * @return void
445 */
446function user_pref_db_update( $p_user_id, $p_project_id, UserPreferences $p_prefs ) {
447	static $s_vars;
448
449	if( $s_vars == null ) {
450		$s_vars = getClassProperties( 'UserPreferences', 'protected' );
451	}
452
453	$t_pairs = array();
454	foreach( $s_vars as $t_var => $t_val ) {
455		$t_pairs[] = "$t_var = :$t_var";
456		$t_param[$t_var] = $p_prefs->$t_var;
457	}
458
459	$t_query = new DbQuery( 'UPDATE {user_pref}
460		SET ' . implode( ', ', $t_pairs ) . '
461		WHERE user_id = :user_id AND project_id = :project_id' );
462	$t_query->bind( $t_param );
463	$t_query->bind( 'user_id', $p_user_id );
464	$t_query->bind( 'project_id', $p_project_id );
465	$t_query->execute();
466}
467
468/**
469 * Backwards compatibility wrapper for user_pref_db_delete()
470 * @deprecated	Use user_pref_db_delete()
471 */
472function user_pref_delete( $p_user_id, $p_project_id = ALL_PROJECTS ) {
473	user_ensure_unprotected( $p_user_id );
474	user_pref_db_delete( $p_user_id, $p_project_id );
475	user_pref_clear_cache( $p_user_id, $p_project_id );
476}
477
478/**
479 * delete a preferences row
480 * returns true if the preferences were successfully deleted
481 * @param integer $p_user_id    A valid user identifier.
482 * @param integer $p_project_id A valid project identifier.
483 * @return void
484 */
485function user_pref_db_delete( $p_user_id, $p_project_id ) {
486	$t_query = new DbQuery( 'DELETE FROM {user_pref}'
487		. ' WHERE user_id=:user_id AND project_id=:project_id'
488	);
489	$t_query->bind( 'user_id', (int)$p_user_id );
490	$t_query->bind( 'project_id', (int)$p_project_id );
491	$t_query->execute();
492}
493
494/**
495 * Backwards compatibility wrapper for user_pref_db_delete_user()
496 * @deprecated	Use user_pref_db_delete_user()
497 */
498function user_pref_delete_all( $p_user_id ) {
499	user_ensure_unprotected( $p_user_id );
500	user_pref_db_delete_user( $p_user_id );
501	user_pref_clear_cache( $p_user_id );
502}
503
504/**
505 * delete all preferences for a user in all projects
506 * returns true if the prefs were successfully deleted
507 *
508 * It is far more efficient to delete them all in one query than to
509 *  call user_pref_delete() for each one and the code is short so that's
510 *  what we do
511 * @param integer $p_user_id A valid user identifier.
512 * @return void
513 */
514function user_pref_db_delete_user( $p_user_id ) {
515	$t_query = new DbQuery( 'DELETE FROM {user_pref} WHERE user_id=:user_id' );
516	$t_query->bind( 'user_id', (int)$p_user_id );
517	$t_query->execute();
518}
519
520/**
521 * Sets project default to ALL_PROJECTS.
522 *
523 * @param integer $p_project_id A valid project identifier.
524 * @param array   $p_users      A list of users (empty = all users).
525 *
526 * @return void
527 */
528function user_pref_clear_project_default( $p_project_id, array $p_users = array() ) {
529	$t_query = new DbQuery( 'UPDATE {user_pref}'
530		. ' SET default_project = ' . ALL_PROJECTS
531		. ' WHERE default_project = :default'
532	);
533	$t_query->bind( 'default', (int)$p_project_id );
534	if( $p_users ) {
535		$t_query->append_sql( ' AND ' . $t_query->sql_in( 'user_id', 'users' ) );
536		$t_query->bind( 'users', $p_users );
537	}
538	$t_query->execute();
539}
540
541/**
542 * Sets project default to ALL_PROJECTS if current default is not valid.
543 *
544 * When users are removed from a project, the ones having that project as
545 * default but who are no longer authorized to access it, need to have their
546 * now-invalid preference updated.
547 *
548 * @param integer $p_project_id A valid project identifier.
549 * @param array   $p_users      A list of users (empty = all users).
550 *
551 * @return void
552 */
553function user_pref_clear_invalid_project_default( $p_project_id, array $p_users = array() ) {
554	# Get all users having the project as default
555	$t_query = new DbQuery( 'SELECT user_id FROM {user_pref} WHERE default_project = :default' );
556	$t_query->bind( 'default', (int)$p_project_id );
557	if( $p_users ) {
558		$t_query->append_sql( ' AND ' . $t_query->sql_in( 'user_id', 'users' ) );
559		$t_query->bind( 'users', $p_users );
560	}
561	$t_query->execute();
562
563	$t_users_having_project_as_default = array_column( $t_query->fetch_all(), 'user_id' );
564
565	# Users who can't access the project anymore must have the default cleared
566	$t_users_to_clear = array();
567	foreach( $t_users_having_project_as_default as $t_id ) {
568		if( access_get_project_level( $p_project_id, $t_id ) == ANYBODY ) {
569			$t_users_to_clear[] = $t_id;
570		}
571	}
572
573	if( !empty( $t_users_to_clear ) ) {
574		user_pref_clear_project_default( $p_project_id, $t_users_to_clear );
575	}
576}
577
578/**
579 * delete all preferences for a project for all users (part of deleting the project)
580 * returns true if the prefs were successfully deleted
581 *
582 * It is far more efficient to delete them all in one query than to
583 * call user_pref_delete() for each one and the code is short so that's what we do
584 * @param integer $p_project_id A valid project identifier.
585 * @return void
586 */
587function user_pref_db_delete_project( $p_project_id ) {
588	$t_query = new DbQuery( 'DELETE FROM {user_pref} WHERE project_id=:project_id' );
589	$t_query->bind( 'project_id', (int)$p_project_id );
590	$t_query->execute();
591}
592
593/**
594 * return the user's preferences in a UserPreferences object
595 * @param integer $p_user_id    A valid user identifier.
596 * @param integer $p_project_id A valid project identifier.
597 * @return UserPreferences
598 */
599function user_pref_get( $p_user_id, $p_project_id = ALL_PROJECTS ) {
600	static $s_vars;
601	global $g_cache_current_user_pref;
602
603	if( isset( $g_cache_current_user_pref[(int)$p_project_id] ) &&
604		auth_is_user_authenticated() &&
605		auth_get_current_user_id() == $p_user_id ) {
606		return $g_cache_current_user_pref[(int)$p_project_id];
607	}
608
609	$t_prefs = new UserPreferences( $p_user_id, $p_project_id );
610
611	$t_row = user_pref_cache_row( $p_user_id, $p_project_id, false );
612
613	# If the user has no preferences for the given project
614	if( false === $t_row ) {
615		if( ALL_PROJECTS != $p_project_id ) {
616			# Try to get the prefs for ALL_PROJECTS (the defaults)
617			$t_row = user_pref_cache_row( $p_user_id, ALL_PROJECTS, false );
618		}
619
620		# If $t_row is still false (the user doesn't have default preferences)
621		if( false === $t_row ) {
622			# We use an empty array
623			$t_row = array();
624		}
625	}
626
627	if( $s_vars == null ) {
628		$s_vars = getClassProperties( 'UserPreferences', 'protected' );
629	}
630
631	$t_row_keys = array_keys( $t_row );
632
633	# Check each variable in the class
634	foreach( $s_vars as $t_var => $t_val ) {
635		# If we got a field from the DB with the same name
636		if( in_array( $t_var, $t_row_keys, true ) ) {
637			# Store that value in the object
638			$t_prefs->$t_var = $t_row[$t_var];
639		}
640	}
641	if( auth_is_user_authenticated() && auth_get_current_user_id() == $p_user_id ) {
642		$g_cache_current_user_pref[(int)$p_project_id] = $t_prefs;
643	}
644	return $t_prefs;
645}
646
647/**
648 * Return the specified preference field for the user id
649 * If the preference can't be found try to return a defined default
650 * If that fails, trigger a WARNING and return ''
651 * @param integer $p_user_id    A valid user identifier.
652 * @param string  $p_pref_name  A valid user preference name.
653 * @param integer $p_project_id A valid project identifier.
654 * @return string
655 */
656function user_pref_get_pref( $p_user_id, $p_pref_name, $p_project_id = ALL_PROJECTS ) {
657	static $s_vars;
658
659	$t_prefs = user_pref_get( $p_user_id, $p_project_id );
660
661	if( $s_vars == null ) {
662		$t_reflection = new ReflectionClass( 'UserPreferences' );
663		$s_vars = $t_reflection->getDefaultProperties();
664	}
665
666	if( in_array( $p_pref_name, array_keys( $s_vars ), true ) ) {
667		return $t_prefs->Get( $p_pref_name );
668	} else {
669		error_parameters( $p_pref_name );
670		trigger_error( ERROR_DB_FIELD_NOT_FOUND, WARNING );
671		return '';
672	}
673}
674
675/**
676 * returns user language
677 * @param integer $p_user_id    A valid user identifier.
678 * @param integer $p_project_id A valid project identifier.
679 * @return string language name or null if invalid language specified
680 */
681function user_pref_get_language( $p_user_id, $p_project_id = ALL_PROJECTS ) {
682	$t_prefs = user_pref_get( $p_user_id, $p_project_id );
683
684	# ensure the language is a valid one
685	$t_lang = $t_prefs->language;
686	if( !lang_language_exists( $t_lang ) ) {
687		$t_lang = null;
688	}
689	return $t_lang;
690}
691
692/**
693 * Set a user preference
694 *
695 * By getting the preferences for the project first we deal fairly well with defaults. If there are currently no
696 * preferences for that project, the ALL_PROJECTS preferences will be returned so we end up storing a new set of
697 * preferences for the given project based on the preferences for ALL_PROJECTS.  If there isn't even an entry for
698 * ALL_PROJECTS, we'd get returned a default UserPreferences object to modify.
699 * @param integer $p_user_id    A valid user identifier.
700 * @param string  $p_pref_name  The name of the preference value to set.
701 * @param string  $p_pref_value A preference value to set.
702 * @param integer $p_project_id A valid project identifier.
703 * @param boolean $p_check_protected	Whether to perform a check to not allow modify protected users
704 * @return boolean
705 */
706function user_pref_set_pref( $p_user_id, $p_pref_name, $p_pref_value, $p_project_id = ALL_PROJECTS, $p_check_protected = true ) {
707	$t_prefs = user_pref_get( $p_user_id, $p_project_id );
708
709	if( $t_prefs->$p_pref_name != $p_pref_value ) {
710		$t_prefs->$p_pref_name = $p_pref_value;
711		user_pref_set( $p_user_id, $t_prefs, $p_project_id, $p_check_protected );
712	}
713
714	return true;
715}
716
717/**
718 * Set the user's preferences for the project from the given preferences object
719 * Do the work by calling update or insert as appropriate
720 * @param integer         $p_user_id    A valid user identifier.
721 * @param UserPreferences $p_prefs      A UserPreferences object containing settings to set.
722 * @param integer         $p_project_id A valid project identifier.
723 * @param boolean         $p_check_protected	Whether to perform a check to not allow modify protected users
724 * @return void
725 */
726function user_pref_set( $p_user_id, UserPreferences $p_prefs, $p_project_id = ALL_PROJECTS, $p_check_protected = true ) {
727	if( $p_check_protected ) {
728		user_ensure_unprotected( $p_user_id );
729	}
730	if( user_pref_exists( $p_user_id, $p_project_id ) ) {
731		user_pref_db_update( $p_user_id, $p_project_id, $p_prefs );
732	} else {
733		user_pref_db_insert( $p_user_id, $p_project_id, $p_prefs );
734	}
735	user_pref_clear_cache( $p_user_id, $p_project_id );
736}
737
738/**
739 * Delete the user's preferences row for the given project
740 * @param integer         $p_user_id    A valid user identifier.
741 * @param integer         $p_project_id A valid project identifier.
742 * @param boolean         $p_check_protected	Whether to perform a check to not allow modify protected users
743 * @return void
744 */
745function user_pref_reset( $p_user_id, $p_project_id = ALL_PROJECTS, $p_check_protected = true ) {
746	if( $p_check_protected ) {
747		user_ensure_unprotected( $p_user_id );
748	}
749	if( user_pref_exists( $p_user_id, $p_project_id ) ) {
750		user_pref_db_delete( $p_user_id, $p_project_id );
751	}
752	user_pref_clear_cache( $p_user_id, $p_project_id );
753}
754