b2evolution PHP Cross Reference Blogging Systems

Source: /inc/users/model/_user.class.php - 4865 lines - 157032 bytes - Summary - Text - Print

Description: This file implements the User class. This file is part of the evoCore framework - {@link http://evocore.net/} See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements the User class.
   4   *
   5   * This file is part of the evoCore framework - {@link http://evocore.net/}
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
  10   *
  11   * {@internal License choice
  12   * - If you have received this file as part of a package, please find the license.txt file in
  13   *   the same folder or the closest folder above for complete license terms.
  14   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  15   *   then you must choose one of the following licenses before using the file:
  16   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  17   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  18   * }}
  19   *
  20   * {@internal Open Source relicensing agreement:
  21   * Daniel HAHLER grants Francois PLANQUE the right to license
  22   * Daniel HAHLER's contributions to this file and the b2evolution project
  23   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  24   * }}
  25   *
  26   * @package evocore
  27   *
  28   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  29   * @author fplanque: Francois PLANQUE
  30   * @author blueyed: Daniel HAHLER
  31   *
  32   * @version $Id: _user.class.php 6136 2014-03-08 07:59:48Z manuel $
  33   */
  34  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  35  
  36  
  37  // DEBUG: (Turn switch on or off to log debug info for specified category)
  38  $GLOBALS['debug_perms'] = false;
  39  
  40  
  41  load_class( '_core/model/dataobjects/_dataobject.class.php', 'DataObject' );
  42  
  43  /**
  44   * User Class
  45   *
  46   * @package evocore
  47   */
  48  class User extends DataObject
  49  {
  50      //var $postcode;
  51      var $age_min;
  52      var $age_max;
  53      var $login;
  54      var $pass;
  55      var $firstname;
  56      var $lastname;
  57      var $nickname;
  58      var $locale;
  59      var $email;
  60      var $url;
  61      var $datecreated;
  62      var $lastseen_ts;
  63      var $level;
  64      var $avatar_file_ID;
  65      var $reg_ctry_ID;
  66      var $ctry_ID;
  67      var $rgn_ID;
  68      var $subrg_ID;
  69      var $city_ID;
  70      var $source;
  71      var $unsubscribe_key;
  72      var $gender;
  73  
  74      /**
  75       * User account status
  76       *
  77       * 'new', 'activated', 'autoactivated', 'emailchanged', 'deactivated', 'failedactivation', 'closed'
  78       *
  79       * @var string
  80       */
  81      var $status;
  82  
  83      /**
  84       * Number of posts by this user. Use get_num_posts() to access this (lazy filled).
  85       * @var array ( key = status name, value = number of posts with status, key = '' - total number with all statuses )
  86       * @access protected
  87       */
  88      var $_num_posts;
  89  
  90      /**
  91       * Number of comments by this user. Use get_num_comments() to access this (lazy filled).
  92       * @var array  ( key = status name, value = number of comments with status, key = '' - total number with all statuses )
  93       * @access protected
  94       */
  95      var $_num_comments;
  96  
  97      /**
  98       * The ID of the (primary, currently only) group of the user.
  99       * @var integer
 100       */
 101      var $grp_ID;
 102  
 103      /**
 104       * Reference to group
 105       * @see User::get_Group()
 106       * @var Group
 107       * @access protected
 108       */
 109      var $Group;
 110  
 111      /**
 112       * Country lazy filled
 113       *
 114       * @var country
 115       */
 116      var $Country;
 117      var $Region;
 118      var $Subregion;
 119      var $City;
 120  
 121      /**
 122       * Blog posts statuses permissions
 123       */
 124      var $blog_post_statuses = array();
 125  
 126      /**
 127       * Cache for perms.
 128       * @access protected
 129       * @var array
 130       */
 131      var $cache_perms = array();
 132  
 133  
 134      /**
 135       * User fields
 136       */
 137      var $userfields = array();
 138      var $userfields_by_type = array();
 139      var $updated_fields = array();
 140      var $new_fields = array();
 141  
 142      /**
 143       * Userfield defs
 144       */
 145      var $userfield_defs;
 146  
 147      /**
 148       * Constructor
 149       *
 150       * @param object DB row
 151       */
 152  	function User( $db_row = NULL )
 153      {
 154          global $default_locale, $Settings, $localtimenow;
 155  
 156          // Call parent constructor:
 157          parent::DataObject( 'T_users', 'user_', 'user_ID' );
 158  
 159          // blueyed> TODO: this will never get translated for the current User if he has another locale/lang set than default, because it gets adjusted AFTER instantiating him/her..
 160          //       Use a callback (get_delete_restrictions/get_delete_cascades) instead? Should be also better for performance!
 161          // fp> These settings should probably be merged with the global database description used by the installer/upgrader. However I'm not sure about how compelx plugins would be able to integrate then...
 162          $this->delete_restrictions = array(
 163                  array( 'table'=>'T_blogs', 'fk'=>'blog_owner_user_ID', 'msg'=>T_('%d blogs owned by this user') ),
 164                  //array( 'table'=>'T_items__item', 'fk'=>'post_lastedit_user_ID', 'msg'=>T_('%d posts last edited by this user') ),
 165                  array( 'table'=>'T_items__item', 'fk'=>'post_assigned_user_ID', 'msg'=>T_('%d posts assigned to this user') ),
 166                  // Do not delete user private messages
 167                  //array( 'table'=>'T_messaging__message', 'fk'=>'msg_author_user_ID', 'msg'=>T_('The user has authored %d message(s)') ),
 168                  //array( 'table'=>'T_messaging__threadstatus', 'fk'=>'tsta_user_ID', 'msg'=>T_('The user is part of %d messaging thread(s)') ),
 169              );
 170  
 171          $this->delete_cascades = array(
 172                  array( 'table'=>'T_users__usersettings', 'fk'=>'uset_user_ID', 'msg'=>T_('%d user settings on collections') ),
 173                  array( 'table'=>'T_sessions', 'fk'=>'sess_user_ID', 'msg'=>T_('%d sessions opened by this user') ),
 174                  array( 'table'=>'T_coll_user_perms', 'fk'=>'bloguser_user_ID', 'msg'=>T_('%d user permissions on blogs') ),
 175                  array( 'table'=>'T_comments__votes', 'fk'=>'cmvt_user_ID', 'msg'=>T_('%d user votes on comments') ),
 176                  array( 'table'=>'T_subscriptions', 'fk'=>'sub_user_ID', 'msg'=>T_('%d blog subscriptions') ),
 177                  array( 'table'=>'T_items__item', 'fk'=>'post_creator_user_ID', 'msg'=>T_('%d posts created by this user') ),
 178                  array( 'table'=>'T_items__subscriptions', 'fk'=>'isub_user_ID', 'msg'=>T_('%d post subscriptions') ),
 179                  array( 'table'=>'T_messaging__contact', 'fk'=>'mct_to_user_ID', 'msg'=>T_('%d contacts from other users contact list') ),
 180                  array( 'table'=>'T_messaging__contact', 'fk'=>'mct_from_user_ID', 'msg'=>T_('%d contacts from this user contact list') ),
 181                  array( 'table'=>'T_messaging__contact_groups', 'fk'=>'cgr_user_ID', 'msg'=>T_('%d contact groups') ),
 182                  array( 'table'=>'T_messaging__contact_groupusers', 'fk'=>'cgu_user_ID', 'msg'=>T_('%d contacts from contact groups') ),
 183                  array( 'table'=>'T_pluginusersettings', 'fk'=>'puset_user_ID', 'msg'=>T_('%d user settings on plugins') ),
 184                  array( 'table'=>'T_users__fields', 'fk'=>'uf_user_ID', 'msg'=>T_('%d user fields') ),
 185                  array( 'table'=>'T_links', 'fk'=>'link_usr_ID', 'msg'=>T_('%d links to this user') ),
 186                  array( 'table'=>'T_links', 'fk'=>'link_creator_user_ID', 'msg'=>T_('%d links created by this user') ),
 187                  array( 'table'=>'T_files', 'fk'=>'file_root_ID', 'and_condition' => 'file_root_type = "user"', 'msg'=>T_('%d files from this user file root') ),
 188              );
 189  
 190          if( $db_row == NULL )
 191          { // Setting those object properties, which are not "NULL" in DB (MySQL strict mode):
 192  
 193              // echo 'Creating blank user';
 194              $this->set( 'login', 'login' );
 195              $this->set( 'pass', md5('pass') );
 196              $this->set( 'locale',
 197                  isset( $Settings )
 198                      ? $Settings->get('default_locale') // TODO: (settings) use "new users template setting"
 199                      : $default_locale );
 200              $this->set( 'email', '' );    // fp> TODO: this is an invalid value. Saving the object without a valid email should fail! (actually: it should be fixed by providing a valid email)
 201              $this->set( 'level', isset( $Settings ) ? $Settings->get('newusers_level') : 0 );
 202              if( isset($localtimenow) )
 203              {
 204                  $this->set_datecreated( $localtimenow );
 205                  $this->set( 'profileupdate_date', date( 'Y-m-d', $localtimenow ) );
 206              }
 207              else
 208              { // We don't know local time here!
 209                  $this->set_datecreated( time() );
 210                  $this->set( 'profileupdate_date', date( 'Y-m-d', time() ) );
 211              }
 212  
 213              if( isset( $Settings ) )
 214              { // Set Group for this user:
 215                  $GroupCache = & get_GroupCache();
 216                  $new_user_Group = & $GroupCache->get_by_ID( $Settings->get( 'newusers_grp_ID' ) );
 217                  $this->set_Group( $new_user_Group );
 218                  // set status for this user
 219                  $this->set( 'status', $Settings->get('newusers_mustvalidate') ? 'new' : 'autoactivated' );
 220              }
 221              else
 222              {
 223                  // set status for this user
 224                  $this->set( 'status', 'new' );
 225              }
 226  
 227              $this->set( 'unsubscribe_key', generate_random_key() );
 228          }
 229          else
 230          {
 231              // echo 'Instanciating existing user';
 232              $this->ID = $db_row->user_ID;
 233              $this->age_min = $db_row->user_age_min;
 234              $this->age_max = $db_row->user_age_max;
 235              $this->login = $db_row->user_login;
 236              $this->pass = $db_row->user_pass;
 237              $this->firstname = $db_row->user_firstname;
 238              $this->lastname = $db_row->user_lastname;
 239              $this->nickname = $db_row->user_nickname;
 240              $this->locale = $db_row->user_locale;
 241              $this->email = $db_row->user_email;
 242              $this->status = $db_row->user_status;
 243              $this->url = $db_row->user_url;
 244              $this->datecreated = $db_row->user_created_datetime;
 245              $this->lastseen_ts = $db_row->user_lastseen_ts;
 246              $this->level = $db_row->user_level;
 247              $this->unsubscribe_key = $db_row->user_unsubscribe_key;
 248              $this->gender = $db_row->user_gender;
 249              $this->avatar_file_ID = $db_row->user_avatar_file_ID;
 250              $this->reg_ctry_ID = $db_row->user_reg_ctry_ID;
 251              $this->ctry_ID = $db_row->user_ctry_ID;
 252              $this->rgn_ID = $db_row->user_rgn_ID;
 253              $this->subrg_ID = $db_row->user_subrg_ID;
 254              $this->city_ID = $db_row->user_city_ID;
 255              $this->source = $db_row->user_source;
 256              $this->profileupdate_date = $db_row->user_profileupdate_date;
 257  
 258              // Group for this user:
 259              $this->grp_ID = $db_row->user_grp_ID;
 260          }
 261      }
 262  
 263  
 264      /**
 265       * Load data from Request form fields.
 266       *
 267       * @return boolean true if loaded data seems valid.
 268       */
 269  	function load_from_Request()
 270      {
 271          global $DB, $Settings, $UserSettings, $GroupCache, $Messages, $action;
 272          global $current_User, $Session, $localtimenow;
 273  
 274          $is_new_user = ( $this->ID == 0 );
 275  
 276          // ---- Login checking / START ----
 277          $edited_user_login = param( 'edited_user_login', 'string' );
 278          if( empty( $edited_user_login ) )
 279          {    // Empty login
 280              param_error( 'edited_user_login', T_('Please enter your login.') );
 281          }
 282          param_check_valid_login( 'edited_user_login' );
 283  
 284          $UserCache = & get_UserCache();
 285          $UserLogin = $UserCache->get_by_login( $edited_user_login );
 286          if( $UserLogin && $UserLogin->ID != $this->ID )
 287          {    // The login is already registered
 288              $login_error_message = T_( 'This login already exists.' );
 289              if( $current_User->check_perm( 'users', 'edit' ) )
 290              {
 291                  $login_error_message = sprintf( T_( 'This login &laquo;%s&raquo; already exists. Do you want to <a %s>edit the existing user</a>?' ),
 292                      $edited_user_login,
 293                      'href="'.get_user_settings_url( 'profile', $UserLogin->ID ).'"' );
 294              }
 295              param_error( 'edited_user_login', $login_error_message );
 296          }
 297  
 298          if( !param_has_error( 'edited_user_login' ) )
 299          {    // We want all logins to be lowercase to guarantee uniqueness regardless of the database case handling for UNIQUE indexes:
 300              $this->set_from_Request( 'login', 'edited_user_login', true, 'evo_strtolower' );
 301          }
 302          // ---- Login checking / END ----
 303  
 304          $is_identity_form = param( 'identity_form', 'boolean', false );
 305          $is_admin_form = param( 'admin_form', 'boolean', false );
 306          $has_full_access = $current_User->check_perm( 'users', 'edit' );
 307  
 308          // ******* Admin form or new user create ******* //
 309          // In both cases current user must have users edit permission!
 310          if( ( $is_admin_form || ( $is_identity_form && $is_new_user ) ) && $current_User->check_perm( 'users', 'edit', true ) )
 311          { // level/group and email options are displayed on identity form only when creating a new user.
 312              if( $this->ID != 1 )
 313              { // the admin user group can't be changed
 314                  param_integer_range( 'edited_user_level', 0, 10, T_('User level must be between %d and %d.') );
 315                  $this->set_from_Request( 'level', 'edited_user_level', true );
 316  
 317                  $edited_user_Group = $GroupCache->get_by_ID( param( 'edited_user_grp_ID', 'integer' ) );
 318                  $this->set_Group( $edited_user_Group );
 319              }
 320  
 321              param( 'edited_user_source', 'string', true );
 322              $this->set_from_Request('source', 'edited_user_source', true);
 323  
 324              // set email, without changing the user status
 325              $edited_user_email = param( 'edited_user_email', 'string', true );
 326              param_check_not_empty( 'edited_user_email', T_('Please enter your e-mail address.') );
 327              param_check_email( 'edited_user_email', true );
 328              $this->set_email( $edited_user_email, false );
 329  
 330              if( $is_admin_form )
 331              {    // Admin form
 332                  $notification_sender_email = param( 'notification_sender_email', 'string', true );
 333                  param_check_email( 'notification_sender_email' );
 334                  if( ! empty( $notification_sender_email ) )
 335                  { // Change a value of setting
 336                      $UserSettings->set( 'notification_sender_email', $notification_sender_email, $this->ID );
 337                  }
 338                  elseif( $UserSettings->get( 'notification_sender_email' , $this->ID ) != '' )
 339                  { // Delete a setting record from DB
 340                      $UserSettings->delete( 'notification_sender_email', $this->ID );
 341                  }
 342  
 343                  $notification_sender_name = param( 'notification_sender_name', 'string', true );
 344                  if( ! empty( $notification_sender_name ) )
 345                  { // Change a value of setting
 346                      $UserSettings->set( 'notification_sender_name', $notification_sender_name, $this->ID );
 347                  }
 348                  elseif( $UserSettings->get( 'notification_sender_name' , $this->ID ) != '' )
 349                  { // Delete a setting record from DB
 350                      $UserSettings->delete( 'notification_sender_name', $this->ID );
 351                  }
 352  
 353                  if( !isset( $this->dbchanges['user_email'] ) )
 354                  {    // If email address is not changed
 355                      // Update status of email address in the T_email_blocked table
 356                      $edited_email_status = param( 'edited_email_status', 'string' );
 357                      load_class( 'tools/model/_emailblocked.class.php', 'EmailBlocked' );
 358                      $EmailBlockedCache = & get_EmailBlockedCache();
 359                      $EmailBlocked = & $EmailBlockedCache->get_by_name( $this->get( 'email' ), false, false );
 360                      if( !$EmailBlocked && $edited_email_status != 'unknown' )
 361                      {    // Create new record in the T_email_blocked table
 362                          $EmailBlocked = new EmailBlocked();
 363                          $EmailBlocked->set( 'address', $this->get( 'email' ) );
 364                      }
 365                      if( !empty( $EmailBlocked ) )
 366                      {    // Save status of an email address
 367                          $EmailBlocked->set( 'status', $edited_email_status );
 368                          $EmailBlocked->dbsave();
 369                      }
 370                  }
 371  
 372                  // Update status of IP range in DB
 373                  $edited_iprange_status = param( 'edited_iprange_status', 'string' );
 374                  $IPRangeCache = & get_IPRangeCache();
 375                  $IPRange = & $IPRangeCache->get_by_ip( int2ip( $UserSettings->get( 'created_fromIPv4', $this->ID ) ) );
 376                  if( !$IPRange && !empty( $edited_iprange_status ) )
 377                  {    // IP range doesn't exist in DB, Create new record
 378                      $ip_24bit_start = ip2int( preg_replace( '#\.\d{1,3}$#i', '.0', int2ip( $UserSettings->get( 'created_fromIPv4', $this->ID ) ) ) );
 379                      $ip_24bit_end = ip2int( preg_replace( '#\.\d{1,3}$#i', '.255', int2ip( $UserSettings->get( 'created_fromIPv4', $this->ID ) ) ) );
 380                      $IPRange = new IPRange();
 381                      $IPRange->set( 'IPv4start', $ip_24bit_start );
 382                      $IPRange->set( 'IPv4end', $ip_24bit_end );
 383                      $IPRange->set( 'user_count', 1 );
 384                  }
 385                  if( $IPRange )
 386                  {    // Save status of IP range
 387                      if( $IPRange->get( 'status' ) != 'blocked' && $edited_iprange_status == 'blocked' )
 388                      {    // Status was changed to blocked, we should increase counter
 389                          $IPRange->set( 'block_count', $IPRange->block_count + 1 );
 390                      }
 391                      else if( $IPRange->get( 'status' ) == 'blocked' && $edited_iprange_status != 'blocked' )
 392                      {    // Status was changed from blocked to another, we should decrease counter
 393                          $IPRange->set( 'block_count', $IPRange->block_count - 1 );
 394                      }
 395                      $IPRange->set( 'status', $edited_iprange_status, true );
 396                      $IPRange->dbsave();
 397                  }
 398              }
 399          }
 400  
 401          // ******* Identity form ******* //
 402          if( $is_identity_form )
 403          {
 404              $can_edit_users = $current_User->check_perm( 'users', 'edit' );
 405              $edited_user_perms = array( 'edited-user', 'edited-user-required' );
 406  
 407              global $edited_user_age_min, $edited_user_age_max;
 408              param( 'edited_user_age_min', 'string', true );
 409              param( 'edited_user_age_max', 'string', true );
 410              param_check_interval( 'edited_user_age_min', 'edited_user_age_max', T_('Age must be a number.'), T_('The first age must be lower than (or equal to) the second.') );
 411              if( !param_has_error( 'edited_user_age_min' ) && $Settings->get( 'minimum_age' ) > 0 &&
 412                  !empty( $edited_user_age_min ) && $edited_user_age_min < $Settings->get( 'minimum_age' ) )
 413              {    // Limit user by minimum age
 414                  param_error( 'edited_user_age_min', sprintf( T_('You must be at least %d years old to use this service.'), $Settings->get( 'minimum_age' ) ) );
 415              }
 416              $this->set_from_Request( 'age_min', 'edited_user_age_min', true );
 417              $this->set_from_Request( 'age_max', 'edited_user_age_max', true );
 418  
 419              $firstname_editing = $Settings->get( 'firstname_editing' );
 420              if( ( in_array( $firstname_editing, $edited_user_perms ) && $this->ID == $current_User->ID ) || ( $firstname_editing != 'hidden' && $can_edit_users ) )
 421              {    // User has a permissions to save Firstname
 422                  param( 'edited_user_firstname', 'string', true );
 423                  if( $firstname_editing == 'edited-user-required' )
 424                  {    // First name is required
 425                      if( $can_edit_users )
 426                      {    // Display a note message if user can edit all users
 427                          param_add_message_to_Log( 'edited_user_firstname', T_('Please enter your first name.'), 'note' );
 428                      }
 429                      else
 430                      {    // Display an error message
 431                          param_check_not_empty( 'edited_user_firstname', T_('Please enter your first name.') );
 432                      }
 433                  }
 434                  $this->set_from_Request('firstname', 'edited_user_firstname', true);
 435              }
 436  
 437              $lastname_editing = $Settings->get( 'lastname_editing' );
 438              if( ( in_array( $lastname_editing, $edited_user_perms ) && $this->ID == $current_User->ID ) || ( $lastname_editing != 'hidden' && $can_edit_users ) )
 439              {    // User has a permissions to save Lastname
 440                  param( 'edited_user_lastname', 'string', true );
 441                  if( $lastname_editing == 'edited-user-required' )
 442                  {    // Last name is required
 443                      if( $can_edit_users )
 444                      {    // Display a note message if user can edit all users
 445                          param_add_message_to_Log( 'edited_user_lastname', T_('Please enter last name.'), 'note' );
 446                      }
 447                      else
 448                      {    // Display an error message
 449                          param_check_not_empty( 'edited_user_lastname', T_('Please enter last name.') );
 450                      }
 451                  }
 452                  $this->set_from_Request('lastname', 'edited_user_lastname', true);
 453              }
 454  
 455              $nickname_editing = $Settings->get( 'nickname_editing' );
 456              if( ( in_array( $nickname_editing, $edited_user_perms ) && $this->ID == $current_User->ID ) || ( $nickname_editing != 'hidden' && $can_edit_users ) )
 457              {    // User has a permissions to save Nickname
 458                  param( 'edited_user_nickname', 'string', true );
 459                  if( $nickname_editing == 'edited-user-required' )
 460                  {    // Nickname is required
 461                      if( $can_edit_users )
 462                      {    // Display a note message if user can edit all users
 463                          param_add_message_to_Log( 'edited_user_nickname', T_('Please enter your nickname.'), 'note' );
 464                      }
 465                      else
 466                      {    // Display an error message
 467                          param_check_not_empty( 'edited_user_nickname', T_('Please enter your nickname.') );
 468                      }
 469                  }
 470                  $this->set_from_Request('nickname', 'edited_user_nickname', true);
 471              }
 472  
 473              param( 'edited_user_gender', 'string', '' );
 474              if( param_check_gender( 'edited_user_gender', $Settings->get( 'registration_require_gender' ) == 'required' ) )
 475              {
 476                  $this->set_from_Request('gender', 'edited_user_gender', true);
 477              }
 478  
 479              // ---- Locations / START ----
 480              load_funcs( 'regional/model/_regional.funcs.php' );
 481  
 482              if( user_country_visible() )
 483              {    // Save country
 484                  $country_ID = param( 'edited_user_ctry_ID', 'integer', true );
 485                  $country_is_required = ( $Settings->get( 'location_country' ) == 'required' && countries_exist() );
 486                  if( $country_is_required && $can_edit_users && $country_ID == 0 )
 487                  {    // Display a note message if user can edit all users
 488                      param_add_message_to_Log( 'edited_user_ctry_ID', T_('Please select a country.'), 'note' );
 489                  }
 490                  else
 491                  {    // Display an error message
 492                      param_check_number( 'edited_user_ctry_ID', T_('Please select a country.'), $country_is_required );
 493                  }
 494                  $this->set_from_Request('ctry_ID', 'edited_user_ctry_ID', true);
 495              }
 496  
 497              if( user_region_visible() && isset( $country_ID ) && $country_ID > 0 )
 498              {    // Save region
 499                  $region_ID = param( 'edited_user_rgn_ID', 'integer', true );
 500                  $region_is_required = ( $Settings->get( 'location_region' ) == 'required' && regions_exist( $country_ID ) );
 501                  if( $region_is_required && $can_edit_users && $region_ID == 0 )
 502                  {    // Display a note message if user can edit all users
 503                      param_add_message_to_Log( 'edited_user_rgn_ID', T_('Please select a region.'), 'note' );
 504                  }
 505                  else
 506                  {    // Display an error message
 507                      param_check_number( 'edited_user_rgn_ID', T_('Please select a region'), $region_is_required );
 508                  }
 509                  $this->set_from_Request('rgn_ID', 'edited_user_rgn_ID', true);
 510              }
 511  
 512              if( user_subregion_visible() && isset( $region_ID ) && $region_ID > 0 )
 513              {    // Save subregion
 514                  $subregion_ID = param( 'edited_user_subrg_ID', 'integer', true );
 515                  $subregion_is_required = ( $Settings->get( 'location_subregion' ) == 'required' && subregions_exist( $region_ID ) );
 516                  if( $subregion_is_required && $can_edit_users && $subregion_ID == 0 )
 517                  {    // Display a note message if user can edit all users
 518                      param_add_message_to_Log( 'edited_user_subrg_ID', T_('Please select a sub-region.'), 'note' );
 519                  }
 520                  else
 521                  {    // Display an error message
 522                      param_check_number( 'edited_user_subrg_ID', T_('Please select a sub-region.'), $subregion_is_required );
 523                  }
 524                  $this->set_from_Request('subrg_ID', 'edited_user_subrg_ID', true);
 525              }
 526  
 527              if( user_city_visible() && isset( $region_ID ) && $region_ID > 0 )
 528              {    // Save city
 529                  $city_ID = param( 'edited_user_city_ID', 'integer', true );
 530                  $city_is_required = ( $Settings->get( 'location_city' ) == 'required' && cities_exist( $country_ID, $region_ID, $subregion_ID ) );
 531                  if( $city_is_required && $can_edit_users && $city_ID == 0 )
 532                  {    // Display a note message if user can edit all users
 533                      param_add_message_to_Log( 'edited_user_city_ID', T_('Please select a city.'), 'note' );
 534                  }
 535                  else
 536                  {    // Display an error message
 537                      param_check_number( 'edited_user_city_ID', T_('Please select a city.'), $city_is_required );
 538                  }
 539                  $this->set_from_Request('city_ID', 'edited_user_city_ID', true);
 540              }
 541              // ---- Locations / END ----
 542  
 543  
 544              // ---- Additional Fields / START ----
 545  
 546              // Load all defined userfields for following checking of required fields
 547              $this->userfield_defs_load();
 548  
 549              // EXPERIMENTAL user fields & EXISTING fields:
 550              // Get indices of existing userfields:
 551              $userfield_IDs = $DB->get_results( '
 552                          SELECT uf_ID, uf_ufdf_ID
 553                              FROM T_users__fields
 554                           WHERE uf_user_ID = '.$this->ID );
 555  
 556              foreach( $userfield_IDs as $userfield )
 557              {
 558                  $field_type = ( $this->userfield_defs[$userfield->uf_ufdf_ID][0] == 'text' ) ? 'text' : 'string';
 559                  $uf_val = param( 'uf_'.$userfield->uf_ID, $field_type, '' );
 560  
 561                  if( $this->userfield_defs[$userfield->uf_ufdf_ID][0] == 'list' && $uf_val == '---' )
 562                  {    // Option list has a value '---' for empty value
 563                      $uf_val = '';
 564                  }
 565  
 566                  $uf_val = trim( strip_tags( $uf_val ) );
 567                  if( empty( $uf_val ) && $this->userfield_defs[$userfield->uf_ufdf_ID][2] == 'require' )
 568                  {    // Display error for empty required field
 569                      if( $current_User->check_perm( 'users', 'edit' ) )
 570                      {    // Display a note message if user can edit all users
 571                          param_add_message_to_Log( 'uf_'.$userfield->uf_ID, sprintf( T_('Please enter a value for the field "%s".'), $this->userfield_defs[$userfield->uf_ufdf_ID][1] ), 'note' );
 572                      }
 573                      else
 574                      {    // Display an error message
 575                          param_error( 'uf_'.$userfield->uf_ID, T_('Please enter a value.') );
 576                      }
 577                  }
 578                  else
 579                  {    // Update field
 580                      if( $this->userfield_defs[$userfield->uf_ufdf_ID][0] == 'url' )
 581                      {    // Check url fields
 582                          param_check_url( 'uf_'.$userfield->uf_ID, 'commenting' );
 583                      }
 584                      if( $this->userfield_defs[$userfield->uf_ufdf_ID][4] == 'list' )
 585                      {    // Option "Multiple values" == "List style"
 586                          // Split by comma and save each phrase as separate field
 587                          $uf_val = explode( ',', $uf_val );
 588                          foreach( $uf_val as $v => $val )
 589                          {
 590                              $val = trim( $val );
 591                              if( $v == 0 )
 592                              {    // Update field with first value
 593                                  $this->userfield_update( $userfield->uf_ID, $val );
 594                              }
 595                              else if( !empty( $val ) )
 596                              {    // Add a new field for new values
 597                                  $this->userfield_add( $userfield->uf_ufdf_ID, $val );
 598                              }
 599                          }
 600                      }
 601                      else
 602                      {    // Forbidden & Allowed fields
 603                          $this->userfield_update( $userfield->uf_ID, $uf_val );
 604                      }
 605                  }
 606              }
 607  
 608              // Duplicate fields:
 609              if( $is_new_user )
 610              {
 611                  $user_id = param( 'orig_user_ID', 'integer', 0 );
 612                  if( $user_id !== 0 )
 613                  {
 614                      $userfield_IDs = $DB->get_results( '
 615                                  SELECT uf_ID, uf_ufdf_ID
 616                                      FROM T_users__fields
 617                                   WHERE uf_user_ID = '.$user_id );
 618                      foreach( $userfield_IDs as $userfield_ID )
 619                      {
 620                          $uf_val = param( 'uf_'.$userfield_ID->uf_ID, 'string', '' );
 621                          $uf_type = $userfield_ID->uf_ufdf_ID;
 622                          if( !empty($uf_val) )
 623                          {
 624                              $this->userfield_add( $uf_type, $uf_val );
 625                          }
 626                      }
 627                  }
 628              }
 629  
 630              $uf_new_fields = param( 'uf_new', 'array/array/string' );    // Recommended & required fields (it still not saved in DB)
 631              $uf_add_fields = param( 'uf_add', 'array/array/string' );    // Added fields
 632  
 633              // Add a new field: (JS is not enabled)
 634              if( $action == 'add_field' )
 635              {    // Button 'Add' new field is pressed
 636                  $new_field_type = param( 'new_field_type', 'integer', 0 );
 637                  if( empty( $new_field_type ) )
 638                  {    // We cannot add a new field without type
 639                      param_error( 'new_field_type', T_('Please select a field type.') );
 640                  }
 641                  else
 642                  {    // Save an adding field(in the array) to display it again if errors will be exist
 643                      $new_field_type_exists = false;
 644  
 645                      if( $this->userfield_defs[$new_field_type][4] == 'allowed' || $this->userfield_defs[$new_field_type][4] == 'list' )
 646                      {    // This field can be duplicated
 647                          global $add_field_types;
 648                          $add_field_types = array( $new_field_type );
 649                      }
 650                      else
 651                      {    // We should to solve we can add this field or don't
 652                          if( ! isset( $uf_new_fields[$new_field_type] ) && ! isset( $uf_add_fields[$new_field_type] ) )
 653                          {    // User is adding this field first time
 654                              if( is_array( $userfield_IDs ) && count( $userfield_IDs ) > 0 )
 655                              {    // User has fields that saved in DB
 656                                  foreach( $userfield_IDs as $userfield )
 657                                  {
 658                                      if( $userfield->uf_ufdf_ID == $new_field_type )
 659                                      {    // New adding field already exists for current user in DB
 660                                          $new_field_type_exists = true;
 661                                          break;
 662                                      }
 663                                  }
 664                              }
 665                              if( ! $new_field_type_exists )
 666                              {    // Field doesn't still exist for current user
 667                                  global $add_field_types;
 668                                  $add_field_types = array( $new_field_type );
 669                              }
 670                          }
 671                          else
 672                          {    // Field exists, no duplicates available
 673                              $new_field_type_exists = true;
 674                          }
 675  
 676                          if( $new_field_type_exists )
 677                          {    // Field already is added for current user, we should display error about this
 678                              param_error( 'new_field_type', T_('You already added this field, please select another.') );
 679                          }
 680                      }
 681  
 682                      if( ! $new_field_type_exists )
 683                      {    // Mark a new field to enter a value
 684                          param_error( 'uf_add['.$new_field_type.'][]', T_('Please enter a value in this new field.') );
 685                      }
 686                  }
 687              }
 688  
 689              // Save a New recommended & require fields AND Adding fields
 690              if( count( $uf_new_fields ) > 0 || count( $uf_add_fields ) > 0 )
 691              {
 692                  $uf_fields = array(
 693                      'new' => $uf_new_fields,
 694                      'add' => $uf_add_fields
 695                  );
 696                  foreach( $uf_fields as $uf_type => $uf_new_fields )
 697                  {
 698                      if( $uf_type == 'add' )
 699                      {    // Save an adding fields to display it again if errors will be exist
 700                          global $add_field_types;
 701                          if( ! isset( $add_field_types ) )
 702                          {    // Don't rewrite already existing array
 703                              $add_field_types = array();
 704                          }
 705                      }
 706                      foreach( $uf_new_fields as $uf_new_id => $uf_new_vals )
 707                      {
 708                          foreach( $uf_new_vals as $uf_new_val )
 709                          {
 710                              if( $this->userfield_defs[$uf_new_id][0] == 'list' && $uf_new_val == '---' )
 711                              {    // Option list has a value '---' for empty value
 712                                  $uf_new_val = '';
 713                              }
 714  
 715                              $uf_new_val = trim( strip_tags( $uf_new_val ) );
 716                              if( $uf_new_val != '' )
 717                              {    // Insert a new field in DB if it is filled
 718                                  if( $this->userfield_defs[$uf_new_id][0] == 'url' )
 719                                  {    // Check url fields
 720                                      param_check_url( 'uf_'.$uf_type.'['.$uf_new_id.'][]', 'commenting' );
 721                                  }
 722                                  if( $this->userfield_defs[$uf_new_id][4] == 'list' )
 723                                  {    // Option "Multiple values" == "List style"
 724                                      // Split by comma and save each phrase as separate field
 725                                      $uf_new_val = explode( ',', $uf_new_val );
 726                                      foreach( $uf_new_val as $val )
 727                                      {
 728                                          $val = trim( $val );
 729                                          if( !empty( $val ) )
 730                                          {    // Exclude empty values(spaces)
 731                                              $this->userfield_add( (int)$uf_new_id, $val );
 732                                          }
 733                                      }
 734                                  }
 735                                  else
 736                                  {    // Forbidden & Allowed fields
 737                                      $this->userfield_add( (int)$uf_new_id, $uf_new_val );
 738                                  }
 739                              }
 740                              elseif( empty( $uf_new_val ) && $this->userfield_defs[$uf_new_id][2] == 'require' )
 741                              {    // Display error for empty required field & new adding field
 742                                  if( $current_User->check_perm( 'users', 'edit' ) )
 743                                  {    // Display a note message if user can edit all users
 744                                      param_add_message_to_Log( 'uf_'.$uf_type.'['.$uf_new_id.'][]', sprintf( T_('Please enter a value for the field "%s".'), $this->userfield_defs[$uf_new_id][1] ), 'note' );
 745                                  }
 746                                  else
 747                                  {    // Display an error message
 748                                      param_error( 'uf_'.$uf_type.'['.$uf_new_id.'][]', T_('Please enter a value.') );
 749                                  }
 750                              }
 751  
 752                              if( $uf_type == 'add' )
 753                              {    // Save new added field, it used on the _user_identity.form
 754                                  $add_field_types[] = $uf_new_id;
 755                              }
 756                          }
 757                      }
 758                  }
 759              }
 760              // ---- Additional Fields / END ----
 761  
 762              // update profileupdate_date, because a publicly visible user property was changed
 763              $this->set_profileupdate_date();
 764          }
 765  
 766          // ******* Password form ******* //
 767  
 768          $is_password_form = param( 'password_form', 'boolean', false );
 769          if( $is_password_form || $is_new_user )
 770          {
 771              $reqID = param( 'reqID', 'string', '' );
 772  
 773              if( $is_new_user || ( $has_full_access && $this->ID != $current_User->ID ) || ( !empty( $reqID ) && $reqID == $Session->get( 'core.changepwd.request_id' ) ) )
 774              {    // current password is not required:
 775                  //   - new user creating process
 776                  //   - current user has full access and not editing his own pasword
 777                  //   - password change requested by email
 778                  param( 'edited_user_pass1', 'string', true );
 779                  $edited_user_pass2 = param( 'edited_user_pass2', 'string', true );
 780  
 781                  if( param_check_passwords( 'edited_user_pass1', 'edited_user_pass2', true, $Settings->get('user_minpwdlen') ) )
 782                  {     // We can set password
 783                      $this->set( 'pass', md5( $edited_user_pass2 ) );
 784                  }
 785              }
 786              else
 787              {
 788                  // ******* Password edit form ****** //
 789                  param( 'edited_user_pass1', 'string', true );
 790                  $edited_user_pass2 = param( 'edited_user_pass2', 'string', true );
 791  
 792                  $current_user_pass = param( 'current_user_pass', 'string', true );
 793  
 794                  if( ! strlen($current_user_pass) )
 795                  {
 796                      param_error('current_user_pass' , T_('Please enter your current password.') );
 797                      param_check_passwords( 'edited_user_pass1', 'edited_user_pass2', true, $Settings->get('user_minpwdlen') );
 798                  }
 799                  else
 800                  {
 801  
 802                      if( $this->pass == md5($current_user_pass) )
 803                      {
 804                          if( param_check_passwords( 'edited_user_pass1', 'edited_user_pass2', true, $Settings->get('user_minpwdlen') ) )
 805                          { // We can set password
 806                              $this->set( 'pass', md5( $edited_user_pass2 ) );
 807                          }
 808                      }
 809                      else
 810                      {
 811                          param_error('current_user_pass' , T_('Your current password is incorrect.') );
 812                          param_check_passwords( 'edited_user_pass1', 'edited_user_pass2', true, $Settings->get('user_minpwdlen') );
 813                      }
 814                  }
 815  
 816              }
 817          }
 818  
 819  
 820          // Used in Preferences & Notifications forms
 821          $has_messaging_perm = $this->check_perm( 'perm_messaging', 'reply' );
 822  
 823          // ******* Preferences form ******* //
 824  
 825          $is_preferences_form = param( 'preferences_form', 'boolean', false );
 826  
 827          if( $is_preferences_form )
 828          {
 829              // Other preferences
 830              param( 'edited_user_locale', 'string', true );
 831              $this->set_from_Request('locale', 'edited_user_locale', true);
 832  
 833              // Session timeout
 834              $edited_user_timeout_sessions = param( 'edited_user_timeout_sessions', 'string', NULL );
 835              if( isset( $edited_user_timeout_sessions ) && ( $current_User->ID == $this->ID  || $current_User->check_perm( 'users', 'edit' ) ) )
 836              {
 837                  switch( $edited_user_timeout_sessions )
 838                  {
 839                      case 'default':
 840                          $UserSettings->set( 'timeout_sessions', NULL, $this->ID );
 841                          break;
 842                      case 'custom':
 843                          $UserSettings->set( 'timeout_sessions', param_duration( 'timeout_sessions' ), $this->ID );
 844                          break;
 845                  }
 846              }
 847  
 848              $UserSettings->set( 'show_online', param( 'edited_user_showonline', 'integer', 0 ), $this->ID );
 849          }
 850  
 851          // ******* Notifications form ******* //
 852          $is_subscriptions_form = param( 'subscriptions_form', 'boolean', false );
 853  
 854          if( $is_subscriptions_form )
 855          {
 856              if( $action == 'subscribe' )
 857              { // Do only subscribe to new blog (Don't update the user's settings from the same form)
 858  
 859                  // A selected blog to subscribe
 860                  $subscribe_blog_ID = param( 'subscribe_blog', 'integer', 0 );
 861                  // Get checkbox values:
 862                  $sub_items    = param( 'sub_items_new',    'integer', 0 );
 863                  $sub_comments = param( 'sub_comments_new', 'integer', 0 );
 864  
 865                  // Note: we do not check if subscriptions are allowed here, but we check at the time we're about to send something
 866                  if( $subscribe_blog_ID && ( $sub_items || $sub_comments ) )
 867                  { // We need to record values:
 868                      $DB->query( 'REPLACE INTO T_subscriptions( sub_coll_ID, sub_user_ID, sub_items, sub_comments )
 869                        VALUES ( '.$DB->quote( $subscribe_blog_ID ).', '.$DB->quote( $this->ID ).', '.$DB->quote( $sub_items ).', '.$DB->quote( $sub_comments ).' )' );
 870  
 871                      $Messages->add( T_('Subscriptions have been changed.'), 'success' );
 872                  }
 873                  else
 874                  { // Display an error message to inform user about incorrect actions
 875                      $Messages->add( T_('Please select at least one setting to subscribe on the selected blog.'), 'error' );
 876                  }
 877              }
 878              else
 879              { // Update user's settings
 880  
 881                  // Email communication
 882                  $edited_user_email = param( 'edited_user_email', 'string', true );
 883                  param_check_not_empty( 'edited_user_email', T_('Please enter your e-mail address.') );
 884                  param_check_email( 'edited_user_email', true );
 885                  $this->set_email( $edited_user_email );
 886  
 887                  // set messaging options
 888                  if( $has_messaging_perm )
 889                  {
 890                      $UserSettings->set( 'enable_PM', param( 'PM', 'integer', 0 ), $this->ID );
 891                  }
 892                  $emails_msgform = $Settings->get( 'emails_msgform' );
 893                  if( ( $emails_msgform == 'userset' ) || ( ( $emails_msgform == 'adminset' ) && ( $current_User->check_perm( 'users', 'edit' ) ) ) )
 894                  { // enable email option is displayed only if user can set or if admin can set and current User is an administrator
 895                      $UserSettings->set( 'enable_email', param( 'email', 'integer', 0 ), $this->ID );
 896                  }
 897  
 898                  // Email format
 899                  $UserSettings->set( 'email_format', param( 'edited_user_email_format', 'string', 'auto' ), $this->ID );
 900  
 901                  // set notification options
 902                  if( $has_messaging_perm )
 903                  { // update 'notify messages' only if user has messaging rights and this option was displayed
 904                      $UserSettings->set( 'notify_messages', param( 'edited_user_notify_messages', 'integer', 0 ), $this->ID );
 905                      $UserSettings->set( 'notify_unread_messages', param( 'edited_user_notify_unread_messages', 'integer', 0 ), $this->ID );
 906                  }
 907                  if( $this->check_role( 'post_owner' ) )
 908                  { // update 'notify_published_comments' only if user has at least one post or user has right to create new post
 909                      $UserSettings->set( 'notify_published_comments', param( 'edited_user_notify_publ_comments', 'integer', 0 ), $this->ID );
 910                  }
 911                  $is_comment_moderator = $this->check_role( 'comment_moderator' );
 912                  if( $is_comment_moderator || $this->check_role( 'comment_editor' ) )
 913                  { // update 'notify_comment_moderation' only if user is comment moderator/editor at least in one blog
 914                      $UserSettings->set( 'notify_comment_moderation', param( 'edited_user_notify_cmt_moderation', 'integer', 0 ), $this->ID );
 915                  }
 916                  if( $is_comment_moderator )
 917                  { // update 'send_cmt_moderation_reminder' only if user is comment moderator at least in one blog
 918                      $UserSettings->set( 'send_cmt_moderation_reminder', param( 'edited_user_send_cmt_moderation_reminder', 'integer', 0 ), $this->ID );
 919                  }
 920                  if( $this->check_role( 'post_moderator' ) )
 921                  { // update 'notify_post_moderation' only if user is post moderator at least in one blog
 922                      $UserSettings->set( 'notify_post_moderation', param( 'edited_user_notify_post_moderation', 'integer', 0 ), $this->ID );
 923                  }
 924                  if( $this->grp_ID == 1 )
 925                  {
 926                      $UserSettings->set( 'send_activation_reminder', param( 'edited_user_send_activation_reminder', 'integer', 0 ), $this->ID );
 927                  }
 928  
 929                  if( $this->check_perm( 'users', 'edit' ) )
 930                  { // edited user has permission to edit all users, save notification preferences
 931                      $UserSettings->set( 'notify_new_user_registration', param( 'edited_user_notify_new_user_registration', 'integer', 0 ), $this->ID );
 932                      $UserSettings->set( 'notify_activated_account', param( 'edited_user_notify_activated_account', 'integer', 0 ), $this->ID );
 933                      $UserSettings->set( 'notify_closed_account', param( 'edited_user_notify_closed_account', 'integer', 0 ), $this->ID );
 934                      $UserSettings->set( 'notify_reported_account', param( 'edited_user_notify_reported_account', 'integer', 0 ), $this->ID );
 935                  }
 936  
 937                  if( $this->check_perm( 'options', 'edit' ) )
 938                  { // edited user has permission to edit options, save notification preferences
 939                      $UserSettings->set( 'notify_cronjob_error', param( 'edited_user_notify_cronjob_error', 'integer', 0 ), $this->ID );
 940                  }
 941  
 942                  // Newsletter
 943                  $UserSettings->set( 'newsletter_news', param( 'edited_user_newsletter_news', 'integer', 0 ), $this->ID );
 944                  $UserSettings->set( 'newsletter_ads', param( 'edited_user_newsletter_ads', 'integer', 0 ), $this->ID );
 945  
 946                  // Emails limit per day
 947                  param_integer_range( 'edited_user_notification_email_limit', 0, 999, T_('Notificaiton email limit must be between %d and %d.') );
 948                  $UserSettings->set( 'notification_email_limit', param( 'edited_user_notification_email_limit', 'integer', 0 ), $this->ID );
 949                  param_integer_range( 'edited_user_newsletter_limit', 0, 999, T_('Newsletter limit must be between %d and %d.') );
 950                  $UserSettings->set( 'newsletter_limit', param( 'edited_user_newsletter_limit', 'integer', 0 ), $this->ID );
 951  
 952                  /**
 953                   * Update the subscriptions:
 954                   */
 955                  $subs_blog_IDs = param( 'subs_blog_IDs', 'string', true );
 956                  $subs_item_IDs = param( 'subs_item_IDs', 'string', true );
 957  
 958                  // Work the blogs:
 959                  $subscription_values = array();
 960                  $unsubscribed = array();
 961                  $subs_blog_IDs = explode( ',', $subs_blog_IDs );
 962                  foreach( $subs_blog_IDs as $loop_blog_ID )
 963                  {
 964                      // Make sure no dirty hack is coming in here:
 965                      $loop_blog_ID = intval( $loop_blog_ID );
 966  
 967                      // Get checkbox values:
 968                      $sub_items    = param( 'sub_items_'.$loop_blog_ID,    'integer', 0 );
 969                      $sub_comments = param( 'sub_comments_'.$loop_blog_ID, 'integer', 0 );
 970  
 971                      if( $sub_items || $sub_comments )
 972                      {    // We have a subscription for this blog
 973                          $subscription_values[] = "( $loop_blog_ID, $this->ID, $sub_items, $sub_comments )";
 974                      }
 975                      else
 976                      {    // No subscription here:
 977                          $unsubscribed[] = $loop_blog_ID;
 978                      }
 979                  }
 980  
 981                  // Note: we do not check if subscriptions are allowed here, but we check at the time we're about to send something
 982                  if( count( $subscription_values ) )
 983                  {    // We need to record values:
 984                      $DB->query( 'REPLACE INTO T_subscriptions( sub_coll_ID, sub_user_ID, sub_items, sub_comments )
 985                                                  VALUES '.implode( ', ', $subscription_values ) );
 986                  }
 987  
 988                  if( count( $unsubscribed ) )
 989                  {    // We need to make sure some values are cleared:
 990                      $DB->query( 'DELETE FROM T_subscriptions
 991                                                   WHERE sub_user_ID = '.$this->ID.'
 992                                                       AND sub_coll_ID IN ('.implode( ', ', $unsubscribed ).')' );
 993                  }
 994  
 995                  // Individual post subscriptions
 996                  if( !empty( $subs_item_IDs ) )
 997                  { // user was subscribed to at least one post update notification
 998                      $subs_item_IDs = explode( ',', $subs_item_IDs );
 999                      $unsubscribed = array();
1000                      foreach( $subs_item_IDs as $loop_item_ID )
1001                      {
1002                          if( !param( 'item_sub_'.$loop_item_ID, 'integer', 0 ) )
1003                          { // user wants to unsubscribe from this post notifications
1004                              $unsubscribed[] = $loop_item_ID;
1005                          }
1006                      }
1007                      if( !empty( $unsubscribed ) )
1008                      { // unsubscribe list is not empty, delete not wanted subscriptions
1009                          $DB->query( 'DELETE FROM T_items__subscriptions
1010                                                   WHERE isub_user_ID = '.$this->ID.'
1011                                                       AND isub_item_ID IN ('.implode( ', ', $unsubscribed ).')' );
1012                      }
1013                  }
1014              }
1015          }
1016  
1017          // ******* Advanced form ******* //
1018          $is_advanced_form = param( 'advanced_form', 'boolean', false );
1019  
1020          if( $is_advanced_form )
1021          {
1022              $UserSettings->set( 'admin_skin', param( 'edited_user_admin_skin', 'string' ), $this->ID );
1023  
1024              // Action icon params:
1025              param_integer_range( 'edited_user_action_icon_threshold', 1, 5, T_('The threshold must be between 1 and 5.') );
1026              $UserSettings->set( 'action_icon_threshold', param( 'edited_user_action_icon_threshold', 'integer', true ), $this->ID );
1027  
1028              param_integer_range( 'edited_user_action_word_threshold', 1, 5, T_('The threshold must be between 1 and 5.') );
1029              $UserSettings->set( 'action_word_threshold', param( 'edited_user_action_word_threshold', 'integer'), $this->ID );
1030  
1031              $UserSettings->set( 'display_icon_legend', param( 'edited_user_legend', 'integer', 0 ), $this->ID );
1032  
1033              // Set bozo validador activation
1034              $UserSettings->set( 'control_form_abortions', param( 'edited_user_bozo', 'integer', 0 ), $this->ID );
1035  
1036              // Focus on first
1037              $UserSettings->set( 'focus_on_first_input', param( 'edited_user_focusonfirst', 'integer', 0 ), $this->ID );
1038  
1039              // Results per page
1040              $edited_user_results_page_size = param( 'edited_user_results_page_size', 'integer', NULL );
1041              if( isset( $edited_user_results_page_size ) )
1042              {
1043                  $UserSettings->set( 'results_per_page', $edited_user_results_page_size, $this->ID );
1044              }
1045          }
1046  
1047          if( $is_preferences_form || ( $is_identity_form && $is_new_user ) )
1048          {    // Multiple session
1049              $multiple_sessions = $Settings->get( 'multiple_sessions' );
1050              if( ( $multiple_sessions != 'adminset_default_no' && $multiple_sessions != 'adminset_default_yes' ) || $current_User->check_perm( 'users', 'edit' ) )
1051              {
1052                  $UserSettings->set( 'login_multiple_sessions', param( 'edited_user_set_login_multiple_sessions', 'integer', 0 ), $this->ID );
1053              }
1054          }
1055  
1056          return ! param_errors_detected();
1057      }
1058  
1059  
1060      /**
1061       * Get a param
1062       *
1063       * @param string the parameter
1064       */
1065  	function get( $parname )
1066      {
1067          if( $this->check_status( 'is_closed' ) && ( !is_admin_page() ) && ( $parname != 'login' ) && ( $parname != 'status' ) )
1068          { // if this account is closed and we are not in backoffice, don't return other information then login or status
1069              return NULL;
1070          }
1071  
1072          switch( $parname )
1073          {
1074              case 'fullname':
1075                  return trim($this->firstname.' '.$this->lastname);
1076  
1077              case 'preferredname':
1078                  return $this->get_preferred_name();
1079  
1080              case 'num_posts':
1081                  return $this->get_num_posts();
1082  
1083              case 'num_comments':
1084                  return $this->get_num_comments();
1085  
1086              default:
1087              // All other params:
1088                  return parent::get( $parname );
1089          }
1090      }
1091  
1092  
1093      /**
1094       * Get the name of the account with complete details for admin select lists
1095       *
1096       * @return string
1097       */
1098  	function get_account_name()
1099      {
1100          if( $this->check_status( 'is_closed' ) && ( !is_admin_page() ) )
1101          { // don't return closed accounts information except login name
1102              return $this->login;
1103          }
1104  
1105          return $this->login.' - '.$this->firstname.' '.$this->lastname.' ('.$this->nickname.')';
1106      }
1107  
1108  
1109      /**
1110       * Get link to User
1111       *
1112       * @return string
1113       */
1114  	function get_link( $params = array() )
1115      {
1116          // Make sure we are not missing any param:
1117          $params = array_merge( array(
1118                  'format'       => 'htmlbody',
1119                  'link_to'      => 'userpage', // userurl userpage 'userurl>userpage'
1120                  'link_text'    => 'preferredname', // avatar | preferredname
1121                  'link_rel'     => '',
1122                  'link_class'   => '',
1123                  'thumb_size'   => 'crop-top-32x32',
1124                  'thumb_class'  => '',
1125              ), $params );
1126  
1127          if( $params['link_text'] == 'avatar' )
1128          {
1129              $r = $this->get_avatar_imgtag( $params['thumb_size'], $params['thumb_class'] );
1130          }
1131          else
1132          {
1133              $r = $this->dget( 'preferredname', $params['format'] );
1134          }
1135  
1136          switch( $params['link_to'] )
1137          {
1138              case 'userpage':
1139              case 'userpage>userurl':
1140                  $url = $this->get_userpage_url();
1141                  break;
1142  
1143              case 'userurl':
1144                  $url = $this->url;
1145                  break;
1146  
1147              case 'userurl>userpage':
1148                  // We give priority to user submitted url:
1149                  if( evo_strlen($this->url) > 10 )
1150                  {
1151                      $url = $this->url;
1152                  }
1153                  else
1154                  {
1155                      $url = $this->get_userpage_url();
1156                  }
1157                  break;
1158          }
1159  
1160          if( !empty($url) )
1161          {
1162              $link = '<a href="'.$url.'"';
1163              if( !empty($params['link_rel']) )
1164              {
1165                  $link .= ' rel="'.$params['link_rel'].'"';
1166              }
1167              if( !empty($params['link_class']) )
1168              {
1169                  $link .= ' class="'.$params['link_class'].'"';
1170              }
1171              $r = $link.'>'.$r.'</a>';
1172          }
1173  
1174          return $r;
1175      }
1176  
1177  
1178      /**
1179       * Get preferred name of the user
1180       *
1181       * @return string
1182       */
1183  	function get_preferred_name()
1184      {
1185          if( $this->check_status( 'is_closed' ) && ( !is_admin_page() ) )
1186          { // don't return closed accounts information except login name
1187              return $this->login;
1188          }
1189  
1190          if( !empty( $this->nickname ) )
1191          {    // Nickname
1192              return $this->get( 'nickname' );
1193          }
1194          elseif( !empty( $this->firstname ) || !empty( $this->lastname ) )
1195          {    // First name
1196              return $this->get('fullname');
1197          }
1198          else
1199          {    // Login
1200              return $this->get( 'login' );
1201          }
1202      }
1203  
1204  
1205      /**
1206       * Get user's login with gender color
1207       *
1208       * @param array Params
1209       * @return string User's preferred name with gender color if this available
1210       */
1211  	function get_colored_login( $params = array() )
1212      {
1213          // Make sure we are not missing any param:
1214          $params = array_merge( array(
1215                  'mask'         => '$login$', // $avatar$ $login$
1216                  'login_format' => 'htmlbody',
1217                  'avatar_size'  => 'crop-top-15x15',
1218              ), $params );
1219  
1220          $avatar = '';
1221          $login = '';
1222  
1223          if( strpos( $params['mask'], '$login$' ) !== false )
1224          {    // Display login
1225              $login = $this->dget( 'login', $params['login_format'] );
1226          }
1227  
1228          if( strpos( $params['mask'], '$avatar$' ) !== false )
1229          {    // Display avatar
1230              $avatar = $this->get_avatar_imgtag( $params['avatar_size'], '' );
1231          }
1232  
1233          $mask = array( '$login$', '$avatar$' );
1234          $data = array( $login, $avatar );
1235  
1236          return '<span class="'.$this->get_gender_class().'">'.str_replace( $mask, $data, $params['mask'] ).'</span>';
1237      }
1238  
1239  
1240      /**
1241       * Get User identity link, which is a composite of user avatar and login, both point to the specific user profile tab.
1242       *
1243       * @return string User avatar and login if the identity link is not available, the identity link otherwise.
1244       */
1245  	function get_identity_link( $params = array() )
1246      {
1247          // Make sure we are not missing any param:
1248          $params = array_merge( array(
1249                  'before'         => ' ',
1250                  'after'          => ' ',
1251                  'format'         => 'htmlbody',
1252                  'link_to'        => 'userpage',
1253                  'link_text'      => 'avatar', // avatar | only_avatar | login | nickname | firstname | lastname | fullname | preferredname
1254                  'link_rel'       => '',
1255                  'link_class'     => '',
1256                  'thumb_size'     => 'crop-top-15x15',
1257                  'thumb_class'    => 'avatar_before_login',
1258                  'thumb_zoomable' => false,
1259                  'login_mask'     => '', // example: 'text $login$ text'
1260                  'display_bubbletip' => true,
1261                  'nowrap'         => true,
1262              ), $params );
1263  
1264          $identity_url = get_user_identity_url( $this->ID );
1265  
1266          $attr_bubbletip = '';
1267          if( $params['display_bubbletip'] )
1268          {    // Set attribute to initialize a bubbletip
1269              $attr_bubbletip = ' rel="bubbletip_user_'.$this->ID.'"';
1270          }
1271  
1272          $avatar_tag = '';
1273          if( $params['link_text'] == 'avatar' || $params['link_text'] == 'only_avatar' )
1274          {
1275              $avatar_tag = $this->get_avatar_imgtag( $params['thumb_size'], $params['thumb_class'], '', $params['thumb_zoomable'] );
1276              if( $params['thumb_zoomable'] )
1277              { // User avatar is zoomable
1278                  // Add tag param to init bubbletip
1279                  $avatar_tag = str_replace( '<img ', '<img rel="bubbletip_user_'.$this->ID.'"', $avatar_tag );
1280                  return $avatar_tag; // We should exit here, to avoid the double adding of tag <a>
1281              }
1282          }
1283  
1284          $link_login = '';
1285          if( $params['link_text'] != 'only_avatar' )
1286          { // Display a login, nickname, firstname, lastname, fullname or preferredname
1287              switch( $params['link_text'] )
1288              {
1289                  case 'login':
1290                  case 'avatar':
1291                      $link_login = $this->login;
1292                      break;
1293                  case 'nickname':
1294                      $link_login = $this->nickname;
1295                      break;
1296                  case 'firstname':
1297                      $link_login = $this->firstname;
1298                      break;
1299                  case 'lastname':
1300                      $link_login = $this->lastname;
1301                      break;
1302                  case 'fullname':
1303                      $link_login = $this->firstname.' '.$this->lastname;
1304                      break;
1305                  case 'preferredname':
1306                      $link_login = $this->get_preferred_name();
1307                      break;
1308              }
1309              $link_login = trim( $link_login );
1310              if( empty( $link_login ) )
1311              { // Use a login by default if a selected field is empty
1312                  $link_login = $this->login;
1313              }
1314              if( $params['login_mask'] != '' )
1315              { // Apply login mask
1316                  $link_login = str_replace( '$login$', $link_login, $params['login_mask'] );
1317              }
1318          }
1319  
1320          if( empty( $identity_url ) )
1321          {
1322              return '<span class="'.$this->get_gender_class().'"'.$attr_bubbletip.'>'.$avatar_tag.$link_login.'</span>';
1323          }
1324  
1325          $link_title = T_( 'Show the user profile' );
1326          $link_text = '<span'.( $params['nowrap'] ? ' class="nowrap"' : '' ).'>'.$avatar_tag.$link_login.'</span>';
1327          return '<a href="'.$identity_url.'" title="'.$link_title.'" class="'.$this->get_gender_class().'"'.$attr_bubbletip.'>'.$link_text.'</a>';
1328      }
1329  
1330  
1331      /**
1332       * Get Regional object
1333       *
1334       * @param string Object name (Country, Region, Subregion, City)
1335       * @param string ID name
1336       * @return object
1337       */
1338      function & get_Regional( $Object, $ID )
1339      {
1340          if( $this->check_status( 'is_closed' ) && ( !is_admin_page() ) )
1341          { // don't return closed accounts regional information to front office
1342              return false;
1343          }
1344  
1345          if( is_null($this->$Object) && !empty($this->$ID ) )
1346          {
1347              $Cache = & call_user_func( 'get_'.$Object.'Cache' );
1348              $this->$Object = $Cache->get_by_ID( $this->$ID, false );
1349          }
1350  
1351          return $this->$Object;
1352      }
1353  
1354  
1355      /**
1356       * Get Regional name
1357       *
1358       * @param string Object name (Country, Region, Subregion, City)
1359       * @param string ID name
1360       * @return string Name of Country, Region, Subregion or City
1361       */
1362  	function get_regional_name( $Object, $ID )
1363      {
1364          if( $this->get_Regional( $Object, $ID ) )
1365          {    // We have an regional object:
1366              return $this->$Object->name;
1367          }
1368  
1369          return '';
1370      }
1371  
1372  
1373      /**
1374       * Get Country object
1375       */
1376      function & get_Country()
1377      {
1378          return $this->get_Regional( 'Country', 'ctry_ID' );
1379      }
1380  
1381  
1382      /**
1383       * Get country name
1384       */
1385  	function get_country_name()
1386      {
1387          if( empty( $this->ctry_ID ) )
1388          {
1389              return;
1390          }
1391  
1392          load_class( 'regional/model/_country.class.php', 'Country' );
1393          return $this->get_regional_name( 'Country', 'ctry_ID' );
1394      }
1395  
1396      /**
1397       * Get region name
1398       */
1399  	function get_region_name()
1400      {
1401          if( empty( $this->rgn_ID ) )
1402          {
1403              return;
1404          }
1405  
1406          load_class( 'regional/model/_region.class.php', 'Region' );
1407          return $this->get_regional_name( 'Region', 'rgn_ID' );
1408      }
1409  
1410      /**
1411       * Get subregion name
1412       */
1413  	function get_subregion_name()
1414      {
1415          if( empty( $this->subrg_ID ) )
1416          {
1417              return;
1418          }
1419  
1420          load_class( 'regional/model/_subregion.class.php', 'Subregion' );
1421          return $this->get_regional_name( 'Subregion', 'subrg_ID' );
1422      }
1423  
1424      /**
1425       * Get city name
1426       *
1427       * @param boolean TRUE - show postcode
1428       * @return string city name
1429       */
1430  	function get_city_name( $show_postcode = true )
1431      {
1432          if( empty( $this->city_ID ) )
1433          {
1434              return;
1435          }
1436  
1437          load_class( 'regional/model/_city.class.php', 'City' );
1438          $city = $this->get_regional_name( 'City', 'city_ID' );
1439          if( $this->City && $show_postcode )
1440          {    // Get postcode
1441              $city .= ' ('.$this->City->postcode.')';
1442          }
1443          return $city;
1444      }
1445  
1446  
1447      /**
1448       * Get the number of blogs owned by this user
1449       *
1450       * @return integer
1451       */
1452  	function get_num_blogs()
1453      {
1454          global $DB;
1455  
1456          return $DB->get_var( 'SELECT count(*)
1457                                  FROM T_blogs
1458                                  WHERE blog_owner_user_ID = '.$this->ID );
1459      }
1460  
1461  
1462      /**
1463       * Get the number of posts for the user.
1464       *
1465       * @param string Posts status
1466       * @return integer
1467       */
1468  	function get_num_posts( $status = '' )
1469      {
1470          global $DB;
1471          global $collections_Module;
1472  
1473          if( isset( $collections_Module ) && is_null( $this->_num_posts ) )
1474          {
1475              $SQL = new SQL( 'Get the number of posts for the user' );
1476              $SQL->SELECT( 'post_status, COUNT(*)' );
1477              $SQL->FROM( 'T_items__item' );
1478              $SQL->WHERE( 'post_creator_user_ID = '.$this->ID );
1479              $SQL->GROUP_BY( 'post_status' );
1480              $this->_num_posts = $DB->get_assoc( $SQL->get() );
1481  
1482              // Calc number of posts with all statuses
1483              $total_num_posts = 0;
1484              foreach( $this->_num_posts as $status_num_posts )
1485              {
1486                  $total_num_posts += $status_num_posts;
1487              }
1488              $this->_num_posts[''] = $total_num_posts;
1489          }
1490  
1491          return !empty( $this->_num_posts[ $status ] ) ? $this->_num_posts[ $status ] : 0;
1492      }
1493  
1494  
1495      /**
1496       * Get the number of comments for the user.
1497       *
1498       * @param string Comments status
1499       * @return integer
1500       */
1501  	function get_num_comments( $status = '' )
1502      {
1503          global $DB;
1504          global $collections_Module;
1505  
1506          if( isset( $collections_Module ) && is_null( $this->_num_comments ) )
1507          {
1508              $SQL = new SQL( 'Get the number of comments for the user' );
1509              $SQL->SELECT( 'comment_status, COUNT(*)' );
1510              $SQL->FROM( 'T_comments' );
1511              $SQL->WHERE( 'comment_author_ID = '.$this->ID );
1512              $SQL->GROUP_BY( 'comment_status' );
1513              $this->_num_comments = $DB->get_assoc( $SQL->get() );
1514  
1515              // Calc number of comments with all statuses
1516              $total_num_comments = 0;
1517              foreach( $this->_num_comments as $status_num_comments )
1518              {
1519                  $total_num_comments += $status_num_comments;
1520              }
1521              $this->_num_comments[''] = $total_num_comments;
1522          }
1523  
1524          return !empty( $this->_num_comments[ $status ] ) ? $this->_num_comments[ $status ] : 0;
1525      }
1526  
1527  
1528      /**
1529       * Get the number of user sessions
1530       *
1531       * @param boolean set true to return the number of sessions as a link to the user sessions list
1532       * @return integer|string number of sessions or link to user sessions where the link text is the number of sessions
1533       */
1534  	function get_num_sessions( $link_sessions = false )
1535      {
1536          global $DB;
1537  
1538          $num_sessions = $DB->get_var( 'SELECT count( sess_ID )
1539                                              FROM T_sessions
1540                                              WHERE sess_user_ID = '.$this->ID );
1541  
1542          if( $link_sessions && ( $num_sessions > 0 ) )
1543          {
1544              return $num_sessions.' - <a href="?ctrl=user&amp;user_ID='.$this->ID.'&amp;user_tab=sessions" class="roundbutton middle" title="'.format_to_output( T_('Go to user activity'), 'htmlattr' ).'">'.get_icon( 'magnifier', 'imgtag', array( 'title' => T_('Go to user activity') ) ).'</a>';
1545          }
1546  
1547          return $num_sessions;
1548      }
1549  
1550  
1551      /**
1552       * Get the number of user messages
1553       *
1554       * @param string Type ( sent | received )
1555       * @return integer the number of requested type of messages
1556       */
1557  	function get_num_messages( $type = 'sent' )
1558      {
1559          global $DB;
1560  
1561          if( $type == 'received' )
1562          {    // Get a count of messages received
1563              $SQL = new SQL();
1564              $SQL->SELECT( 'COUNT( msg_ID )' );
1565              $SQL->FROM( 'T_messaging__threadstatus' );
1566              $SQL->FROM_add( 'LEFT JOIN T_messaging__message ON tsta_thread_ID = msg_thread_ID' );
1567              $SQL->WHERE( 'tsta_user_ID = '.$DB->quote( $this->ID ) );
1568              $SQL->WHERE_and( 'msg_author_user_ID != '.$DB->quote( $this->ID ) );
1569          }
1570          else
1571          {    // Get a count of messages sent
1572              $SQL = new SQL();
1573              $SQL->SELECT( 'COUNT( msg_ID )' );
1574              $SQL->FROM( 'T_messaging__message' );
1575              $SQL->WHERE( 'msg_author_user_ID = '.$DB->quote( $this->ID ) );
1576          }
1577  
1578          return $DB->get_var( $SQL->get() );
1579      }
1580  
1581  
1582      /**
1583       * Get the number of other users posts which were edited by this user
1584       *
1585       * @return integer the number of edited posts
1586       */
1587  	function get_num_edited_posts()
1588      {
1589          global $DB;
1590          global $collections_Module;
1591  
1592          return $DB->get_var( 'SELECT COUNT( DISTINCT( post_ID ) )
1593                                      FROM T_items__item
1594                                      INNER JOIN T_items__version ON post_ID = iver_itm_ID
1595                                      WHERE post_creator_user_ID <> '.$this->ID.' AND
1596                                          ( iver_edit_user_ID = '.$this->ID.' OR post_lastedit_user_ID = '.$this->ID.' )' );
1597      }
1598  
1599  
1600      /**
1601       * Get the path to the media directory. If it does not exist, it will be created.
1602       *
1603       * If we're {@link is_admin_page() on an admin page}, it adds status messages.
1604       * @todo These status messages should rather go to a "syslog" and not be displayed to a normal user
1605       * @todo dh> refactor this into e.g. create_media_dir() and use it for Blog::get_media_dir, too.
1606       *
1607       * @param boolean Create the directory, if it does not exist yet?
1608       * @return mixed the path as string on success, false if the dir could not be created
1609       */
1610  	function get_media_dir( $create = true )
1611      {
1612          global $media_path, $Messages, $Settings, $Debuglog;
1613  
1614          if( ! $Settings->get( 'fm_enable_roots_user' ) )
1615          {    // User directories are disabled:
1616              $Debuglog->add( 'Attempt to access user media dir, but this feature is disabled', 'files' );
1617              return false;
1618          }
1619  
1620          $userdir = get_canonical_path( $media_path.$this->get_media_subpath() );
1621  
1622          if( $create && ! is_dir( $userdir ) )
1623          {
1624              if( ! is_writable( dirname($userdir) ) )
1625              { // add error
1626                  if( is_admin_page() )
1627                  {
1628                      $Messages->add( sprintf( T_("The user's media directory &laquo;%s&raquo; could not be created, because the parent directory is not writable or does not exist."), rel_path_to_base($userdir) )
1629                              .get_manual_link('directory_creation_error'), 'error' );
1630                  }
1631                  return false;
1632              }
1633              elseif( ! evo_mkdir( $userdir ) )
1634              { // add error
1635                  if( is_admin_page() )
1636                  {
1637                      $Messages->add( sprintf( T_("The user's media directory &laquo;%s&raquo; could not be created."), rel_path_to_base($userdir) )
1638                              .get_manual_link('directory_creation_error'), 'error' );
1639                  }
1640                  return false;
1641              }
1642              else
1643              { // add note:
1644                  if( is_admin_page() )
1645                  {
1646                      $Messages->add( sprintf( T_("The user's directory &laquo;%s&raquo; has been created with permissions %s."), rel_path_to_base($userdir), substr( sprintf('%o', fileperms($userdir)), -3 ) ), 'success' );
1647                  }
1648              }
1649          }
1650          return $userdir;
1651      }
1652  
1653  
1654      /**
1655       * Get the URL to the media folder
1656       *
1657       * @return string the URL
1658       */
1659  	function get_media_url()
1660      {
1661          global $media_url, $Settings, $Debuglog;
1662          global $Blog;
1663  
1664          if( ! $Settings->get( 'fm_enable_roots_user' ) )
1665          {    // User directories are disabled:
1666              $Debuglog->add( 'Attempt to access user media URL, but this feature is disabled', 'files' );
1667              return false;
1668          }
1669  
1670          if( isset($Blog) )
1671          {    // We are currently looking at a blog. We are going to consider (for now) that we want the users and their files
1672              // to appear as being part of that blog.
1673              return $Blog->get_local_media_url().$this->get_media_subpath();
1674          }
1675  
1676          // System media url:
1677          return $media_url.$this->get_media_subpath();
1678      }
1679  
1680  
1681      /**
1682       * Get user media directory subpath, e.g. users/{login}/ or users/usr_{user ID}/
1683       */
1684  	function get_media_subpath()
1685      {
1686          if( is_valid_login( $this->login, true ) )
1687          {    // Valid ASCII login, use it as is
1688              return 'users/'.$this->login.'/';
1689          }
1690          else
1691          {    // Non-ASCII login
1692              return 'users/usr_'.$this->ID.'/';
1693          }
1694      }
1695  
1696  
1697      /**
1698       * Get message form url
1699       */
1700  	function get_msgform_url( $formurl, $redirect_to = NULL )
1701      {
1702          global $ReqURI;
1703  
1704          if( ! $this->get_msgform_possibility() )
1705          {
1706              return NULL;
1707          }
1708  
1709          if( $redirect_to == NULL )
1710          {
1711              $redirect_to = $ReqURI;
1712          }
1713  
1714          return url_add_param( $formurl, 'recipient_id='.$this->ID.'&amp;redirect_to='.rawurlencode( $redirect_to ) );
1715      }
1716  
1717  
1718      /**
1719       * Get user page url
1720       */
1721  	function get_userpage_url()
1722      {
1723          /**
1724           * @var Blog
1725           */
1726          global $Blog;
1727  
1728          if( empty($Blog) )
1729          {
1730              return NULL;
1731          }
1732  
1733          $blogurl = $Blog->gen_blogurl();
1734  
1735          return url_add_param( $Blog->get('userurl'), 'user_ID='.$this->ID );
1736      }
1737  
1738  
1739      /**
1740       * Get url from defined userfields
1741       *
1742       * @param boolean TRUE if we want get a value from url fields of the table T_users__fields, FALSE - get from $this->url
1743       * @return string Url
1744       */
1745  	function get_field_url( $from_extra_fields = false )
1746      {
1747          global $DB;
1748  
1749          if( $from_extra_fields )
1750          {    // Get value from DB or from cache
1751              if( isset( $this->field_url ) )
1752              {    // Get url from variable already defined in first calling of this method
1753                  return $this->field_url;
1754              }
1755              else
1756              {    // Get value from DB
1757                  $this->field_url = (string)$DB->get_var( '
1758                      SELECT uf_varchar
1759                          FROM T_users__fields
1760                              LEFT JOIN T_users__fielddefs ON uf_ufdf_ID = ufdf_ID
1761                              LEFT JOIN T_users__fieldgroups ON ufdf_ufgp_ID = ufgp_ID
1762                      WHERE uf_user_ID = '.$this->ID.'
1763                          AND ufdf_type = "url"
1764                      ORDER BY ufgp_order, ufdf_order, uf_ID
1765                      LIMIT 1' );
1766                  return $this->field_url;
1767              }
1768          }
1769          else
1770          {    // Get value from $this->url (T_users)
1771              return $this->url;
1772          }
1773      }
1774  
1775  
1776      /**
1777       * Get link from defined userfields
1778       *
1779       * @param array Template params
1780       * @return string Link
1781       */
1782  	function get_field_link( $params = array() )
1783      {
1784          $params = array_merge( array(
1785                  'target' => '_blank',
1786                  'rel'    => 'nofollow'
1787              ), $params );
1788  
1789          $link = '<a href="'.$this->get_field_url().'"'.get_field_attribs_as_string( $params, false ).'>'.$this->get_field_url().'</a>';
1790  
1791          return $link;
1792      }
1793  
1794  
1795      /**
1796       * Set param value
1797       *
1798       * @param string parameter name
1799       * @param mixed parameter value
1800       * @param boolean true to set to NULL if empty value
1801       * @return boolean true, if a value has been set; false if it has not changed
1802       */
1803  	function set( $parname, $parvalue, $make_null = false )
1804      {
1805          switch( $parname )
1806          {
1807              case 'level':
1808                  return $this->set_param( $parname, 'number', $parvalue, $make_null );
1809  
1810              case 'ctry_ID':
1811              default:
1812                  return $this->set_param( $parname, 'string', $parvalue, $make_null );
1813          }
1814      }
1815  
1816  
1817      /**
1818       * Set date created.
1819       *
1820       * @param integer seconds since Unix Epoch.
1821       */
1822  	function set_datecreated( $datecreated, $isYMDhour = false )
1823      {
1824          if( !$isYMDhour )
1825          {
1826              $datecreated = date('Y-m-d H:i:s', $datecreated );
1827          }
1828          // Set value:
1829          $this->datecreated = $datecreated;
1830          // Remmeber change for later db update:
1831          $this->dbchange( 'user_created_datetime', 'string', 'datecreated' );
1832      }
1833  
1834  
1835      /**
1836       * Set email address of the user.
1837       *
1838       * If the email address has changed and we're configured to invalidate the user in this case,
1839       * the user's account gets not active 'emaichanged' status here.
1840       *
1841       * @param string email address to set for the User
1842       * @param boolean Set TRUE if changing of account status is required
1843       * @return boolean true, if set; false if not changed
1844       */
1845  	function set_email( $email, $change_status = true )
1846      {
1847          global $Settings;
1848  
1849          $r = parent::set_param( 'email', 'string', $email );
1850  
1851          if( $change_status )
1852          { // Change user status to 'emailchanged' (if email has changed and Settings are available, which they are not during install):
1853              if( $r && ( $this->ID != 0 ) && isset($Settings) && $Settings->get('newusers_revalidate_emailchg') && ( $this->check_status( 'is_validated' ) ) )
1854              { // Deactivate the account, because the changed email has not been verified yet, but the user account is active:
1855                  $this->set( 'status', 'emailchanged' );
1856              }
1857          }
1858  
1859          if( preg_match( '#@(.+)#i', $email, $ematch ) )
1860          {    // Set email domain ID
1861              global $DB;
1862  
1863              $email_domain = $ematch[1];
1864  
1865              $SQL = new SQL();
1866              $SQL->SELECT( 'dom_ID' );
1867              $SQL->FROM( 'T_basedomains' );
1868              $SQL->WHERE( 'dom_type = \'email\'' );
1869              $SQL->WHERE_and( 'dom_name = '.$DB->quote( $email_domain ) );
1870  
1871              $dom_ID = $DB->get_var( $SQL->get() );
1872              if( !$dom_ID )
1873              {    // The email domains doesn't exist yet, Insert new record
1874                  $DB->query( 'INSERT INTO T_basedomains ( dom_type, dom_name )
1875                      VALUES ( \'email\', '.$DB->quote( $email_domain ).' )' );
1876                  $dom_ID = $DB->insert_id;
1877              }
1878  
1879              $this->set( 'email_dom_ID', (int) $dom_ID );
1880          }
1881  
1882          return $r;
1883      }
1884  
1885  
1886      /**
1887       * Set new Group.
1888       *
1889       * @param Group the Group object to put the user into
1890       * @return boolean true if set, false if not changed
1891       */
1892  	function set_Group( & $Group )
1893      {
1894          if( $this->grp_ID !== $Group->ID )
1895          {
1896              $this->Group = & $Group;
1897              $this->set( 'grp_ID', $Group->ID );
1898              return true;
1899          }
1900  
1901          return false;
1902      }
1903  
1904      /**
1905       * @deprecated by {@link User::set_Group()} since 1.9
1906       */
1907  	function setGroup( & $Group )
1908      {
1909          global $Debuglog;
1910          $Debuglog->add( 'Call to deprecated method User::setGroup(), use set_Group() instead.', 'deprecated' );
1911          return $this->set_Group( $Group );
1912      }
1913  
1914  
1915      /**
1916       * Get the {@link Group} of the user.
1917       *
1918       * @return Group (by reference)
1919       */
1920      function & get_Group()
1921      {
1922          if( ! isset($this->Group) )
1923          {
1924              $GroupCache = & get_GroupCache();
1925              $this->Group = & $GroupCache->get_by_ID($this->grp_ID);
1926          }
1927          return $this->Group;
1928      }
1929  
1930  
1931    /**
1932       * Check password
1933       *
1934       * @param string password
1935       * @param boolean Is the password parameter already MD5()'ed?
1936       * @return boolean
1937       */
1938  	function check_password( $pass, $pass_is_md5 = false )
1939      {
1940          if( !$pass_is_md5 )
1941          {
1942              $pass = md5( $pass );
1943          }
1944          // echo 'pass: ', $pass, '/', $this->pass;
1945  
1946          return ( $pass == $this->pass );
1947      }
1948  
1949  
1950      /**
1951       * Check permission for this user
1952       *
1953       * @param string Permission name, can be one of:
1954       *                - 'edit_timestamp'
1955       *                - 'cats_post_statuses', see {@link User::check_perm_catsusers()}
1956       *                - either group permission names, see {@link Group::check_perm()}
1957       *                - either blogusers permission names, see {@link User::check_perm_blogusers()}
1958       * @param string Permission level
1959       * @param boolean Execution will halt if this is !0 and permission is denied
1960       * @param mixed Permission target (blog ID, array of cat IDs, Item, Comment...)
1961       * @return boolean 0 if permission denied
1962       */
1963  	function check_perm( $permname, $permlevel = 'any', $assert = false, $perm_target = NULL )
1964      {
1965          global $Debuglog;
1966  
1967          if( is_object($perm_target) && isset($perm_target->ID) )
1968          {
1969              $perm_target_ID = $perm_target->ID;
1970          }
1971          elseif( !is_array($perm_target) )
1972          {
1973              $perm_target_ID = $perm_target;
1974          }
1975  
1976          if( isset($perm_target_ID)    // if it makes sense to check the cache
1977              && isset($this->cache_perms[$permname][$permlevel][$perm_target_ID]) )
1978          { // Permission in available in Cache:
1979              $Debuglog->add( "Got perm [$permname][$permlevel][$perm_target_ID] from cache", 'perms' );
1980              return $this->cache_perms[$permname][$permlevel][$perm_target_ID];
1981          }
1982  
1983          $pluggable_perms = array( 'admin', 'spamblacklist', 'slugs', 'templates', 'options', 'files', 'users' );
1984          if( in_array( $permname, $pluggable_perms ) )
1985          {
1986              $permname = 'perm_'.$permname;
1987          }
1988          //$Debuglog->add( "Querying perm [$permname][$permlevel]".( isset( $perm_target_ID ) ? '['.$perm_target_ID.']' : '' ).']', 'perms' );
1989          //pre_dump( 'Perm target: '.var_export( $perm_target, true ) );
1990  
1991          $perm = false;
1992  
1993          switch( $permname )
1994          { // What permission do we want to check?
1995              case 'cats_post_statuses':
1996              case 'cats_post!published':
1997              case 'cats_post!community':
1998              case 'cats_post!protected':
1999              case 'cats_post!private':
2000              case 'cats_post!review':
2001              case 'cats_post!draft':
2002              case 'cats_post!deprecated':
2003              case 'cats_post!redirected':
2004              case 'cats_page':
2005              case 'cats_intro':
2006              case 'cats_podcast':
2007              case 'cats_sidebar':
2008                  // Category permissions...
2009                  if( ! is_array( $perm_target ) )
2010                  {    // We need an array here:
2011                      $perm_target = array( $perm_target );
2012                  }
2013  
2014                  // First we need to create an array of blogs, not cats
2015                  $perm_target_blogs = array();
2016                  foreach( $perm_target as $loop_cat_ID )
2017                  {
2018                      $loop_cat_blog_ID = get_catblog( $loop_cat_ID );
2019                      // echo "cat $loop_cat_ID -> blog $loop_cat_blog_ID <br />";
2020                      if( ! in_array( $loop_cat_blog_ID, $perm_target_blogs ) )
2021                      { // not already in list: add it:
2022                          $perm_target_blogs[] = $loop_cat_blog_ID;
2023                      }
2024                  }
2025  
2026                  $perm = true; // Permission granted if no blog denies it below
2027                  $blogperm = 'blog_'.substr( $permname, 5 );
2028                  // Now we'll check permissions for each blog:
2029                  foreach( $perm_target_blogs as $loop_blog_ID )
2030                  {
2031                      if( ! $this->check_perm( $blogperm, $permlevel, false, $loop_blog_ID ) )
2032                      { // If at least one blog denies the permission:
2033                          $perm = false;
2034                          break;
2035                      }
2036                  }
2037                  break;
2038  
2039              case 'recycle_owncmts':
2040                  // Check permission to edit comments for own items
2041                  $Comment = & $perm_target;
2042                  $Item = & $Comment->get_Item();
2043                  $blog_ID = $Item->get_blog_ID();
2044                  if( $Item->creator_user_ID == $this->ID )
2045                  { // Current user is owner of this item
2046                      if( $Item->is_locked() && !$this->check_perm( 'blog_cats', 'edit', false, $blog_ID ) )
2047                      { // Comment item is locked and current user is not allowed to edit locked items comment
2048                          break;
2049                      }
2050  
2051                      $comment_author_User = & $Comment->get_author_User();
2052                      if( ( empty( $comment_author_User ) || ( $comment_author_User->level <= $this->level ) )
2053                          && in_array( $Comment->status, array( 'published', 'community', 'protected' ) ) )
2054                      { // Comment author is anonymous or his level is lower than current User level, and the Comment was published with some of the above statuses
2055                          // Check blog user perms to see if user may recycle his own posts comments
2056                          $perm = $this->check_perm_blogusers( 'blog_recycle_owncmts', $permlevel, $blog_ID );
2057                          if( ! $perm )
2058                          { // Check groups for permissions to this specific blog:
2059                              $perm = $this->Group->check_perm_bloggroups( 'blog_recycle_owncmts', $permlevel, $blog_ID );
2060                          }
2061                      }
2062                  }
2063                  break;
2064  
2065              case 'blog_ismember':
2066              case 'blog_post_statuses':
2067              case 'blog_post!published':
2068              case 'blog_post!community':
2069              case 'blog_post!protected':
2070              case 'blog_post!private':
2071              case 'blog_post!review':
2072              case 'blog_post!draft':
2073              case 'blog_post!deprecated':
2074              case 'blog_post!redirected':
2075              case 'blog_del_post':
2076              case 'blog_edit':
2077              case 'blog_edit_cmt':
2078              case 'blog_comments':
2079              case 'blog_comment_statuses':
2080              case 'blog_del_cmts':
2081              case 'blog_vote_spam_comments':
2082              case 'blog_comment!published':
2083              case 'blog_comment!community':
2084              case 'blog_comment!protected':
2085              case 'blog_comment!private':
2086              case 'blog_comment!deprecated':
2087              case 'blog_comment!review':
2088              case 'blog_comment!draft':
2089              case 'blog_properties':
2090              case 'blog_cats':
2091              case 'blog_page':
2092              case 'blog_intro':
2093              case 'blog_podcast':
2094              case 'blog_sidebar':
2095              case 'blog_edit_ts':
2096                  // Blog permission to edit its properties...
2097                  if( $this->check_perm_blogowner( $perm_target_ID ) )
2098                  {    // Owner can do *almost* anything:
2099                      $perm = true;
2100                      break;
2101                  }
2102                  /* continue */
2103              case 'blog_admin': // This is what the owner does not have access to!
2104  
2105                  // Group may grant VIEW access, FULL access:
2106                  $this->get_Group();
2107                  $group_permlevel = ( $permlevel == 'view' ||  $permlevel == 'any' ) ? $permlevel : 'editall';
2108                  if( $this->Group->check_perm( 'blogs', $group_permlevel ) )
2109                  { // If group grants a global permission:
2110                      $perm = true;
2111                      break;
2112                  }
2113  
2114                  if( $perm_target_ID > 0 )
2115                  { // Check user perm for this blog:
2116                      $perm = $this->check_perm_blogusers( $permname, $permlevel, $perm_target_ID );
2117                      if( ! $perm )
2118                      { // Check groups for permissions to this specific blog:
2119                          $perm = $this->Group->check_perm_bloggroups( $permname, $permlevel, $perm_target_ID );
2120                      }
2121                  }
2122  
2123                  break;
2124  
2125              case 'comment!CURSTATUS':
2126                  /**
2127                   * @var Comment
2128                   */
2129                  $Comment = & $perm_target;
2130                  // Change the permname to one of the following:
2131                  $permname = 'comment!'.$Comment->status;
2132              case 'comment!published':
2133              case 'comment!community':
2134              case 'comment!protected':
2135              case 'comment!private':
2136              case 'comment!review':
2137              case 'comment!draft':
2138              case 'comment!deprecated':
2139              case 'comment!trash':
2140                  /**
2141                   * @var Comment
2142                   */
2143                  $Comment = & $perm_target;
2144                  $Item = & $Comment->get_Item();
2145                  $blog_ID = $Item->get_blog_ID();
2146                  $check_status = substr( $permname, 8 );
2147  
2148                  if( ( $permlevel != 'view' ) &&  $Item->is_locked() && !$this->check_perm( 'blog_cats', 'edit', false, $blog_ID ) )
2149                  { // Comment item is locked and current user is not allowed to edit/moderate locked items comment
2150                      break;
2151                  }
2152  
2153                  if( $this->check_perm_blog_global( $blog_ID, $permlevel ) )
2154                  { // User has global permission on this blog:
2155                      $perm = true;
2156                      break;
2157                  }
2158  
2159                  if( $Comment->status == 'trash' )
2160                  { // only global group 'editall' perm can give rights to 'trash' status, but this is not the case
2161                      break;
2162                  }
2163  
2164                  if( $permlevel == 'delete' )
2165                  { // permlevel is delete so we have to check the 'blog_del_cmts' permission
2166                      $perm = $this->check_perm( 'blog_del_cmts', 'edit', false, $blog_ID )
2167                              || $this->check_perm( 'recycle_owncmts', $permlevel, false, $Comment );
2168                      break;
2169                  }
2170  
2171                  // Check comment current status permissions at the blog level:
2172                  $blog_permname = 'blog_comment!'.$Comment->status;
2173                  $perm = $perm || $this->check_perm_blogusers( $blog_permname, $permlevel, $blog_ID, $Comment );
2174                  if( ! $perm )
2175                  { // Check groups for permissions to this specific blog:
2176                      $perm = $this->Group->check_perm_bloggroups( $blog_permname, $permlevel, $blog_ID, $Comment, $this );
2177                  }
2178                  if( $perm && ( $Comment->status != $check_status ) )
2179                  { // also check the requested status permissions at the blog level
2180                      $blog_permname = 'blog_comment!'.$check_status;
2181                      $perm = $this->check_perm_blogusers( $blog_permname, 'create', $blog_ID );
2182                      if( ! $perm )
2183                      { // Check groups for permissions to this specific blog:
2184                          $perm = $this->Group->check_perm_bloggroups( $blog_permname, 'create', $blog_ID );
2185                      }
2186                  }
2187                  break;
2188  
2189              case 'item_post!CURSTATUS':
2190                  /**
2191                   * @var Item
2192                   */
2193                  $Item = & $perm_target;
2194                  // Change the permname to one of the following:
2195                  $permname = 'item_post!'.$Item->status;
2196              case 'item_post!published':
2197              case 'item_post!community':
2198              case 'item_post!protected':
2199              case 'item_post!private':
2200              case 'item_post!review':
2201              case 'item_post!draft':
2202              case 'item_post!deprecated':
2203              case 'item_post!redirected':
2204                  // Get the Blog ID
2205                  /**
2206                   * @var Item
2207                   */
2208                  $Item = & $perm_target;
2209                  $blog_ID = $Item->get_blog_ID();
2210                  $check_status = substr( $permname, 10 );
2211  
2212                  if( ( $permlevel != 'view' ) && $Item->is_locked() && !$this->check_perm( 'blog_cats', 'edit', false, $blog_ID ) )
2213                  { // Item is locked and current user is not allowed to edit locked items ( only view permission is allowed by default for locked items )
2214                      break;
2215                  }
2216  
2217                  if( $this->check_perm_blog_global( $blog_ID, $permlevel ) )
2218                  { // User has global permission on this blog:
2219                      $perm = true;
2220                      break;
2221                  }
2222  
2223                  if( $permlevel == 'delete' )
2224                  { // permlevel is delete so we have to check the 'blog_del_post' permission
2225                      $perm = $this->check_perm( 'blog_del_post', 'edit', false, $blog_ID );
2226                      break;
2227                  }
2228  
2229                  // Check permissions at the blog level:
2230                  $blog_permname = 'blog_post!'.$Item->status;
2231                  $perm = $this->check_perm_blogusers( $blog_permname, $permlevel, $blog_ID, $Item );
2232                  if( ! $perm )
2233                  { // Check groups for permissions to this specific blog:
2234                      $perm = $this->Group->check_perm_bloggroups( $blog_permname, $permlevel, $blog_ID, $Item, $this );
2235                  }
2236                  if( $perm && ( $Item->status != $check_status ) )
2237                  { // also check the requested status permissions at the blog level
2238                      $blog_permname = 'blog_post!'.$check_status;
2239                      $perm = $this->check_perm_blogusers( $blog_permname, 'create', $blog_ID );
2240                      if( ! $perm )
2241                      { // Check groups for permissions to this specific blog:
2242                          $perm = $this->Group->check_perm_bloggroups( $blog_permname, 'create', $blog_ID );
2243                      }
2244                  }
2245                  break;
2246  
2247              case 'stats':
2248                  // Blog permission to edit its properties...
2249                  $this->get_Group();
2250  
2251                  // Group may grant VIEW acces, FULL access:
2252                  if( $this->Group->check_perm( $permname, $permlevel ) )
2253                  { // If group grants a global permission:
2254                      $perm = true;
2255                      break;
2256                  }
2257  
2258                  if( $perm_target > 0 )
2259                  { // Check user perm for this blog:
2260                      $perm = $this->check_perm_blogusers( $permname, $permlevel, $perm_target );
2261                      if ( ! $perm )
2262                      { // Check groups for permissions to this specific blog:
2263                          $perm = $this->Group->check_perm_bloggroups( $permname, $permlevel, $perm_target );
2264                      }
2265                  }
2266                  break;
2267  
2268              // asimo> edit_timestamp permission was converted to blog_edit_ts permission
2269  
2270  
2271              // asimo> files permission was converted to pluggable permission
2272              /*case 'files':
2273                  $this->get_Group();
2274                  $perm = $this->Group->check_perm( $permname, $permlevel );*/
2275  
2276                  /* Notes:
2277                   *  - $perm_target can be:
2278                   *    - NULL or 0: check global group permission only
2279                   *    - positive: check global group permission and
2280                   *      (if granted) if a specific blog denies it.
2281  * fp> This is BAD BAD BAD because it's inconsistent with the other permissions
2282  * in b2evolution. There should NEVER be a denying. ony additional allowing.
2283  * It's also inconsistent with most other permission systems.
2284  * The lower file permission level for groups is now called "No Access"
2285  * This should be renamed to "Depending on each blog's permissions"
2286  * Whatever general permissions you have on files, blog can give you additional permissions
2287  * but they can never take a global perm away.
2288  * Tblue> On the permissions page it says that the blog perms will be restricted
2289  * by any global perms, which means to me that a blog cannot grant e. g.
2290  * the files upload perm if this perm isn't granted globally... But apparently
2291  * it shouldn't be like that?! I understand it should be like that then:
2292  * if( ! $perm && $perm_target && in_array( $permlevel, array( 'add', 'view', 'edit' ) )
2293  * {
2294  *         // check if blog grants permission.
2295  * }
2296  * If this is correct, we should remove the note on the blog permissions
2297  * pages and the group properties form.
2298  * fp> ok, I had forgotten we had that old message, but still it doesn't say it will, it says it *may* !
2299  * To be exact the message should be "
2300  * Note: General group permissions may further restrict or extend any permissions defined here."
2301  * Restriction should only happen when "NO ACCESS" is selected
2302  * But when "Depending on each blog's permissions" is selected, THEN (and I guess ONLY then) the blog permissions should be used
2303  * Note: This is quite messy actually. maybe it would make more sense to separate group permissions by "root type":
2304  * i-e nto use the same permission for blog roots vs user root vs shared root vs skins root
2305  * what do you think?
2306  * Tblue> That sounds OK. So we would add another option to the global
2307  * 'files' group perm setting ("Depending on each blog's permissions"), right?
2308  * fp> yes.
2309  * tb> Regarding separation: It could make sense. The blog-specific permissions would only
2310  * affect blog roots (and if "Depending on each blog's permissions" is selected;
2311  * for the other roots we would add separate (global) settings...
2312  * fp> yes.
2313                   *  - Only a $permlevel of 'add', 'view' or 'edit' can be
2314                   *    denied by blog permissions.
2315                   *  - If the group grants the 'all' permission, blogs cannot
2316                   *    deny it.
2317                   */
2318  /*
2319                  if( $perm && $perm_target && in_array( $permlevel, array( 'add', 'view', 'edit' ) )
2320                      && $this->Group->get( 'perm_files' ) != 'all' )
2321                  {    // Check specific blog perms:
2322                      $perm = $this->check_perm_blogusers( $permname, $permlevel, $perm_target );
2323                      if ( ! $perm )
2324                      { // Check groups for permissions for this specific blog:
2325                          $perm = $this->Group->check_perm_bloggroups( $permname, $permlevel, $perm_target );
2326                      }
2327                  }
2328  */
2329                  //break;
2330  
2331              default:
2332                  // Check pluggable permissions using user permission check function
2333                  $this->get_Group();
2334                  $perm = Module::check_perm( $permname, $permlevel, $perm_target, 'user_func', $this->Group );
2335                  if( $perm === true || $perm === NULL )
2336                  { // If user_func or user perm not exists in the corresponding module then $perm value will be NULL and we have to check the group permission.
2337                      // If user_func exists and returns true, then we have to check group permission to make sure it does not restrict the user perm.
2338  
2339                      // Other global permissions (see if the group can handle them).
2340                      // Forward request to group:
2341                      $perm = $this->Group->check_perm( $permname, $permlevel, $perm_target );
2342                  }
2343          }
2344  
2345          if( is_object($perm_target) )
2346          {    // Prevent catchable E_FATAL with PHP 5.2 (because there's no __tostring for e.g. Item)
2347              $taget_name = get_class($perm_target).'('.$perm_target_ID.')';
2348          }
2349          elseif( is_array($perm_target) )
2350          {    // Convert to ID list
2351              $taget_name = '('.implode(',', $perm_target).')';
2352          }
2353          else
2354          {
2355              $taget_name = $perm_target;
2356          }
2357  
2358          $Debuglog->add( 'User perm '.$permname.':'.$permlevel.':'.$taget_name.' => '.($perm ? 'granted' : 'DENIED'), 'perms' );
2359  
2360          if( ! $perm && $assert )
2361          { // We can't let this go on!
2362              global $app_name;
2363              debug_die( sprintf( /* %s is the application name, usually "b2evolution" */ T_('Group/user permission denied by %s!'), $app_name )." ($permname:$permlevel:".( is_object( $perm_target ) ? get_class( $perm_target ).'('.$perm_target_ID.')' : $perm_target ).")" );
2364          }
2365  
2366          if( isset($perm_target_ID) )
2367          {
2368              // echo "cache_perms[$permname][$permlevel][$perm_target] = $perm;";
2369              $this->cache_perms[$permname][$permlevel][$perm_target_ID] = $perm;
2370          }
2371  
2372          return $perm;
2373      }
2374  
2375  
2376      /**
2377       * Check if the user is the owner of the designated blog (which gives him a lot of permissions)
2378       *
2379       * @param integer
2380       * @return boolean
2381       */
2382  	function check_perm_blogowner( $blog_ID )
2383      {
2384          if( empty( $blog_ID ) )
2385          {
2386              return false;
2387          }
2388  
2389          $BlogCache = & get_BlogCache();
2390          /**
2391           * @var Blog
2392           */
2393          $Blog = & $BlogCache->get_by_ID( $blog_ID );
2394  
2395          return ( $Blog->owner_user_ID == $this->ID );
2396      }
2397  
2398  
2399      /**
2400       * Check if the user is the owner of the designated item (which gives him a lot of permissions)
2401       *
2402       * @param integer
2403       * @return boolean
2404       */
2405  	function check_perm_itemowner( $item_ID )
2406      {
2407          if( empty( $item_ID ) )
2408          {
2409              return false;
2410          }
2411  
2412          $ItemCache = & get_ItemCache();
2413          /**
2414           * @var Item
2415           */
2416          $Item = & $ItemCache->get_by_ID( $item_ID );
2417  
2418          return ( $Item->creator_user_ID == $this->ID );
2419      }
2420  
2421  
2422      /**
2423       * Check if user has global perms on this blog
2424       *
2425       * @param integer blog ID
2426       * @param string permlevel
2427       * @return boolean true if user is the owner, or user group has some permission on all blogs
2428       */
2429  	function check_perm_blog_global( $blog_ID, $permlevel = 'edit' )
2430      {
2431          if( $this->check_perm_blogowner( $blog_ID ) )
2432          { // user is the blog owner
2433              return true;
2434          }
2435  
2436          // Group may grant VIEW access, FULL access:
2437          $this->get_Group();
2438          $group_permlevel = ( $permlevel == 'view' && $permlevel == 'any' ) ? 'viewall' : 'editall';
2439          if( $this->Group->check_perm( 'blogs', $group_permlevel ) )
2440          { // If group grants a global permission:
2441              return true;
2442          }
2443  
2444          return false;
2445      }
2446  
2447  
2448      /**
2449       * Check permission for this user on a specified blog
2450       *
2451       * This is not for direct use, please call {@link User::check_perm()} instead
2452       *
2453       * @see User::check_perm()
2454       * @param string Permission name, can be one of the following:
2455       *                  - blog_ismember
2456       *                  - blog_post_statuses
2457       *                  - blog_del_post
2458       *                  - blog_edit_ts
2459       *                  - blog_comments
2460       *                  - blog_cats
2461       *                  - blog_properties
2462       * @param string Permission level
2463       * @param integer Permission target blog ID
2464       * @param Item Item that we want to edit
2465       * @return boolean 0 if permission denied
2466       */
2467  	function check_perm_blogusers( $permname, $permlevel, $perm_target_blog, $perm_target = NULL )
2468      {
2469          // Check if user blog advanced perms are loaded
2470          if( ! isset( $this->blog_post_statuses[$perm_target_blog] ) )
2471          { // Blog post statuses have not been loaded yet:
2472              $this->blog_post_statuses[$perm_target_blog] = array();
2473              if( ! load_blog_advanced_perms( $this->blog_post_statuses[$perm_target_blog], $perm_target_blog, $this->ID, 'bloguser' ) )
2474              { // Could not load blog advanced user perms
2475                  return false;
2476              }
2477          }
2478  
2479          // Check permission and return the result
2480          return check_blog_advanced_perm( $this->blog_post_statuses[$perm_target_blog], $this->ID, $permname, $permlevel, $perm_target );
2481      }
2482  
2483  
2484      /**
2485       * Check if user status permit the give action
2486       *
2487       * @param string action
2488       * @param integger target ID - can be a post ID, user ID
2489       * @return boolean true if the action is permitted, false otherwise
2490       */
2491  	function check_status( $action, $target = NULL )
2492      {
2493          global $Settings, $Blog;
2494  
2495          switch( $action )
2496          {
2497              case 'can_view_user':
2498                  if( $Settings->get( 'allow_anonymous_user_profiles' ) || ( !empty( $target ) && ( $target == $this->ID ) ) )
2499                  { // even anonymous users can see users profile, or user wants to check his own profile
2500                      return true;
2501                  }
2502                  return ( ( $this->status == 'activated' ) || ( $this->status == 'autoactivated' ) );
2503              case 'can_view_users':
2504                  if( $Settings->get( 'allow_anonymous_user_list' ) )
2505                  { // even anonymous users can see users list
2506                      return true;
2507                  }
2508                  return ( ( $this->status == 'activated' ) || ( $this->status == 'autoactivated' ) );
2509              case 'can_view_comments':
2510              case 'can_view_contacts':
2511              case 'can_view_messages':
2512              case 'can_view_threads':
2513              case 'is_validated':
2514              case 'can_access_admin':
2515              case 'can_edit_post':
2516              case 'can_edit_comment':
2517              case 'can_edit_contacts':
2518              case 'can_report_user':
2519                  return ( ( $this->status == 'activated' ) || ( $this->status == 'autoactivated' ) );
2520              case 'can_be_validated':
2521                  return ( ( $this->status == 'new' ) || ( $this->status == 'emailchanged' ) || ( $this->status == 'deactivated' ) || ( $this->status == 'failedactivation' ) );
2522              case 'can_view_msgform':
2523              case 'can_receive_any_message': // can this user receive emails or private messages
2524              case 'can_receive_pm':
2525              case 'can_display_avatar': // can display user's avatar for not admin users
2526              case 'can_display_link': // can display user's profile link for not admin users
2527                  return ( $this->status != 'closed' );
2528              case 'is_closed':
2529                  return ( $this->status == 'closed' );
2530              default:
2531                  debug_die( 'This action is not handled during status check!' );
2532          }
2533      }
2534  
2535      /**
2536       * Check if the user has the given role in any blog
2537       *
2538       * @param string role name, available values ( post_owner, moderator )
2539       * @return mixed NULL if the given roll name is not defined or there are no blogs, true if the user is super admin, 0 if the user doesn't have the given role, positive number otherwise
2540       */
2541  	function check_role( $rolename )
2542      {
2543          global $DB;
2544  
2545          if( $this->check_perm( 'blogs', 'editall' ) )
2546          { // if user has global editall blogs permission then it has any kind of role in all blogs
2547              return true;
2548          }
2549  
2550          switch( $rolename )
2551          {
2552              case 'post_owner':
2553                  // User is considerated as a post owner, if already has at least one post, or he has right to create posts
2554                  if( $this->get_num_posts() > 0 )
2555                  { // User already has at least one post
2556                      return true;
2557                  }
2558                  $role_conditions = array( 'perm_poststatuses' => array( 'IS NOT NULL', '<> ""' ) );
2559                  break;
2560  
2561              case 'member':
2562                  // User has member role if is member of at least one blog
2563                  $role_conditions = array( 'ismember' => array( 'IS NOT NULL', '<> 0' ) );
2564                  break;
2565  
2566              case 'comment_editor':
2567                  // User has permission to edit some other users comments at least in one status
2568                  $role_conditions = array(
2569                      'perm_edit_cmt' => array( 'IS NOT NULL', '<> "no"', '<> "own"' ),
2570                      'perm_cmtstatuses' => array( 'IS NOT NULL', '<> 0' )
2571                  );
2572                  break;
2573  
2574              case 'comment_moderator':
2575                  // set comment moderator perm names
2576                  $edit_perm_name = 'perm_edit_cmt';
2577                  $statuses_perm_name = 'perm_cmtstatuses';
2578              case 'post_moderator':
2579                  if( $rolename == 'post_moderator' )
2580                  { // set post moderator perm names
2581                      $edit_perm_name = 'perm_edit';
2582                      $statuses_perm_name = 'perm_poststatuses';
2583                  }
2584  
2585                  // A moderator must have permissions to create post/comment with at least two statuses from moderation statuses + published status
2586                  $check_statuses = get_visibility_statuses( 'moderation' );
2587                  // Create addition of statuses perm values
2588                  $perms_value = get_status_permvalue( 'published' );
2589                  foreach( $check_statuses as $status )
2590                  {
2591                      $perms_value = $perms_value + get_status_permvalue( $status );
2592                  }
2593  
2594                  // Check if user has permission to edit other comments than his own and has create permission on at least two statuses defined above
2595                  $role_conditions = array(
2596                      $edit_perm_name => array( 'IS NOT NULL', '<> "no"', '<> "own"' ),
2597                      $statuses_perm_name => array( 'IS NOT NULL', 'BIT_COUNT( $perm_field$ & '.$perms_value.' ) > 1' )
2598                  );
2599                  break;
2600  
2601              default: // roll with the given roll name is not defined
2602                  return NULL;
2603          }
2604  
2605          $where_clause = '';
2606          $perm_prefixes = array( 'bloguser_', 'bloggroup_' );
2607          foreach( $perm_prefixes as $prefix )
2608          { // Check requred perms on blogusers and bloggroups as well
2609              $where_part = '';
2610              foreach( $role_conditions as $perm_name => $conditions )
2611              { // Go through each required permission
2612                  $perm_field = $prefix.$perm_name;
2613                  foreach( $conditions as $condition )
2614                  { // Check all defined conditions and join with 'AND' operator
2615                      if( strpos( $condition, '$perm_field$' ) !== false )
2616                      { // The $perm_filed must be replaced in the middle of the condition
2617                          $where_part .= '( '.str_replace( '$perm_field$', $perm_field, $condition ).' ) AND ';
2618                      }
2619                      else
2620                      { // The $perm_filed must be added into the beginning of the condition
2621                          $where_part .= '( '.$perm_field.' '.$condition.' ) AND ';
2622                      }
2623                  }
2624              }
2625              // Remove the last ' AND ' from the end of this where clause part
2626              $where_part = substr( $where_part, 0, strlen( $where_part ) - 5 );
2627              // Add the created conditions to the final where clause
2628              $where_clause .= '( '.$where_part.' )';
2629              if( $prefix != 'bloggroup_' )
2630              { // 'bloggroup_' perm check is the last, but everywhere else we need an 'OR' operator
2631                  $where_clause .= ' OR ';
2632              }
2633          }
2634  
2635          // Count blog ids where this user has the required permissions for the given role
2636          $SQL = new SQL();
2637          $SQL->SELECT( 'count( blog_ID )' );
2638          $SQL->FROM( 'T_blogs' );
2639          $SQL->FROM_add( 'LEFT JOIN T_coll_user_perms ON (blog_advanced_perms <> 0 AND blog_ID = bloguser_blog_ID AND bloguser_user_ID = '.$this->ID.' )' );
2640          $SQL->FROM_add( 'LEFT JOIN T_coll_group_perms ON (blog_advanced_perms <> 0 AND blog_ID = bloggroup_blog_ID AND bloggroup_group_ID = '.$this->grp_ID.' )' );
2641          $SQL->WHERE( 'blog_owner_user_ID = '.$this->ID );
2642          $SQL->WHERE_or( $where_clause );
2643  
2644          return $DB->get_var( $SQL->get(), 0, NULL, 'Check user role in all blogs' );
2645      }
2646  
2647  
2648      /**
2649       * Check if this user and his group accept receiving private messages or not
2650       *
2651       * @return boolean
2652       */
2653  	function accepts_pm()
2654      {
2655          global $UserSettings;
2656          return ( $UserSettings->get( 'enable_PM', $this->ID ) && $this->check_perm( 'perm_messaging', 'reply' ) );
2657      }
2658  
2659  
2660      /**
2661       * Check if this user accepts receiving emails and has an email address
2662       *
2663       * @return boolean
2664       */
2665  	function accepts_email()
2666      {
2667          global $UserSettings, $Settings;
2668          return ( $Settings->get( 'emails_msgform') != 'never' ) && ( $UserSettings->get( 'enable_email', $this->ID ) ) && ( ! empty( $this->email ) );
2669      }
2670  
2671  
2672      /**
2673       * Get messaging possibilities between current user and this user
2674       *
2675       * @return NULL|string allowed messaging possibility: PM > email > login > NULL
2676       */
2677  	function get_msgform_possibility( $current_User = NULL )
2678      {
2679          if( ! $this->check_status( 'can_receive_any_message' ) )
2680          { // there is no way to send a message to a user with closed account
2681              return NULL;
2682          }
2683  
2684          if( is_logged_in() )
2685          { // current User is a registered user
2686              if( $current_User == NULL )
2687              {
2688                  global $current_User;
2689              }
2690              if( $this->accepts_pm() && $current_User->accepts_pm() && ( $this->ID != $current_User->ID ) )
2691              { // both user has permission to send or receive private message and not the same user
2692                  // check if current_User is allowed to create a new conversation with this user.
2693                  $blocked_contact = check_blocked_contacts( array( $this->ID ) );
2694                  if( empty( $blocked_contact ) )
2695                  { // user is allowed to send pm to this user, and he didn't reached his new thread limit yet
2696                      return 'PM';
2697                  }
2698              }
2699              if( $this->accepts_email() )
2700              { // this user allows email => send email
2701                  return 'email';
2702              }
2703          }
2704          else
2705          { // current User is not logged in
2706              if( $this->accepts_email() )
2707              { // this user allows email
2708                  return 'email';
2709              }
2710              if( $this->accepts_pm() )
2711              { // no email option try to log in and send private message (just registered users can send PM)
2712                  return 'login';
2713              }
2714          }
2715          // no messaging option between current_User and this user
2716          return NULL;
2717      }
2718  
2719  
2720      /**
2721       * Get the reason why current User is not able to make a contact with this User
2722       *
2723       * This is used when get_msgform_possibility returns NULL;
2724       *
2725       * @return string
2726       */
2727  	function get_no_msgform_reason()
2728      {
2729          global $current_User;
2730  
2731          if( ( is_logged_in() ) && $this->accepts_pm() )
2732          {
2733              if( $current_User->accepts_pm() )
2734              { // current User accepts private messages
2735                  if( $current_User->ID == $this->ID )
2736                  { // current User and recipient user are the same
2737                      return T_( 'You cannot send a private message to yourself.' );
2738                  }
2739                  // current User is not able to contact with this User because User has blocked current User or current User is not in this users contact list
2740                  return T_( 'This user can only be contacted through private messages, but you are not allowed to send private message to this user.' );
2741              }
2742              // current User has no messaging permission or has disabled private messages in user preferences
2743              return T_( 'This user can only be contacted through private messages but you are not allowed to send any private messages.' );
2744          }
2745          // current User is not logged in or this User doesn't want to be contacted
2746          return T_( 'This user does not wish to be contacted directly.' );
2747      }
2748  
2749  
2750      /**
2751       * Insert object into DB based on previously recorded changes
2752       *
2753       * Triggers the plugin event AfterUserInsert.
2754       *
2755       * @param boolean TRUE to automatically create new blog if group has permission
2756       * @return boolean true on success
2757       */
2758  	function dbinsert( $create_auto_blog = true )
2759      {
2760          global $Plugins, $DB;
2761  
2762          $DB->begin();
2763  
2764          if( $result = parent::dbinsert() )
2765          { // We could insert the user object..
2766  
2767              // Add new fields:
2768              if( !empty($this->new_fields) )
2769              {
2770                  $sql = 'INSERT INTO T_users__fields( uf_user_ID, uf_ufdf_ID, uf_varchar )
2771                                  VALUES ('.$this->ID.', '.implode( '), ('.$this->ID.', ', $this->new_fields ).' )';
2772                  $DB->query( $sql, 'Insert new fields' );
2773                  // Reset new fields in object:
2774                  $this->new_fields = array();
2775              }
2776  
2777              // Notify plugins:
2778              // A user could be created also in another DB (to synchronize it with b2evo)
2779              $Plugins->trigger_event( 'AfterUserInsert', $params = array( 'User' => & $this ) );
2780  
2781              $Group = & $this->get_Group();
2782              if( $create_auto_blog && $Group->check_perm( 'perm_getblog', 'allowed' ) )
2783              { // automatically create new blog for this user
2784                  // TODO: sam2kb> Create a blog only when this user is validated!
2785                  $new_Blog = new Blog( NULL );
2786                  $shortname = $this->get( 'login' );
2787                  $new_Blog->set( 'owner_user_ID', $this->ID );
2788                  $new_Blog->set( 'shortname', $shortname );
2789                  $new_Blog->set( 'name', $shortname.'\'s blog' );
2790                  $new_Blog->set( 'locale', $this->get( 'locale' ));
2791                  $new_Blog->set( 'urlname', urltitle_validate( $shortname, $shortname, $new_Blog->ID, false, 'blog_urlname', 'blog_ID', 'T_blogs', $this->get( 'locale' ) ) );
2792  
2793                  // Defines blog settings by its kind.
2794                  $Plugins->trigger_event( 'InitCollectionKinds', array(
2795                                  'Blog' => & $new_Blog,
2796                                  'kind' => 'std',
2797                              ) );
2798  
2799                  $new_Blog->create();
2800              }
2801  
2802              /* Save IP Range -- start */
2803              $ip = int2ip( ip2int( $_SERVER['REMOTE_ADDR'] ) ); // Convert IPv6 to IPv4
2804              if( preg_match( '#^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#i', $ip ) )
2805              {    // Check IP for correct format
2806                  $ip_24bit_start = ip2int( preg_replace( '#\.\d{1,3}$#i', '.0', $ip ) );
2807                  $ip_24bit_end = ip2int( preg_replace( '#\.\d{1,3}$#i', '.255', $ip ) );
2808  
2809                  if( $iprange = get_ip_range( $ip_24bit_start, $ip_24bit_end ) )
2810                  {    // Update ip range
2811                      $DB->query( 'UPDATE T_antispam__iprange
2812                                      SET aipr_user_count = '.$DB->quote( $iprange->aipr_user_count + 1 ).'
2813                                      WHERE aipr_ID = '.$DB->quote( $iprange->aipr_ID ) );
2814                  }
2815                  else
2816                  {    // Insert new ip range
2817                      $DB->query( 'INSERT INTO T_antispam__iprange ( aipr_IPv4start, aipr_IPv4end, aipr_user_count )
2818                                      VALUES ( '.$DB->quote( $ip_24bit_start ).', '.$DB->quote( $ip_24bit_end ).', '.$DB->quote( '1' ).' ) ' );
2819                  }
2820              }
2821              /* Save IP Range -- end */
2822          }
2823  
2824          $DB->commit();
2825  
2826          return $result;
2827      }
2828  
2829  
2830      /**
2831       * Update the DB based on previously recorded changes.
2832       *
2833       * Triggers the plugin event AfterUserUpdate.
2834       */
2835  	function dbupdate()
2836      {
2837          global $DB, $Plugins, $current_User, $localtimenow;
2838  
2839          $DB->begin();
2840  
2841          parent::dbupdate();
2842  
2843          // Update existing fields:
2844          if( !empty($this->updated_fields) )
2845          {
2846              foreach( $this->updated_fields as $uf_ID => $uf_val )
2847              {
2848                  // Note the updated_fields key values must be integers, so don't need casting or DB->quote()
2849                  if( empty( $uf_val ) )
2850                  {    // Delete field:
2851                      $DB->query( 'DELETE FROM T_users__fields
2852                                                           WHERE uf_ID = '.$uf_ID );
2853                  }
2854                  else
2855                  {    // Update field:
2856                      $DB->query( 'UPDATE T_users__fields
2857                                                      SET uf_varchar = '.$DB->quote( $uf_val ).'
2858                                                  WHERE uf_ID = '.$uf_ID );
2859                  }
2860              }
2861              // Reset updated fields in object:
2862              $this->updated_fields = array();
2863          }
2864  
2865          // Add new fields:
2866          if( !empty($this->new_fields) )
2867          {
2868              $sql = 'INSERT INTO T_users__fields( uf_user_ID, uf_ufdf_ID, uf_varchar )
2869                              VALUES ('.$this->ID.', '.implode( '), ('.$this->ID.', ', $this->new_fields ).' )';
2870              $DB->query( $sql, 'Insert new fields' );
2871              // Reset new fields in object:
2872              $this->new_fields = array();
2873          }
2874  
2875          // Notify plugins:
2876          // Example: An authentication plugin could synchronize/update the password of the user.
2877          $Plugins->trigger_event( 'AfterUserUpdate', $params = array( 'User' => & $this ) );
2878  
2879          $DB->commit();
2880  
2881          // This User has been modified, cached content depending on it should be invalidated:
2882          BlockCache::invalidate_key( 'user_ID', $this->ID );
2883  
2884          return true;
2885      }
2886  
2887  
2888      /**
2889       * Delete user and dependencies from database
2890       *
2891       * Includes WAY TOO MANY requests because we try to be compatible with MySQL 3.23, bleh!
2892       *
2893       * @param Log Log object where output gets added (by reference).
2894       */
2895  	function dbdelete( & $Log )
2896      {
2897          global $DB, $Plugins;
2898  
2899          if( $this->ID == 0 ) debug_die( 'Non persistant object cannot be deleted!' );
2900  
2901          $deltype = param( 'deltype', 'string', '' ); // spammer
2902  
2903          $DB->begin();
2904  
2905          if( $deltype == 'spammer' )
2906          { // If we delete user as spammer we should delete private messaged of this user
2907              $this->delete_messages();
2908          }
2909          else
2910          { // If we delete user as not spammer we keep his comments as from anonymous user
2911              // Transform registered user comments to unregistered:
2912              $ret = $DB->query( 'UPDATE T_comments
2913                                                      SET comment_author_ID = NULL,
2914                                                              comment_author = '.$DB->quote( $this->get('preferredname') ).',
2915                                                              comment_author_email = '.$DB->quote( $this->get('email') ).',
2916                                                              comment_author_url = '.$DB->quote( $this->get('url') ).'
2917                                                      WHERE comment_author_ID = '.$this->ID );
2918              if( is_a( $Log, 'log' ) )
2919              {
2920                  $Log->add( 'Transforming user\'s comments to unregistered comments... '.sprintf( '(%d rows)', $ret ), 'note' );
2921              }
2922          }
2923  
2924          // Get list of posts that are going to be deleted (3.23)
2925          $post_list = implode( ',', $DB->get_col( '
2926                  SELECT post_ID
2927                    FROM T_items__item
2928                   WHERE post_creator_user_ID = '.$this->ID ) );
2929  
2930          if( !empty( $post_list ) )
2931          {
2932              // Delete comments
2933              $ret = $DB->query( "DELETE FROM T_comments
2934                                                      WHERE comment_post_ID IN ($post_list)" );
2935              if( is_a( $Log, 'log' ) )
2936              {
2937                  $Log->add( sprintf( 'Deleted %d comments on user\'s posts.', $ret ), 'note' );
2938              }
2939  
2940              // Delete post extracats
2941              $ret = $DB->query( "DELETE FROM T_postcats
2942                                                      WHERE postcat_post_ID IN ($post_list)" );
2943              if( is_a( $Log, 'log' ) )
2944              {
2945                  $Log->add( sprintf( 'Deleted %d extracats of user\'s posts\'.', $ret ) ); // TODO: geeky wording.
2946              }
2947  
2948              // Posts will we auto-deleted by parent method
2949          }
2950          else
2951          { // no posts
2952              if( is_a( $Log, 'log' ) )
2953              {
2954                  $Log->add( 'No posts to delete.', 'note' );
2955              }
2956          }
2957  
2958          // Get list of sessions that are going to be deleted
2959          $sessions_SQL = new SQL();
2960          $sessions_SQL->SELECT( 'sess_ID' );
2961          $sessions_SQL->FROM( 'T_sessions' );
2962          $sessions_SQL->WHERE( 'sess_user_ID = '.$this->ID );
2963          $sessions_list = $DB->get_col( $sessions_SQL->get() );
2964  
2965          if( !empty( $sessions_list ) )
2966          { // Delete all hit logs of this user
2967              $DB->query( 'DELETE FROM T_hitlog
2968                      WHERE hit_sess_ID IN ( '.$DB->quote( $sessions_list ).' )' );
2969          }
2970  
2971          // delete user involved ophan threads
2972          delete_orphan_threads( $this->ID );
2973  
2974          // Remove this user from posts where it was as last edit user
2975          $DB->query( 'UPDATE T_items__item
2976                                      SET post_lastedit_user_ID = NULL
2977                                    WHERE post_lastedit_user_ID = '.$this->ID );
2978          $DB->query( 'UPDATE T_items__version
2979                                      SET iver_edit_user_ID = NULL
2980                                    WHERE iver_edit_user_ID = '.$this->ID );
2981  
2982          // Remove this user from links where it was as last edit user
2983          $DB->query( 'UPDATE T_links
2984                                      SET link_lastedit_user_ID = NULL
2985                                    WHERE link_lastedit_user_ID = '.$this->ID );
2986  
2987          // remember ID, because parent method resets it to 0
2988          $old_ID = $this->ID;
2989          $old_email = $this->get( 'email' );
2990  
2991          // Delete main object:
2992          if( ! parent::dbdelete() )
2993          {
2994              $DB->rollback();
2995  
2996              $Log->add( 'User has not been deleted.', 'error' );
2997              return false;
2998          }
2999  
3000          // user was deleted, also delete this user's media folder recursively
3001          $FileRootCache = & get_FileRootCache();
3002          $root_directory = $FileRootCache->get_root_dir( 'user', $old_ID );
3003          rmdir_r( $root_directory );
3004  
3005          if( $deltype == 'spammer' )
3006          { // User was deleted as spammer, we should mark email of this user as 'Spammer'
3007              load_class( 'tools/model/_emailblocked.class.php', 'EmailBlocked' );
3008              $EmailBlockedCache = & get_EmailBlockedCache();
3009              $EmailBlocked = & $EmailBlockedCache->get_by_name( $old_email, false, false );
3010              if( !$EmailBlocked )
3011              {    // Create new record in the T_email_blocked table
3012                  $EmailBlocked = new EmailBlocked();
3013                  $EmailBlocked->set( 'address', $old_email );
3014              }
3015              if( !empty( $EmailBlocked ) )
3016              { // Save status of an email address
3017                  $EmailBlocked->set( 'status', 'spammer' );
3018                  $EmailBlocked->dbsave();
3019              }
3020          }
3021  
3022          $DB->commit();
3023  
3024          if( is_a( $Log, 'log' ) )
3025          {
3026              $Log->add( 'Deleted User.', 'note' );
3027          }
3028  
3029          // Notify plugins:
3030          $this->ID = $old_ID;
3031          $Plugins->trigger_event( 'AfterUserDelete', $params = array( 'User' => & $this ) );
3032          $this->ID = 0;
3033  
3034          return true;
3035      }
3036  
3037  
3038      /**
3039       * Send an email to the user with a link to validate/confirm his email address.
3040       *
3041       * If the email could get sent, it saves the used "request_id" into the user's Session.
3042       *
3043       * @param string URL, where to redirect the user after he clicked the validation link (gets saved in Session).
3044       * @return boolean True, if the email could get sent; false if not
3045       */
3046  	function send_validate_email( $redirect_to_after, $blog = NULL, $email_changed = false )
3047      {
3048          global $app_name, $Session, $secure_htsrv_url, $baseurl, $servertimenow;
3049          global $Settings, $UserSettings;
3050  
3051          if( $Settings->get( 'validation_process' ) == 'easy' )
3052          { // validation process is set to easy, send and easy activation email
3053              return send_easy_validate_emails( array( $this->ID ), false, $email_changed );
3054          }
3055  
3056          if( mail_is_blocked( $this->email ) )
3057          { // prevent trying to send an email to a blocked email address ( Note this is checked in the send_easy_validate_emails too )
3058              return false;
3059          }
3060  
3061          if( empty( $redirect_to_after ) )
3062          { // redirect to was not set
3063              $redirect_to_after = param( 'redirect_to', 'url', '' );
3064              if( empty( $redirect_to_after ) )
3065              {
3066                  if( is_admin_page() )
3067                  {
3068                      $redirect_to_after = regenerate_url( 'action' );
3069                  }
3070                  else
3071                  {
3072                      $redirect_to_after = $this->get_userpage_url();
3073                  }
3074              }
3075          }
3076  
3077          $request_id = generate_random_key(22);
3078  
3079          $blog_param = ( empty( $blog ) ) ? '' : '&inskin=1&blog='.$blog;
3080  
3081          // Change locale here to localize the email subject and content
3082          locale_temp_switch( $this->get( 'locale' ) );
3083          $email_template_params = array(
3084                  'status'     => $this->status,
3085                  'blog_param' => $blog_param,
3086                  'request_id' => $request_id,
3087              );
3088          $r = send_mail_to_User( $this->ID, T_('Activate your account: $login$'), 'account_activate', $email_template_params, true );
3089          locale_restore_previous();
3090  
3091          if( $r )
3092          { // save request_id into Session
3093              $request_ids = $Session->get( 'core.validatemail.request_ids' );
3094              if( ( ! is_array($request_ids) ) || $email_changed )
3095              { // create new request ids array if it doesn't exist yet, or if user email changed ( this way the old request into the old email address won't be valid )
3096                  $request_ids = array();
3097              }
3098              $request_ids[] = $request_id;
3099              $Session->set( 'core.validatemail.request_ids', $request_ids, 86400 * 2 ); // expires in two days (or when clicked)
3100              // set a redirect_to session variable because this way after the account will be activated we will know where to redirect
3101              $Session->set( 'core.validatemail.redirect_to', $redirect_to_after  );
3102              $Session->dbsave(); // save immediately
3103  
3104              // update last activation email timestamp
3105              $UserSettings->set( 'last_activation_email', date2mysql( $servertimenow ), $this->ID );
3106              $UserSettings->dbupdate();
3107          }
3108  
3109          return $r;
3110      }
3111  
3112  
3113      /**
3114       * Activate user account after user clicks on activate link from email
3115       */
3116  	function activate_from_Request()
3117      {
3118          global $DB, $Settings, $UserSettings;
3119  
3120          // Activate current user account:
3121          $this->set( 'status', 'activated' );
3122          $this->dbupdate();
3123  
3124          // clear last reminder key and last activation email date because the user was activated
3125          $UserSettings->delete( 'last_activation_reminder_key', $this->ID );
3126          $UserSettings->delete( 'last_activation_email', $this->ID );
3127          $UserSettings->delete( 'activation_reminder_count', $this->ID );
3128          $UserSettings->delete( 'send_activation_reminder', $this->ID );
3129          $UserSettings->dbupdate();
3130  
3131          if( $Settings->get( 'newusers_findcomments' ) )
3132          {    // We have to assign the all old comments from current user by email
3133              $DB->query( '
3134                  UPDATE T_comments
3135                     SET comment_author_ID = "'.$this->ID.'"
3136                   WHERE comment_author_email = "'.$this->email.'"
3137                     AND comment_author_ID IS NULL' );
3138          }
3139  
3140          // Create a welcome private message when user's status was changed to Active
3141          $this->send_welcome_message();
3142  
3143          // Send notification email about activated account to users with edit users permission
3144          $email_template_params = array(
3145              'User' => $this,
3146              'login' => $this->login, // this is required in the send_admin_notification
3147          );
3148          send_admin_notification( NT_('New user account activated'), 'account_activated', $email_template_params );
3149      }
3150  
3151  
3152      // Template functions {{{
3153  
3154      /**
3155       * Template function: display user's level
3156       */
3157  	function level()
3158      {
3159          $this->disp( 'level', 'raw' );
3160      }
3161  
3162  
3163      /**
3164       * Template function: display user's login
3165       *
3166       * @param string Output format, see {@link format_to_output()}
3167       */
3168  	function login( $format = 'htmlbody' )
3169      {
3170          $this->disp( 'login', $format );
3171      }
3172  
3173  
3174      /**
3175       * Template helper function: Get a link to a message form for this user.
3176       *
3177       * @param string url of the message form
3178       * @param string to display before link
3179       * @param string to display after link
3180       * @param string link text
3181       * @param string link title
3182       * @param string class name
3183       */
3184  	function get_msgform_link( $form_url = NULL, $before = ' ', $after = ' ', $text = '#', $title = '#', $class = '' )
3185      {
3186          if( empty($this->email) )
3187          { // We have no email for this User :(
3188              return false;
3189          }
3190  
3191          $available_msgform = $this->get_msgform_possibility();
3192          if( ! $available_msgform )
3193          {    // There is no way this user accepts receiving messages.
3194              return false;
3195          }
3196  
3197          if( is_null($form_url) )
3198          {
3199              global $Blog;
3200              $form_url = isset($Blog) ? $Blog->get('msgformurl') : '';
3201          }
3202  
3203          $form_url = url_add_param( $form_url, 'recipient_id='.$this->ID.'&amp;redirect_to='.rawurlencode(url_rel_to_same_host(regenerate_url('','','','&'), $form_url)) );
3204  
3205          if( $title == '#' )
3206          {
3207              switch( $available_msgform )
3208              {
3209                  case 'email':
3210                      $title = T_('Send email to user');
3211                      break;
3212                  case 'PM':
3213                  case 'login':
3214                  default:
3215                      $title = T_('Send message to user');
3216                      break;
3217              }
3218          }
3219          if( $text == '#' ) $text = get_icon( 'email', 'imgtag', array( 'class' => 'middle', 'title' => $title ) );
3220  
3221          $r = '';
3222          $r .= $before;
3223          $r .= '<a href="'.$form_url.'" title="'.$title.'"';
3224          if( !empty( $class ) )
3225          {
3226              $r .= ' class="'.$class.'"';
3227          }
3228          $r .= '>'.$text.'</a>';
3229          $r .= $after;
3230  
3231          return $r;
3232      }
3233  
3234  
3235      /**
3236       * Template function: display a link to a message form for this user
3237       *
3238       * @param string url of the message form
3239       * @param string to display before link
3240       * @param string to display after link
3241       * @param string link text
3242       * @param string link title
3243       * @param string class name
3244       */
3245  	function msgform_link( $form_url = NULL, $before = ' ', $after = ' ', $text = '#', $title = '#', $class = '' )
3246      {
3247          echo $this->get_msgform_link( $form_url, $before, $after, $text, $title, $class );
3248      }
3249  
3250  
3251      /**
3252       * Template function: display user's preferred name
3253       *
3254       * @param string Output format, see {@link format_to_output()}
3255       */
3256  	function preferred_name( $format = 'htmlbody' )
3257      {
3258          echo format_to_output( $this->get_preferred_name(), $format );
3259      }
3260  
3261  
3262      /**
3263       * Template function: display user's URL
3264       *
3265       * @param string string to display before the date (if changed)
3266       * @param string string to display after the date (if changed)
3267       * @param string Output format, see {@link format_to_output()}
3268       */
3269  	function url( $before = '', $after = '', $format = 'htmlbody' )
3270      {
3271          if( !empty( $this->url ) )
3272          {
3273              echo $before;
3274              $this->disp( 'url', $format );
3275              echo $after;
3276          }
3277      }
3278  
3279  
3280      /**
3281       * Template function: display number of user's posts
3282       */
3283  	function num_posts( $format = 'htmlbody' )
3284      {
3285          echo format_to_output( $this->get_num_posts(), $format );
3286      }
3287  
3288  
3289      /**
3290       * Template function: display first name of the user
3291       */
3292  	function first_name( $format = 'htmlbody' )
3293      {
3294          $this->disp( 'firstname', $format );
3295      }
3296  
3297  
3298      /**
3299       * Template function: display last name of the user
3300       */
3301  	function last_name( $format = 'htmlbody' )
3302      {
3303          $this->disp( 'lastname', $format );
3304      }
3305  
3306  
3307      /**
3308       * Template function: display nickname of the user
3309       */
3310  	function nick_name( $format = 'htmlbody' )
3311      {
3312          $this->disp( 'nickname', $format );
3313      }
3314  
3315  
3316      /**
3317       * Return gender of the user
3318       */
3319  	function get_gender()
3320      {
3321          switch( $this->gender )
3322          {
3323              case 'M':
3324                  return T_('A man');
3325  
3326              case 'F':
3327                  return T_('A woman');
3328          }
3329  
3330          return NULL;
3331      }
3332  
3333  
3334      /**
3335       * Return attr class depending on gender of the user
3336       */
3337  	function get_gender_class()
3338      {
3339          $gender_class = 'user';
3340  
3341          if( $this->check_status( 'is_closed' ) )
3342          { // Set different gender color if user is closed
3343              return 'user closed';
3344          }
3345  
3346          if( ! check_setting( 'gender_colored' ) )
3347          { // Don't set gender color if setting is OFF
3348              return $gender_class;
3349          }
3350  
3351          switch( $this->gender )
3352          { // Set a class name for each gender type
3353              case 'M':
3354                  $gender_class .= ' man';
3355                  break;
3356              case 'F':
3357                  $gender_class .= ' woman';
3358                  break;
3359              default:
3360                  $gender_class .= ' nogender';
3361                  break;
3362          }
3363  
3364          return $gender_class;
3365      }
3366  
3367  
3368      /**
3369       * Template function: display email of the user
3370       */
3371  	function email( $format = 'htmlbody' )
3372      {
3373          $this->disp( 'email', $format );
3374      }
3375  
3376  
3377      /**
3378       * Template function: display ICQ of the user
3379       * @deprecated
3380       */
3381  	function icq( $format = 'htmlbody' )
3382      {
3383      }
3384  
3385  
3386      /**
3387       * Template function: display AIM of the user.
3388       * @deprecated
3389       */
3390  	function aim( $format = 'htmlbody' )
3391      {
3392      }
3393  
3394  
3395      /**
3396       * Template function: display Yahoo IM of the user
3397       * @deprecated
3398       */
3399  	function yim( $format = 'htmlbody' )
3400      {
3401      }
3402  
3403  
3404      /**
3405       * Template function: display MSN of the user
3406       * @deprecated
3407       */
3408  	function msn( $format = 'htmlbody' )
3409      {
3410      }
3411  
3412      // }}}
3413  
3414  
3415  	function has_avatar()
3416      {
3417          global $Settings;
3418  
3419          return ( !empty( $this->avatar_file_ID ) && $Settings->get('allow_avatars') );
3420      }
3421  
3422  
3423      /**
3424       * Get {@link File} object of the user's avatar.
3425       *
3426       * @return File This may be NULL.
3427       */
3428      function & get_avatar_File()
3429      {
3430          $File = NULL;
3431  
3432          if( $this->has_avatar() )
3433          {
3434              $FileCache = & get_FileCache();
3435  
3436              // Do not halt on error. A file can disappear without the profile being updated.
3437              /**
3438               * @var File
3439               */
3440              $File = & $FileCache->get_by_ID( $this->avatar_file_ID, false, false );
3441          }
3442  
3443          return $File;
3444      }
3445  
3446      /**
3447       * Get array of {@link File} objects of the previously uploaded avatars.
3448       *
3449       * @param boolean TRUE - to exclude main picture from list
3450       * @param boolean TRUE - to ignore the settings and get the avatars anyway
3451       * @return array of Files.
3452       */
3453  	function get_avatar_Files( $exclude_main_picture = true, $ignore_settings = false )
3454      {
3455          global $Settings;
3456  
3457          $avatar_Files = array();
3458  
3459          if( !$ignore_settings && !( $Settings->get('upload_enabled') && $Settings->get( 'fm_enable_roots_user' ) ) )
3460          { // Upload is not enabled and we have no permission to use it...
3461              return $avatar_Files;
3462          }
3463  
3464          $LinkOwner = new LinkUser( $this );
3465          foreach( $LinkOwner->get_Links() as $user_Link )
3466          {
3467              $l_File = & $user_Link->get_File();
3468              if( $l_File->is_image() )
3469              {
3470                  if( $exclude_main_picture && $l_File->ID == $this->avatar_file_ID )
3471                  { // Exclude the main picture from list of other pictures
3472                      continue;
3473                  }
3474                  $avatar_Files[] = $l_File;
3475              }
3476          }
3477  
3478          return $avatar_Files;
3479      }
3480  
3481  
3482      /**
3483       * Get avatar IMG tag.
3484       *
3485       * @param string size
3486       * @param string class
3487       * @param string align
3488       * @param boolean true if the avatar image should be zoomed on click, false otherwise
3489       * @param string avatar overlay text
3490       * @param string group name for lightbox plugin
3491       * @return string
3492       */
3493  	function get_avatar_imgtag( $size = 'crop-top-64x64', $class = 'avatar', $align = '', $zoomable = false, $avatar_overlay_text = '', $lightbox_group = '' )
3494      {
3495          global $current_User;
3496  
3497          /**
3498           * @var File
3499           */
3500          if( ! $File = & $this->get_avatar_File() )
3501          {    // User doesn't have an avatar
3502              return get_avatar_imgtag_default( $size, $class, $align, array( 'email' => $this->get( 'email' ) ) );
3503          }
3504  
3505          if( ( !$this->check_status( 'can_display_avatar' ) ) && !( is_admin_page() && is_logged_in( false ) && ( $current_User->check_perm( 'users', 'edit' ) ) ) )
3506          { // if the user status doesn't allow to display avatar and current User is not an admin in admin interface, then show default avatar
3507              return get_avatar_imgtag_default( $size, $class, $align, array( 'email' => $this->get( 'email' ) ) );
3508          }
3509  
3510          if( $zoomable )
3511          {    // return clickable avatar tag, zoom on click
3512              if( is_logged_in() )
3513              {    // Only logged in users can see a big picture of the avatar
3514                  // set random value to link_rel, this way the pictures on the page won't be grouped
3515                  // this is usefull because the same avatar picture may appear more times in the same page
3516                  if( empty( $lightbox_group ) )
3517                  {
3518                      $link_rel = 'lightbox[f'.$File->ID.rand(0, 100000).']';
3519                  }
3520                  else
3521                  {
3522                      $link_rel = 'lightbox['.$lightbox_group.']';
3523                  }
3524                  $r = $File->get_tag( '', '', '', '', $size, 'original', $this->login, $link_rel, $class, $align );
3525              }
3526              else
3527              {    // Anonymous user get an avatar picture with link to login page
3528                  global $Blog;
3529                  $redirect_to = '';
3530                  if( isset( $Blog ) )
3531                  {    // Redirect user after login
3532                      $redirect_to = url_add_param( $Blog->gen_blogurl(), 'disp=user&user_ID='.$this->ID, '&' );
3533                  }
3534                  $r = '<a href="'.get_login_url( 'cannot see avatar', $redirect_to ) .'">'.$File->get_thumb_imgtag( $size, $class, $align ).'</a>';
3535              }
3536          }
3537          else
3538          {
3539              $r = $File->get_thumb_imgtag( $size, $class, $align );
3540          }
3541  
3542          if( $r != '' && $avatar_overlay_text != '' )
3543          {    // Add overlay text if it is enabled
3544              $r = $this->get_avatar_overlay_text( $r, $size, $avatar_overlay_text, $class );
3545          }
3546  
3547          return $r;
3548      }
3549  
3550  
3551      /**
3552       * Get overlay text for avatar img tag
3553       *
3554       * @param img tag
3555       * @param avatar size
3556       * @param avatar overlay text
3557       * @param string class
3558       * @return html string, img tag with overlay text
3559       */
3560  	function get_avatar_overlay_text( $img_tag, $size, $avatar_overlay_text, $class = '' )
3561      {
3562          preg_match( '/ width="(\d+)" height="(\d+)" /i', $img_tag, $img_sizes );
3563          if( count( $img_sizes ) == 3 )
3564          {    // img tag has a defined width & height
3565              $width = $img_sizes[1];
3566              $height = $img_sizes[2];
3567          }
3568          else
3569          {    // We try to get a sizes from config
3570              global $thumbnail_sizes;
3571              if( isset( $thumbnail_sizes[$size] ) )
3572              {    // Set a sizes
3573                  $width = $thumbnail_sizes[$size][1];
3574                  $height = $thumbnail_sizes[$size][2];
3575              }
3576          }
3577  
3578          if( empty( $width ) || empty( $height ) )
3579          {    // If sizes is not defined we cannot calculate a font-size for an overlay text
3580              return $img_tag;
3581          }
3582  
3583          $overlay_lines = explode( "\n", str_replace( "\r", '', $avatar_overlay_text ) );
3584          $max_line_length = 0;
3585          foreach( $overlay_lines as $line )
3586          {    // Find the most long line of the overlay text
3587              if( $max_line_length < strlen($line) )
3588              {    // Get max long line
3589                  $max_line_length = strlen($line);
3590              }
3591          }
3592          if( $max_line_length > 0 )
3593          {    // Display an overlay text if max length is defined
3594              // Calculate approximate font size, 1.7 - is custom coefficient of the font
3595              $font_size = ceil( ( $width / $max_line_length ) * 1.7 );
3596              // Set line-height for centering text by vertical
3597              $line_height = ceil( ( $height / count( $overlay_lines ) ) * 0.82 );
3598              // Padding-top give us a vertical centering
3599              $padding_top = ceil( $line_height * 0.32 );
3600  
3601              $tag_is_linked = false;
3602              if( strpos( $img_tag, '</a>' ) !== false )
3603              {    // img_tag is located inside tag <a>, we should to remove a end of the tag and then add
3604                  $img_tag = str_replace( '</a>', '', $img_tag );
3605                  $tag_is_linked = true;
3606              }
3607              $img_tag = '<div class="bubletip_overlay_text '.$class.'">'.
3608                      $img_tag.
3609                      '<div style="font-size:'.$font_size.'px;line-height:'.$line_height.'px;padding-top:'.$padding_top.'px">'.
3610                          nl2br($avatar_overlay_text).
3611                      '</div>';
3612              if( $tag_is_linked )
3613              {    // Add end of the tag which is removed above
3614                  $img_tag .= '</a>';
3615              }
3616              $img_tag .= '</div>';
3617          }
3618  
3619          return $img_tag;
3620      }
3621  
3622  
3623      /**
3624       * Get styled avatar
3625       *
3626       * @param array params
3627       * @return string
3628       */
3629  	function get_avatar_styled( $params = array() )
3630      {
3631          global $thumbnail_sizes;
3632  
3633          $params = array_merge( array(
3634                  'block_class'  => 'avatar_rounded',
3635                  'size'         => 'crop-top-64x64',
3636                  'avatar_class' => 'avatar',
3637                  'zoomable'     => false,
3638                  'overlay_text' => '',
3639                  'show_login'   => true,
3640                  'bubbletip'    => true,
3641              ), $params );
3642  
3643          $bubbletip_param = '';
3644          if( $params['bubbletip'] )
3645          {    // Init bubbletip param
3646              $bubbletip_param = 'rel="bubbletip_user_'.$this->ID.'"';
3647          }
3648  
3649          $style_width = '';
3650          if( isset( $thumbnail_sizes[$params['size']] ) )
3651          {
3652              $style_width = ' style="width:'.$thumbnail_sizes[$params['size']][1].'px"';
3653          }
3654  
3655          $identity_url = get_user_identity_url( $this->ID );
3656  
3657          if( !empty( $identity_url ) )
3658          {
3659              $r = '<a href="'.$identity_url.'" class="'.$params['block_class'].'"'.$bubbletip_param.$style_width.'>';
3660          }
3661          else
3662          {
3663              $r = '<div class="'.$params['block_class'].'"'.$bubbletip_param.$style_width.'>';
3664          }
3665  
3666          $r .= $this->get_avatar_imgtag( $params['size'], $params['avatar_class'], '', $params['zoomable'], $params['overlay_text'] );
3667  
3668          if( $params['show_login'] )
3669          {    // Display user name
3670              $r .= $this->get_colored_login();
3671          }
3672  
3673          $r .= !empty( $identity_url ) ? '</a>' : '</div>';
3674  
3675          return $r;
3676      }
3677  
3678  
3679      /**
3680       * Add a user field
3681       */
3682  	function userfield_add( $type, $val )
3683      {
3684          global $DB;
3685          $this->new_fields[] = $type.', '.$DB->quote( $val );
3686      }
3687  
3688  
3689      /**
3690       * Update an user field. Empty fields will be deleted on dbupdate.
3691       */
3692  	function userfield_update( $uf_ID, $val )
3693      {
3694          global $DB;
3695          $this->updated_fields[$uf_ID] = $val;
3696          // pre_dump( $uf_ID, $val);
3697      }
3698  
3699  
3700      /**
3701       * Load userfields
3702       */
3703  	function userfields_load()
3704      {
3705          global $DB;
3706  
3707          $userfields = $DB->get_results( '
3708              SELECT uf_ID, ufdf_ID, uf_varchar, ufdf_duplicated, ufdf_type, ufdf_name, ufgp_ID, ufgp_name
3709                  FROM T_users__fields
3710                      LEFT JOIN T_users__fielddefs ON uf_ufdf_ID = ufdf_ID
3711                      LEFT JOIN T_users__fieldgroups ON ufdf_ufgp_ID = ufgp_ID
3712              WHERE uf_user_ID = '.$this->ID.'
3713                  AND ufdf_required != "hidden"
3714              ORDER BY ufgp_order, ufdf_order, uf_ID' );
3715  
3716          $userfield_lists = array();
3717          foreach( $userfields as $userfield )
3718          {
3719              if( $userfield->ufdf_duplicated == 'list' )
3720              {    // Prepare a values of list into one array
3721                  if( !isset( $userfield_lists[$userfield->ufdf_ID] ) )
3722                  {    // Init array
3723                      $userfield_lists[$userfield->ufdf_ID] = array();
3724                  }
3725                  userfield_prepare( $userfield );
3726                  $userfield_lists[$userfield->ufdf_ID][] = $userfield->uf_varchar;
3727              }
3728          }
3729  
3730          foreach( $userfields as $userfield )
3731          {
3732              if( $userfield->ufdf_duplicated == 'list' )
3733              {    // List style
3734                  if( isset( $userfield_lists[$userfield->ufdf_ID] ) )
3735                  {    // Save all data for this field:
3736                      $userfield->uf_varchar = implode( ', ', $userfield_lists[$userfield->ufdf_ID] );
3737                      $this->userfields[$userfield->uf_ID] = $userfield;
3738                      // Unset array to avoid a duplicates
3739                      unset( $userfield_lists[$userfield->ufdf_ID] );
3740                  }
3741              }
3742              else
3743              {    // Save all data for this field:
3744                  userfield_prepare( $userfield );
3745                  $this->userfields[$userfield->uf_ID] = $userfield;
3746              }
3747              // Save index
3748              $this->userfields_by_type[$userfield->ufdf_ID][] = $userfield->uf_ID;
3749          }
3750  
3751          // Also make sure the definitions are loaded
3752          $this->userfield_defs_load();
3753      }
3754  
3755  
3756      /**
3757       * Load userfields defs
3758       */
3759  	function userfield_defs_load()
3760      {
3761          global $DB;
3762  
3763          if( !isset($this->userfield_defs) )
3764          {
3765              $userfield_defs = $DB->get_results( '
3766                  SELECT ufdf_ID, ufdf_type, ufdf_name, ufdf_required, ufdf_options, ufdf_duplicated
3767                      FROM T_users__fielddefs' );
3768  
3769              foreach( $userfield_defs as $userfield_def )
3770              {
3771                  $this->userfield_defs[$userfield_def->ufdf_ID] = array(
3772                      $userfield_def->ufdf_type,
3773                      $userfield_def->ufdf_name,
3774                      $userfield_def->ufdf_required,
3775                      $userfield_def->ufdf_options,
3776                      $userfield_def->ufdf_duplicated
3777                  ); //jamesz
3778              }
3779          }
3780      }
3781  
3782  
3783      /**
3784      * Get first field for a specific type
3785      *
3786      * @return string or NULL
3787      */
3788  	function userfieldget_first_for_type( $type_ID )
3789      {
3790          if( !isset($this->userfields_by_type[$type_ID]) )
3791          {
3792              return NULL;
3793          }
3794  
3795          $idx = $this->userfields_by_type[$type_ID][0];
3796  
3797          return $this->userfields[$idx][1];
3798      }
3799  
3800  
3801      /**
3802       * Update user data from Request form fields.
3803       *
3804       * @param boolean is new user
3805       * @return mixed true on success, allowed action otherwise
3806       */
3807  	function update_from_request( $is_new_user = false )
3808      {
3809          global $current_User, $DB, $Messages, $UserSettings, $Settings, $blog, $admin_url;
3810  
3811          if( !$current_User->check_perm( 'users', 'edit' ) && $this->ID != $current_User->ID )
3812          { // user is only allowed to update him/herself
3813              $Messages->add( T_('You are only allowed to update your own profile!') );
3814              return 'view';
3815          }
3816  
3817          // check if is admin form
3818          $is_admin_form = param( 'admin_form', 'boolean', false );
3819  
3820          // memorize user status ( activated or not )
3821          $user_was_activated = $this->check_status( 'is_validated' );
3822          $user_old_email = $this->email;
3823  
3824          // memorize user old login, status and root path, before update
3825          $user_old_login = $this->login;
3826          $user_root_path = NULL;
3827          $FileRootCache = & get_FileRootCache();
3828          if( !$is_new_user )
3829          {
3830              $user_FileRoot = & $FileRootCache->get_by_type_and_ID( 'user', $this->ID );
3831              if( $user_FileRoot && file_exists( $user_FileRoot->ads_path ) )
3832              {
3833                  $user_root_path = $user_FileRoot->ads_path;
3834              }
3835          }
3836  
3837          // load data from request
3838          if( !$this->load_from_Request() )
3839          {    // We have found validation errors:
3840              if( $is_new_user || ( $is_admin_form && ( $this->ID != 1 ) ) )
3841              { // update user status settings but not save
3842                  $this->update_status_from_Request( false );
3843              }
3844              return 'edit';
3845          }
3846  
3847          // Update user
3848          $DB->begin();
3849  
3850          $is_password_form = param( 'password_form', 'boolean', false );
3851          if( $this->dbsave() )
3852          {
3853              $update_success = true;
3854              if( $is_new_user )
3855              {
3856                  $Messages->add( T_('New user has been created.'), 'success' );
3857              }
3858              elseif( $is_password_form )
3859              {
3860                  $Messages->add( T_('Password has been changed.'), 'success' );
3861              }
3862              else
3863              {
3864                  if( $user_old_login != $this->login && $user_root_path != NULL )
3865                  { // user login changed and user has a root directory (another way $user_root_path value would be NULL)
3866                      $FileRootCache->clear();
3867                      $user_FileRoot = & $FileRootCache->get_by_type_and_ID( 'user', $this->ID );
3868                      if( $user_FileRoot )
3869                      { // user FilerRooot exists, rename user root folder
3870                          if( ! @rename( $user_root_path, $user_FileRoot->ads_path ) )
3871                          { // unsuccessful folder rename
3872                              $Messages->add( sprintf( T_('You cannot choose the new login "%s" (cannot rename user fileroot)'), $this->login), 'error' );
3873                              $update_success = false;
3874                          }
3875                      }
3876                  }
3877                  if( $update_success )
3878                  {
3879                      $Messages->add( T_('Profile has been updated.'), 'success' );
3880                  }
3881              }
3882  
3883              if( $update_success )
3884              {
3885                  $user_field_url = $this->get_field_url( true );
3886                  if( $user_field_url != '' )
3887                  {    // Update url from extra user fields
3888                      $this->set( 'url', $user_field_url );
3889                      $this->dbsave();
3890                  }
3891  
3892                  $DB->commit();
3893              }
3894              else
3895              {
3896                  $DB->rollback();
3897              }
3898          }
3899          else
3900          {
3901              $DB->rollback();
3902              $update_success = false;
3903              $Messages->add( 'New user creation error', 'error' );
3904          }
3905  
3906          // Update user status settings
3907          if( ( $is_new_user || ( $is_admin_form && $this->ID != 1 ) ) && ( ! $this->update_status_from_Request( true ) ) )
3908          {
3909              $Messages->add( T_( 'User status couldn\'t be updated!' ), 'error' );
3910          }
3911  
3912          $try_to_send_validate_email = ( $is_new_user || $user_was_activated || ( $user_old_email != $this->email ) );
3913          if( $update_success && $try_to_send_validate_email && $this->check_status( 'can_be_validated' ) )
3914          { // user was deactivated somehow ( or it was just created right now ), check if we need to send validation email
3915              if( ( $Settings->get( 'validation_process' ) != 'easy' ) && ( $current_User->ID != $this->ID ) )
3916              { // validation process is set to secure, we may send activation email only if this user is the current_User
3917                  $reg_settings_url = 'href="'.url_add_param( $admin_url, 'ctrl=registration' ).'#fieldset_wrapper_account_activation"';
3918                  $Messages->add( sprintf( T_( 'Because the Account Activation Process is set to <a %s>Secure</a>, the user will have to request a new activation email next time he tries to log in.' ), $reg_settings_url ), 'note' );
3919              }
3920              else
3921              { // validation process is easy, send email with permanent activation link
3922                  if( $this->send_validate_email( NULL, $blog, ( $user_old_email != $this->email ) ) )
3923                  {
3924                      if( $current_User->ID == $this->ID )
3925                      {
3926                          $redirect_to = NULL;
3927                          if( ( !is_admin_page() ) && ( !empty( $blog ) ) )
3928                          { // set where to redirect in front office, another way it would go to profile_update.php
3929                              $BlogCache = & get_BlogCache();
3930                              $Blog = $BlogCache->get_by_ID( $blog, false );
3931                              $redirect_to = rawurlencode( url_add_param( $Blog->gen_blogurl(), 'disp=userprefs' ) );
3932                          }
3933                          $activate_info_link = 'href="'.get_activate_info_url( $redirect_to ).'"';
3934                          $Messages->add( sprintf( T_('An email has been sent to your email address (%s). Please click on the link therein to activate your account. <a %s>More info &raquo;</a>' ), $this->dget('email'), $activate_info_link ), 'success' );
3935                      }
3936                      else
3937                      {
3938                          $Messages->add( sprintf( T_('An account activation email has been sent to %s' ), $this->login ), 'success' );
3939                      }
3940                  }
3941                  elseif( isset( $demo_mode ) && $demo_mode )
3942                  {
3943                      $Messages->add( 'Could not send activation email. Sending emails is disabled in demo mode.', 'note' );
3944                  }
3945              }
3946          }
3947  
3948          // Update user settings:
3949          $is_preferences_form = param( 'preferences_form', 'boolean', false );
3950          $is_subscriptions_form = param( 'subscriptions_form', 'boolean', false );
3951          if( $is_preferences_form || $is_subscriptions_form || $is_admin_form )
3952          { // Update UserSettings
3953              if( $UserSettings->dbupdate() && ( $is_preferences_form || $is_subscriptions_form ) )
3954              { // Show feature settings update successful message on user preferences form
3955                  $Messages->add( T_('User feature settings have been changed.'), 'success');
3956              }
3957          }
3958  
3959          return true;
3960      }
3961  
3962  
3963      /**
3964       * Handle close user account request and update account close reason
3965       *
3966       * @param boolean save modifications or not. User false only in case when User and UserSettings object will be saved later.
3967       * @return boolean true on success, false otherwise
3968       */
3969  	function update_status_from_Request( $dbsave, $new_status = NULL )
3970      {
3971          global $DB, $UserSettings, $current_User, $servertimenow;
3972  
3973          if( $dbsave )
3974          { // save required
3975              $DB->begin();
3976          }
3977  
3978          if( empty( $new_status ) )
3979          {
3980              $new_status = param( 'edited_user_status', 'string', true );
3981          }
3982  
3983          // get close reason text - max 255 characters
3984          $account_close_reason = substr( param( 'account_close_reason', 'text', '' ), 0, 255 );
3985          if( ( !$this->check_status( 'is_closed' ) ) && ( $new_status == 'closed' ) )
3986          { // account was not closed yet
3987              if( empty( $account_close_reason ) )
3988              {
3989                  $account_close_reason = sprintf( T_( 'Account closed by %s' ), $current_User->get( 'login' ) );
3990              }
3991              $this->set( 'status', 'closed' );
3992              $UserSettings->set( 'account_close_ts', $servertimenow, $this->ID );
3993              $UserSettings->set( 'account_close_reason', $account_close_reason, $this->ID );
3994              // delete last activation email data, this user must not be allowed to reactivate the account ( only admin users may change the status again )
3995              $UserSettings->delete( 'last_activation_reminder_key', $this->ID );
3996              $UserSettings->delete( 'last_activation_email', $this->ID );
3997              // create query to clear all session's of the user
3998              $clear_sessions_query = 'UPDATE T_sessions
3999                                  SET sess_key = NULL
4000                                  WHERE sess_user_ID = '.$DB->quote( $this->ID );
4001              if( $dbsave && $this->dbupdate() && $UserSettings->dbupdate() && ( $DB->query( $clear_sessions_query ) !== false ) )
4002              { // all db modification was successful
4003                  $DB->commit();
4004                  if( $current_User->ID != $this->ID )
4005                  {    // If admin closed some user account
4006                      // Send notification email about closed account to users with edit users permission
4007                      $email_template_params = array(
4008                              'login'   => $this->login,
4009                              'email'   => $this->email,
4010                              'reason'  => $account_close_reason,
4011                              'user_ID' => $this->ID,
4012                              'closed_by_admin' => $current_User->login,
4013                          );
4014                      send_admin_notification( NT_('User account closed'), 'account_closed', $email_template_params );
4015                  }
4016                  return true;
4017              }
4018          }
4019          else
4020          {
4021              $new_status_is_active = ( $new_status == 'activated' || $new_status == 'autoactivated' );
4022              $old_status_is_not_active = false;
4023              if( $this->check_status( 'can_be_validated' ) && $new_status_is_active )
4024              { // User was activated
4025                  $old_status_is_not_active = true;
4026                  // clear activation specific settings
4027                  $UserSettings->delete( 'last_activation_reminder_key', $this->ID );
4028                  $UserSettings->delete( 'last_activation_email', $this->ID );
4029                  $UserSettings->delete( 'activation_reminder_count', $this->ID );
4030                  $UserSettings->delete( 'send_activation_reminder', $this->ID );
4031              }
4032              $old_status_is_not_active = ( $old_status_is_not_active || $this->check_status( 'is_closed' ) );
4033  
4034              // set status
4035              $this->set( 'status', $new_status );
4036              $UserSettings->set( 'account_close_reason', $account_close_reason, $this->ID );
4037              if( $dbsave && $this->dbupdate() )
4038              { // db update
4039                  $UserSettings->dbupdate();
4040                  $DB->commit();
4041                  if( $old_status_is_not_active && $new_status_is_active )
4042                  { // User was activated, create a welcome private message
4043                      $this->send_welcome_message();
4044  
4045                      if( $current_User->ID != $this->ID )
4046                      {    // If admin activated some user account
4047                          // Send notification email about activated account to users with edit users permission
4048                          $email_template_params = array(
4049                              'User' => $this,
4050                              'login' => $this->login, // this is required in the send_admin_notification
4051                              'activated_by_admin' => $current_User->login,
4052                          );
4053                          send_admin_notification( NT_('New user account activated'), 'account_activated', $email_template_params );
4054                      }
4055                  }
4056                  return true;
4057              }
4058          }
4059  
4060          if( $dbsave )
4061          { // save was required, but wasn't successful
4062              $DB->rollback();
4063              return false;
4064          }
4065  
4066          return true;
4067      }
4068  
4069  
4070      /**
4071       * Update profileupdate date. Call after a publicly visible user property was updated.
4072       */
4073  	function set_profileupdate_date()
4074      {
4075          global $current_User, $localtimenow;
4076          if( ( !empty( $current_User ) ) && ( $this->ID == $current_User->ID ) )
4077          {
4078              $this->set( 'profileupdate_date', date( 'Y-m-d', $localtimenow ) );
4079          }
4080      }
4081  
4082  
4083      /**
4084       * Update user avatar file
4085       *
4086       * @param integer the new avatar file ID
4087       * @return mixed true on success, allowed action otherwise
4088       */
4089  	function update_avatar( $file_ID )
4090      {
4091          global $current_User, $Messages;
4092  
4093          if( !$current_User->check_perm( 'users', 'edit' ) && $this->ID != $current_User->ID )
4094          { // user is only allowed to update him/herself
4095              $Messages->add( T_('You are only allowed to update your own profile!'), 'error' );
4096              return 'view';
4097          }
4098  
4099          if( $file_ID == NULL )
4100          {
4101              $Messages->add( T_('Your profile picture could not be changed!'), 'error' );
4102              return 'edit';
4103          }
4104  
4105          $FileCache = & get_FileCache();
4106          $File = $FileCache->get_by_ID( $file_ID );
4107          if( $File->_FileRoot->type == 'user' && $File->_FileRoot->in_type_ID != $this->ID )
4108          { // don't allow to use pictures from other users
4109              $Messages->add( T_('Your profile picture could not be changed!'), 'error' );
4110              return 'edit';
4111          }
4112  
4113          $this->set( 'avatar_file_ID', $file_ID, true );
4114          // update profileupdate_date, because a publicly visible user property was changed
4115          $this->set_profileupdate_date();
4116          $this->dbupdate();
4117  
4118          $Messages->add( T_('Your profile picture has been changed.'), 'success' );
4119          return true;
4120      }
4121  
4122  
4123      /**
4124       * Get the rotate avatar icons
4125       *
4126       * @param integer File ID
4127       * @param array Params
4128       * @return string HTML text with 3 icons to rotate avatar
4129       */
4130  	function get_rotate_avatar_icons( $file_ID, $params = array() )
4131      {
4132          // Make sure we are not missing any param:
4133          $params = array_merge( array(
4134                  'before' => '<br />',
4135                  'after' => '',
4136              ), $params );
4137  
4138          // Init links to rotate avatar
4139          if( is_admin_page() )
4140          {    // Back-office
4141              $url_rotate_90_left = regenerate_url( '', 'user_tab=avatar&user_ID='.$this->ID.'&action=rotate_avatar_90_left&file_ID='.$file_ID.'&'.url_crumb('user'), '', '&');
4142              $url_rotate_180 = regenerate_url( '', 'user_tab=avatar&user_ID='.$this->ID.'&action=rotate_avatar_180&file_ID='.$file_ID.'&'.url_crumb('user'), '', '&');
4143              $url_rotate_90_right = regenerate_url( '', 'user_tab=avatar&user_ID='.$this->ID.'&action=rotate_avatar_90_right&file_ID='.$file_ID.'&'.url_crumb('user'), '', '&');
4144          }
4145          else
4146          {    // Front-office
4147              global $Blog;
4148              $url_rotate_90_left = get_secure_htsrv_url().'profile_update.php?user_tab=avatar&blog='.$Blog->ID.'&user_ID='.$this->ID.'&action=rotate_avatar_90_left&file_ID='.$file_ID.'&'.url_crumb('user');
4149              $url_rotate_180 = get_secure_htsrv_url().'profile_update.php?user_tab=avatar&blog='.$Blog->ID.'&user_ID='.$this->ID.'&action=rotate_avatar_180&file_ID='.$file_ID.'&'.url_crumb('user');
4150              $url_rotate_90_right = get_secure_htsrv_url().'profile_update.php?user_tab=avatar&blog='.$Blog->ID.'&user_ID='.$this->ID.'&action=rotate_avatar_90_right&file_ID='.$file_ID.'&'.url_crumb('user');
4151          }
4152  
4153          $html = $params['before'];
4154  
4155          $html .= action_icon( T_('Rotate this picture 90&deg; to the left'), 'rotate_left', $url_rotate_90_left, '', 0, 0, array( 'style' => 'margin-right:4px' ) );
4156          $html .= action_icon( T_('Rotate this picture 180&deg;'), 'rotate_180', $url_rotate_180, '', 0, 0, array( 'style' => 'margin-right:4px' ) );
4157          $html .= action_icon( T_('Rotate this picture 90&deg; to the right'), 'rotate_right', $url_rotate_90_right, '', 0, 0 );
4158  
4159          $html .= $params['after'];
4160  
4161          return $html;
4162      }
4163  
4164  
4165      /**
4166       * Rotate user avatar file
4167       *
4168       * @param integer the new avatar file ID
4169       * @return mixed TRUE on success;
4170       *               Error code on denied action:
4171       *                 'only_own_profile' - User can update only own profile
4172       *                 'wrong_file'       - Request with wrong file ID
4173       *                 'other_user'       - Restricted to edit files from other users
4174       *                 'rotate_error'     - Some errors in rotate function
4175       */
4176  	function rotate_avatar( $file_ID, $degrees )
4177      {
4178          global $current_User, $Messages;
4179  
4180          if( !$current_User->check_perm( 'users', 'edit' ) && $this->ID != $current_User->ID )
4181          { // user is only allowed to update him/herself
4182              $Messages->add( T_('You are only allowed to update your own profile!'), 'error' );
4183              return 'only_own_profile';
4184          }
4185  
4186          if( $file_ID == NULL )
4187          {
4188              $Messages->add( T_('Your profile picture could not be rotated!'), 'error' );
4189              return 'wrong_file';
4190          }
4191  
4192          $FileCache = & get_FileCache();
4193          if( !$File = $FileCache->get_by_ID( $file_ID, false ) )
4194          {    // File does't exist
4195              $Messages->add( T_('Your profile picture could not be rotated!'), 'error' );
4196              return 'wrong_file';
4197          }
4198  
4199          if( $File->_FileRoot->type != 'user' || $File->_FileRoot->in_type_ID != $this->ID )
4200          {    // don't allow use the pictures from other users
4201              $Messages->add( T_('Your profile picture could not be rotated!'), 'error' );
4202              return 'other_user';
4203          }
4204  
4205          load_funcs( 'files/model/_image.funcs.php' );
4206  
4207          if( !rotate_image( $File, $degrees ) )
4208          {    // Some errors were during rotate the avatar
4209              $Messages->add( T_('Your profile picture could not be rotated!'), 'error' );
4210              return 'rotate_error';
4211          }
4212  
4213          $Messages->add( T_('Your profile picture has been rotated.'), 'success' );
4214          return true;
4215      }
4216  
4217  
4218      /**
4219       * Remove user avatar
4220       *
4221       * @return mixed true on success, false otherwise
4222       */
4223  	function remove_avatar()
4224      {
4225          global $current_User, $Messages;
4226  
4227          if( !$current_User->check_perm( 'users', 'edit' ) && $this->ID != $current_User->ID )
4228          { // user is only allowed to update him/herself
4229