code:
<?php
/**
 * @package Habari
 *
 */

/**
 * Habari Themes class
 *
 */
class Themes
{
	private static $all_themes = null;
	private static $all_data = null;

	/**
	 * Returns the theme dir and path information
	 * @return array An array of Theme data
	 **/
	public static function get_all()
	{
		if ( !isset( self::$all_themes ) ) {
			$dirs = array( HABARI_PATH . '/system/themes/*' , HABARI_PATH . '/3rdparty/themes/*', HABARI_PATH . '/user/themes/*');
			if( Site::is('multi') ) {
				$dirs[] = Site::get_dir('config') . '/themes/*';
			}
			$themes = array();
			foreach($dirs as $dir) {
				$themes = array_merge( $themes, Utils::glob( $dir, GLOB_ONLYDIR | GLOB_MARK ) );
			}

			$themes = array_filter( $themes, create_function('$a', 'return file_exists($a . "/theme.xml");') );
			$themefiles = array_map('basename', $themes);
			self::$all_themes = array_combine($themefiles, $themes);
		}
		return self::$all_themes;
	}

	/**
	 * Returns all theme information -- dir, path, theme.xml, screenshot url
	 * @return array An array of Theme data
	 **/
	public static function get_all_data()
	{
		if ( !isset( self::$all_data ) ) {
			$themedata = array();
			foreach(self::get_all() as $theme_dir => $theme_path ) {
				$themedata['dir'] = $theme_dir;
				$themedata['path'] = $theme_path;

				$themedata['info'] = simplexml_load_file( $theme_path . '/theme.xml' );
				if ( $themedata['info']->getName() != 'pluggable' || (string) $themedata['info']->attributes()->type != 'theme' ) {
				    $themedata['screenshot'] = Site::get_url( 'admin_theme' ) . "/images/screenshot_default.png";
				    $themedata['info']->description = '' . _t( 'This theme is a legacy theme that is not compatible with Habari ' ) . Version::get_habariversion() . '.

Please update your theme.';
				    $themedata['info']->license = '';
				} else {
				    if ( $screenshot = Utils::glob( $theme_path . '/screenshot.{png,jpg,gif}' , GLOB_BRACE) ) {
					    $themedata['screenshot'] = Site::get_url( 'habari' ) . dirname(str_replace( HABARI_PATH, '', $theme_path )) . '/' . basename( $theme_path ) . "/" . basename(reset($screenshot));
				    }
				    else {
					    $themedata['screenshot'] = Site::get_url( 'admin_theme' ) . "/images/screenshot_default.png";
				    }
				}

				self::$all_data[$theme_dir] = $themedata;
			}
		}
		return self::$all_data;
	}

	/**
	 * Returns the name of the active or previewed theme
	 *
	 * @params boolean $nopreview If true, return the real active theme, not the preview
	 * @return string the current theme or previewed theme's directory name
	 */
	public static function get_theme_dir($nopreview = false)
	{
		if(!$nopreview && isset($_SESSION['user_theme_dir'])) {
			$theme_dir = $_SESSION['user_theme_dir'];
		}
		else {
			$theme_dir = Options::get('theme_dir');
		}
		$theme_dir = Plugins::filter('get_theme_dir', $theme_dir);
		return $theme_dir;
	}

	/**
	 * Returns the active theme's full directory path.
	 * @params boolean $nopreview If true, return the real active theme, not the preview
	 * @return string The full path to the active theme directory
	 */
	private static function get_active_theme_dir($nopreview = false)
	{
		$theme_dir = self::get_theme_dir($nopreview);
		$themes = Themes::get_all();

		if (!isset($themes[$theme_dir]))
		{
			$theme_exists = false;
			foreach ($themes as $themedir) {
				if (file_exists(Utils::end_in_slash($themedir) . 'theme.xml')) {
					$theme_dir = basename($themedir);
					Options::set('theme_dir', basename($themedir));
					$theme_exists = true;
					break;
				}
			}
			if (!$theme_exists) {
				die( _t('There is no valid theme currently installed.') );
			}
		}
		return $themes[$theme_dir];
	}

	/**
	 * Returns the active theme information from the database
	 * @params boolean $nopreview If true, return the real active theme, not the preview
	 * @return array An array of Theme data
	 **/
	public static function get_active($nopreview = false)
	{
		$theme = new QueryRecord();
		$theme->theme_dir = Themes::get_active_theme_dir($nopreview);

		$data = simplexml_load_file( Utils::end_in_slash($theme->theme_dir) . 'theme.xml' );
		foreach ( $data as $name=>$value) {
			$theme->$name = (string) $value;
		}
		return $theme;
	}

	/**
	 * Returns theme information for the active theme -- dir, path, theme.xml, screenshot url
	 * @params boolean $nopreview If true, return the real active theme, not the preview
	 * @return array An array of Theme data
	 */
	public static function get_active_data($nopreview = false)
	{
		$all_data = Themes::get_all_data();
		$active_theme_dir = basename(Themes::get_active_theme_dir($nopreview));
		$active_data = $all_data[$active_theme_dir];
		return $active_data;
	}

	/**
	 * function activate_theme
	 * Updates the database with the name of the new theme to use
	 * @param string the name of the theme
	**/
	public static function activate_theme( $theme_name, $theme_dir )
	{
		Options::set( 'theme_name', $theme_name );
		Options::set( 'theme_dir', $theme_dir );
		EventLog::log( _t( 'Activated Theme: %s', array( $theme_name ) ), 'notice', 'theme', 'habari' );
	}

	/**
	 * Sets a theme to be the current user's preview theme
	 *
	 * @param string $theme_name The name of the theme to preview
	 * @param string $theme_dir The directory of the theme to preview
	 */
	public static function preview_theme( $theme_name, $theme_dir )
	{
		$_SESSION['user_theme_name'] = $theme_name;
		$_SESSION['user_theme_dir'] = $theme_dir;
		EventLog::log( _t( 'Previewed Theme: %s', array( $theme_name ) ), 'notice', 'theme', 'habari' );
	}

	/**
	 * Cancel the viewing of any preview theme
	 */
	public static function cancel_preview()
	{
		if(isset($_SESSION['user_theme_name'])) {
			EventLog::log( _t( 'Canceled Theme Preview: %s', array( $_SESSION['user_theme_name'] ) ), 'notice', 'theme', 'habari' );
			unset($_SESSION['user_theme_name']);
			unset($_SESSION['user_theme_dir']);
		}
	}

	/**
	 * Returns a named Theme descendant.
	 * If no parameter is supplied, then
	 * load the active theme from the database.
	 *
	 * If no theme option is set, a fatal error is thrown
	 *
	 * @param name            ( optional ) override the default theme lookup
	 * @param template_engine ( optional ) specify a template engine
	 * @param theme_dir       ( optional ) specify a theme directory
	 **/
	public static function create( $name = '', $template_engine = '', $theme_dir = '' )
	{
		if ( $name != '' ) {
			/*
			 * A theme name ( or more information ) was supplied.  This happens when we
			 * want to use a pre-installed theme ( for instance, the
			 * installer theme. )
			 */
			if ( $template_engine != '' ) {
				/* we load template engine from specified args, not DB */
				$themedata = new QueryRecord();
				$themedata->name = func_get_arg( 0 );
				$themedata->template_engine = $template_engine;
				$themedata->theme_dir = $themedata->name;
				$themedata->version = 0;
				if( $theme_dir != '' ) {
					$themedata->theme_dir = $theme_dir;
				}
				else {
					$themedata->theme_dir = HABARI_PATH . '/user/themes/' . $themedata->theme_dir . '/';
				}
			}
			else {
				/* lookup in DB for template engine info. */
				$themedata = self::get_by_name( $name );
				if ( empty( $themedata ) ) {
					die( _t('Theme not installed.') );
				}
				$themedata->theme_dir = HABARI_PATH . '/user/themes/' . $themedata->theme_dir . '/';
			}
		}
		else {
			// Grab the theme from the database
			$themedata = self::get_active();
			if ( empty( $themedata ) ) {
				die( _t('Theme not installed.') );
			}
		}

		$classname = 'Theme';
		/**
		 * @todo Should we include_once a theme's theme.php file here?
		 **/
		if( file_exists( $themedata->theme_dir . 'theme.php' ) ) {
			include_once( $themedata->theme_dir . 'theme.php' );
			if( defined('THEME_CLASS') ) {
				$classname = THEME_CLASS;
			}
		}

		$created_theme = new $classname($themedata);
		Plugins::act('init_theme');
		return $created_theme;

	}
}

?>


code:
<?php
/**
 * @package Habari
 *
 */

define('UPDATE_URL', 'http://www.habariproject.org/beacon/');

/**
 * Habari Update class
 *
 * Checks for updates to Habari and its libraries
 *
 * @access public
 */
class Update extends Singleton
{
	private $beacons = array();
	private $update; // SimpleXMLElement

	/**
	 * Enables singleton working properly
	 *
	 * @see singleton.php
	 */
	protected static function instance()
	{
		return self::getInstanceOf( get_class() );
	}

	/**
	 * Add a beaconid to the list of beaconids to version-check.
	 *
	 * @param string $name the name of the component that will be checked
	 * @param string $beaconid the id of the beacon to check
	 * @param string $current_version the current version of the resource represented by this beaconid
	 */
	public static function add($name, $beaconid, $current_version)
	{
		if ( empty($name) || empty($beaconid) || empty($current_version) ) {
			throw new Exception(_t('Invalid Beacon information added'));
		}

		self::instance()->beacons[ (string) $beaconid] = array('name' => (string) $name, 'version' => (string) $current_version);
	}

	/**
	 * Return true if the beacon data contains updates from the server
	 *
	 * @param array $beacon the beacon data from the $beacons array
	 * @return boolean true if there are updates available for this beacon
	 */
	private static function filter_unchanged($beacon)
	{
		return isset($beacon['latest_version']);
	}

	/**
	 * Perform a check of all beaconids.
	 * Notifies update_check plugin hooks when checking so that they can add their beaconids to the list.
	 * @return array An array of update beacon information for components that have updates
	 */
	public static function check()
	{
		try {
			$instance = self::instance();
			if(count($instance->beacons) == 0) {
				Update::add('Habari', '7a0313be-d8e3-11db-8314-0800200c9a66', Version::get_habariversion());
				Plugins::act('update_check');
			}

			$request = new RemoteRequest(UPDATE_URL, 'POST');
			$request->set_params(
				array_map(
					create_function('$a', 'return $a["version"];'),
					$instance->beacons
				)
			);
			$request->set_timeout( 10 );
			$result = $request->execute();
			if ( Error::is_error( $result ) ) {
				throw $result;
			}
			$updatedata = $request->get_response_body();
			if ( Error::is_error( $updatedata ) ) {
				throw $updatedata;
			}
			$instance->update = new SimpleXMLElement($updatedata);
			foreach($instance->update as $beacon) {
				$beaconid = (string)$beacon['id'];
				foreach($beacon->update as $update) {
					// Do we have this beacon?  If not, don't process it.
					if(empty($instance->beacons[$beaconid])) {
						continue;
					}
					// If the remote update info version is newer...
					if( version_compare($update['version'], $instance->beacons[$beaconid]['version']) > 0 ) {
						// If this version is more recent than all other newer versions...
						if(
							empty($instance->beacons[$beaconid]['latest_version']) ||
							version_compare
							(
								(string)$update['version'],
								$instance->beacons[$beaconid]['latest_version']
							) > 0
						)
						{
							$instance->beacons[$beaconid]['latest_version'] = (string)$update['version'];
						}
						if(isset($instance->beacons[$beaconid]['severity'])) {
							$instance->beacons[$beaconid]['severity'][] = (string)$update['severity'];
							array_unique($instance->beacons[$beaconid]['severity']);
						}
						else {
							$instance->beacons[$beaconid]['severity'] = array((string)$update['severity']);
						}
						$instance->beacons[$beaconid]['url'] = (string)$beacon['url'];
						$instance->beacons[$beaconid]['changes'][(string)$update['version']] = (string)$update;
					}
				}
			}
			return array_filter($instance->beacons, array('Update', 'filter_unchanged'));
		} catch (Exception $e) {
			return $e;
		}
	}

}

?>

repo_name: DaSourcerer/habari


code:
<?php
/**
 * @package Habari
 *
 */

/**
 * URL class which handles creation of URLs based on the rewrite
 * rules in the database.  Uses rules to construct pretty URLs for use
 * by the system and especially the theme's template engine
 *
 */
class URL extends Singleton
{
	// static collection of rules ( pulled from RewriteController )
	private $rules = NULL;
	private $matched_rule = NULL;
	private static $stub = NULL;

	/**
	 * Enables singleton working properly
	 *
	 * @see singleton.php
	 */
	protected static function instance()
	{
		return self::getInstanceOf( __CLASS__ );
	}

	/**
	 * A simple caching mechanism to avoid reloading rule array
	 */
	private function load_rules()
	{
		if ( URL::instance()->rules != NULL ) {
			return;
		}
		URL::instance()->rules = RewriteRules::get_active();
	}

	/**
	 * Get the matched RewriteRule that was matched in parse().
	 *
	 * @return RewriteRule matched rule, or NULL
	 */
	public static function get_matched_rule()
	{
		return URL::instance()->matched_rule;
	}

	/**
	 * Get the active RewriteRules that are cached in self::load_rules().
	 *
	 * @return array RewriteRules active rules, or NULL
	 */
	public static function get_active_rules()
	{
		return URL::instance()->rules;
	}

	/**
	 * Cause the matched rule to be unset in the case of a 404
	 *
	 * @return RewriteRule A rewrite rule that represents a 404 error - no match on the URL requested
	 */
	public static function set_404()
	{
		if ( empty(URL::instance()->matched_rule) || (URL::instance()->matched_rule->name != 'display_404') ) {
			$rule = RewriteRules::by_name('display_404');
			URL::instance()->matched_rule = reset($rule);
			URL::instance()->matched_rule->match(self::$stub);
		}
		return URL::instance()->matched_rule;
	}

	/**
	 * Match a URL/URI against the rewrite rules stored in the DB.
	 * This method is used by the Controller class for parsing
	 * requests, and by other classes, such as Pingback, which
	 * uses it to determine the post slug for a given URL.
	 *
	 * Returns the matched RewriteRule object, or FALSE.
	 *
	 * @param string $from_url URL string to parse
	 * @return RewriteRule matched rule, or FALSE
	 */
	public static function parse( $from_url )
	{
		$base_url = Site::get_path( 'base', true );

		/*
		 * Strip out the base URL from the requested URL
		 * but only if the base URL isn't /
		 */
		if ( strpos( $from_url, $base_url ) === 0 ) {
			$from_url = MultiByte::substr( $from_url, MultiByte::strlen( $base_url ) );
		}

		/* Trim off any leading or trailing slashes */
		$from_url = trim( $from_url, '/' );

		/* Remove the querystring from the URL */
		if ( MultiByte::strpos( $from_url, '?' ) !== FALSE ) {
			list( $from_url, )= explode( '?', $from_url );
		}

		$url = URL::instance();
		$url->load_rules(); // Cached in singleton

		/*
		 * Run the stub through the regex matcher
		 */
		$pattern_matches = array();
		self::$stub = $from_url;
		foreach ( $url->rules as $rule ) {
			if ( $rule->match( $from_url ) ) {
				$url->matched_rule = $rule;
				/* Stop processing at first matched rule... */
				return $rule;
			}
		}

		return FALSE;
	}

	/**
	 * Builds the required pretty URL given a supplied
	 * rule name and a set of placeholder replacement
	 * values and returns the built URL.
	 *
	 * <code>
	 * URL::get( 'display_entries_by_date', array(
	 * 	'year' => '2000',
	 * 	'month' => '05',
	 * 	'day' => '01',
	 * ) );
	 * </code>
	 *
	 * @param mixed $rule_names string name of the rule or array of rules which would build the URL
	 * @param mixed $args (optional) array or object of placeholder replacement values
	 * @param boolean $useall If true (default), then all passed parameters that are not part of the built URL are tacked onto the URL as querystring
	 * @param boolean $prepend_site If true (default), a full URL is returned, if false, only the path part of the URL is returned
	 */
	public static function get( $rule_names='', $args = array(), $useall = true, $noamp = false, $prepend_site = true )
	{
		$args = self::extract_args( $args );

		$url = URL::instance();
		if ( $rule_names == '' ) {
			// Retrieve current matched RewriteRule
			$selectedrule = $url->get_matched_rule();

			// Retrieve arguments name the RewriteRule can use to build a URL.
			$rr_named_args = $selectedrule->named_args;

			$rr_args = array_merge( $rr_named_args['required'], $rr_named_args['optional']  );
			// For each argument, check if the handler_vars array has that argument and if it does, use it.
			$rr_args_values = array();

			foreach ( $rr_args as $rr_arg ) {
				if ( !isset( $args[$rr_arg] ) ) {
					$rr_arg_value = Controller::get_var( $rr_arg );
					if ( $rr_arg_value != '' ) {
						$args[$rr_arg] = $rr_arg_value;
					}
				}
			}

		}
		else {
			$url->load_rules();
			$selectedrule = null;

			if ( !is_array( $rule_names ) ) {
				$rule_names = array( $rule_names );
			}
			foreach ( $rule_names as $rule_name ) {
				if ( $rules = $url->rules->by_name( $rule_name ) ) {
					$rating = null;
					foreach ( $rules as $rule ) {
						$newrating = $rule->arg_match( $args );
						// Is the rating perfect?
						if ( $rating == 0 ) {
							$selectedrule = $rule;
							break;
						}
						if ( empty( $rating ) || ( $newrating < $rating ) ) {
							$rating = $newrating;
							$selectedrule = $rule;
						}
					}
					if ( isset( $selectedrule ) ) {
						break;
					}
				}
			}
		}

		if ( $selectedrule instanceOf RewriteRule ) {
			$return_url = $selectedrule->build( $args, $useall, $noamp );
			if ( $prepend_site ) {
				return Site::get_url( 'habari', true ) . $return_url;
			}
			else {
				return $return_url;
			}
		}
		else {
			$error = new Exception();
			$error_trace = $error->getTrace();
			// Since URL::out() calls this function, the index 0 is URL::get() which is not the proper failing call.
			if ( isset($error_trace[1]['class']) && isset($error_trace[1]['function']) && ($error_trace[1]['class'] == 'URL') && ($error_trace[1]['function'] == 'out') ) {
				$error_args = $error_trace[1];
			}
			// When calling URL::get() directly, the index 0 is the proper file and line of the failing call.
			else {
				$error_args = $error_trace[0];
			}
			EventLog::log( sprintf( _t('Could not find a rule matching the following names: %s. File: %s (line %s)'), implode(', ', $rule_names), $error_args['file'], $error_args['line'] ) , 'notice', 'rewriterules', 'habari' );
		}
	}

	/**
	 * Helper wrapper function.  Outputs the URL via echo.
	 * @param string $rule_name name of the rule which would build the URL
	 * @param array $args (optional) array of placeholder replacement values
	 * @param boolean $useall If true (default), then all passed parameters that are not part of the built URL are tacked onto the URL as querystring
	 * @param boolean $prepend_site If true (default), a full URL is returned, if false, only the path part of the URL is returned
	 */
	public static function out( $rule_name = null, $args = array(), $useall = true, $noamp = true, $prepend_site = true )
	{
		echo URL::get( $rule_name, $args, $useall, $noamp, $prepend_site );
	}

	/**
	 * Get a fully-qualified URL from a filesystem path
	 *
	 * @param string $path The filesystem path
	 * @param bool whether to include a trailing slash.  Default: No
	 * @param bool whether to leave a filename on the URL.  Default: No
	 * @return string URL
	 */
	public static function get_from_filesystem($path, $trail = false, $preserve_file = false)
	{
		if ( !$preserve_file ) {
			$path = dirname($path);
		}
		$url = Site::get_url('habari') . MultiByte::substr($path, MultiByte::strlen(HABARI_PATH));
		// Replace windows paths with forward slashes
		$url = str_replace( '\\', '/', $url);
		$url .= ( $trail ) ? '/' : '';
		return $url;
	}

	/**
	 * Extract the possible arguments to use in the URL from the passed variable
	 * @param mixed $args An array of values or a URLProperties object with properties to use in the construction of a URL
	 * @return array Properties to use to construct  a URL
	 */
	public static function extract_args( $args, $prefix = '' )
	{
		if ( is_object( $args ) ) {
			if ( $args instanceof URLProperties ) {
				$args = $args->get_url_args();
			}
			else {
				$args_ob = array();
				foreach ( $args as $key => $value ) {
					$args_ob[$key] = $value;
				}
				$args = $args_ob;
			}
		}
		else {
			$args = Utils::get_params( $args );
		}
		// can this be done with array_walk?
		if ( $prefix && $args ) {
			$args_out = array();
			foreach ( $args as $key => $value ) {
				$args_out[$prefix.$key] = $value;
			}
			$args = $args_out;
		}
		return $args;
	}

}

?>


code:
<?php
/**
 * @package Habari
 *
 */

/**
 * Habari URLProperties interface
 *
 */
interface URLProperties
{
	// must return an associative array of
	// named args/values for URL building.
	public function get_url_args();
}

?>


code:
<?php
/**
 * @package Habari
 *
 */

/**
 * Habari UserRecord Class
 *
 * @todo TODO Fix this documentation!
 *
 * Includes an instance of the UserInfo class; for holding inforecords about the user
 * If the User object describes an existing user; use the internal info object to get, set, unset and test for existence (isset) of
 * info records
 * <code>
 * $this->info = new UserInfo ( 1 );  // Info records of user with id = 1
 * $this->info->option1 = "blah"; // set info record with name "option1" to value "blah"
 * $info_value = $this->info->option1; // get value of info record with name "option1" into variable $info_value
 * if ( isset ($this->info->option1) )  // test for existence of "option1"
 * unset ( $this->info->option1 ); // delete "option1" info record
 * </code>
 *
 */
class User extends QueryRecord
{
	/**
	 * Static storage for the currently logged-in User record
	 */
	private static $identity = null;

	private $inforecords = null;

	private $group_list = null;

	protected $url_args;

	/**
	 * Get default fields for this record.
	 * @return array an array of the fields used in the User table
	 */
	public static function default_fields()
	{
		return array(
			'id' => '',
			'username' => '',
			'email' => '',
			'password' => ''
		);
	}

	/**
	 * Constructor for the User class
	 * @param array $paramarray an associative array of initial User fields
	 */
	public function __construct( $paramarray = array() )
	{
		// Defaults
		$this->fields = array_merge(
			self::default_fields(),
			$this->fields );
		parent::__construct($paramarray);
		$this->exclude_fields('id');
		 /* $this->fields['id'] could be null in case of a new user. If so, the info object is _not_ safe to use till after set_key has been called. Info records can be set immediately in any other case. */

	}

	/**
	 * Build and return the anonymous user
	 * @return object user object
	 */
	public static function anonymous()
	{
		static $anonymous = null;
		if ( $anonymous == null ) {
			$anonymous = new User();
			$anonymous->id = 0;
			$anonymous->username = _t('Anonymous');
			Plugins::act('create_anonymous_user', $anonymous);
		}
		return $anonymous;
	}

	/**
	 * Check for the existence of a cookie, and return a user object of the user, if successful
	 * @return object user object, or false if no valid cookie exists
	 */
	public static function identify()
	{
		// Is the logged-in user not cached already?
		if ( isset(self::$identity) ) {
			// is this user acting as another user?
			if ( isset($_SESSION['sudo']) ) {
				// if so, let's return that user data
				return self::get_by_id( intval($_SESSION['sudo']) );
			}
			// otherwise return the logged-in user
			return self::$identity;
		}
		if(isset($_SESSION['user_id'])) {
			if ( $user = self::get_by_id( intval($_SESSION['user_id']) ) ) {
				// Cache the user in the static variable
				self::$identity = $user;
				return $user;
			}
		}
		return self::anonymous();
	}

	/**
	 * Creates a new user object and saves it to the database
	 * @param array An associative array of user fields
	 * @return User the User object that was created
	**/
	public static function create( $paramarray )
	{
		$user = new User( $paramarray );
		$user->insert();
		return $user;
	}

	/**
	 * Save a new user to the users table
	 */
	public function insert()
	{
		$allow = true;
		$allow = Plugins::filter('user_insert_allow', $allow, $this);
		if ( ! $allow ) {
			return;
		}
		Plugins::act('user_insert_before', $this);
		$this->exclude_fields('id');
		$result = parent::insertRecord( DB::table('users') );
		$this->fields['id'] = DB::last_insert_id(); // Make sure the id is set in the user object to match the row id
		$this->info->set_key( $this->id );
		/* If a new user is being created and inserted into the db, info is only safe to use _after_ this set_key call. */
		// $this->info->option_default = "saved";

		// Set the default timezone, date format, and time format
		$this->info->locale_tz = Options::get( 'timezone' );
		$this->info->locale_date_format = Options::get( 'dateformat' );
		$this->info->locale_time_format = Options::get( 'timeformat' );

		$this->info->commit();

		if( $result ) {
			// Add the user to the default authenticated group if it exists
			if( UserGroup::exists( 'authenticated' ) ) {
				$this->add_to_group( 'authenticated' );
			}
		}

		EventLog::log( sprintf(_t('New user created: %s'), $this->username), 'info', 'default', 'habari' );
		Plugins::act('user_insert_after', $this);

		return $result;
	}

	/**
	 * Updates an existing user in the users table
	 */
	public function update()
	{
		$allow = true;
		$allow = Plugins::filter('user_update_allow', $allow, $this);
		if ( ! $allow ) {
			return;
		}
		Plugins::act('user_update_before', $this);
		$this->info->commit();
		$result = parent::updateRecord( DB::table('users'), array( 'id' => $this->id ) );
		Plugins::act('user_update_after', $this);
		return $result;
	}

	/**
	 * Delete a user account
	 */
	public function delete()
	{
		$allow = true;
		$allow = Plugins::filter('user_delete_allow', $allow, $this);
		if ( ! $allow ) {
			return;
		}
		Plugins::act('user_delete_before', $this);
		if(isset($this->info)) {
			$this->info->delete_all();
		}
		// remove all this user's permissions
		DB::query( 'DELETE FROM {user_token_permissions} WHERE user_id=?', array( $this->id ) );
		// remove user from any groups
		DB::query( 'DELETE FROM {users_groups} WHERE user_id=?', array( $this->id ) );
		EventLog::log( sprintf(_t('User deleted: %s'), $this->username), 'info', 'default', 'habari' );
		$result = parent::deleteRecord( DB::table('users'), array( 'id' => $this->id ) );
		Plugins::act('user_delete_after', $this);
		return $result;
	}

	/**
	 * Save the user id into the session
	 */
	public function remember()
	{
		$_SESSION['user_id'] = $this->id;
		ACL::clear_caches();
		Session::set_userid($this->id);
	}

	/**
	 * Delete the user id from the session
	 */
	public function forget()
	{
		// is this user acting as another user?
		if ( isset( $_SESSION['sudo'] ) ) {
			// if so, remove the sudo token, but don't log out
			// the user
			unset( $_SESSION['sudo'] );
			Utils::redirect( Site::get_url( 'admin' ) );
		}
		ACL::clear_caches();
		Plugins::act( 'user_forget', $this );
		Session::clear_userid($_SESSION['user_id']);
		unset($_SESSION['user_id']);
		$home = Options::get('base_url');
		Utils::redirect( Site::get_url( 'habari' ) );
	}

	/**
	* Check a user's credentials to see if they are legit
	* -- calls all auth plugins BEFORE checking local database.
	*
	* @todo Actually call plugins
	*
	* @param string $who A username or email address
	* @param string $pw A password
	* @return object a User object, or false
	*/
	public static function authenticate( $who, $pw )
	{
		if ( '' === $who || '' === $pw ) {
			return false;
		}

		$user = new StdClass();
		$require = false;
		$user = Plugins::filter('user_authenticate', $user, $who, $pw);
		if($user instanceof User) {
			self::$identity = $user;
			Plugins::act( 'user_authenticate_successful', self::$identity );
			EventLog::log( sprintf(_t('Successful login for %s'), $user->username), 'info', 'authentication', 'habari' );
			// set the cookie
			$user->remember();
			return self::$identity;
		}
		elseif(!is_object($user)) {
			Plugins::act( 'user_authenticate_failure', 'plugin' );
			EventLog::log( sprintf(_t('Login attempt (via authentication plugin) for non-existent user %s'), $who), 'warning', 'authentication', 'habari' );
			Session::error( _t( 'Invalid username/password' ) );
			self::$identity = null;
			return false;
		}

		// Check by name first. Allows for the '@' to be in the username, without it being an email address
		$user = self::get_by_name( $who );

		if ( ! $user ) {
			// No such user.
			Plugins::act( 'user_authenticate_failure', 'non-existent' );
			EventLog::log( _t( 'Login attempt for non-existent user %s', array( $who ) ), 'warning', 'authentication', 'habari' );
			Session::error( _t( 'Invalid username/password' ) );
			self::$identity = null;
			return false;
		}

		if ( Utils::crypt( $pw, $user->password ) ) {
			// valid credentials were supplied
			self::$identity = $user;
			Plugins::act( 'user_authenticate_successful', self::$identity );
			EventLog::log( _t( 'Successful login for %s', array( $user->username ) ), 'info', 'authentication', 'habari' );
			// set the cookie
			$user->remember();
			return self::$identity;
		}
		else {
			// Wrong password.
			Plugins::act( 'user_authenticate_failure', 'bad_pass' );
			EventLog::log( _t( 'Wrong password for user %s', array( $user->username ) ), 'warning', 'authentication', 'habari' );
			Session::error( _t( 'Invalid username/password' ) );
			self::$identity = null;
			return false;
		}
	}

	/**
	 * Fetch a user from the database by name, ID, or email address.
	 * This is a wrapper function that will invoke the appropriate
	 * get_by_* method.
	 *
	 * @param mixed $who user ID, username, or e-mail address
	 * @return object User object, or FALSE
	 */
	public static function get( $who )
	{
		if( $who instanceof User ) {
			$user = $who;
		}
		elseif ( is_numeric( $who ) ) {
			// Got a User ID
			$user = self::get_by_id( $who );
		}
		else {
			// Got username or email
			$user = self::get_by_name( $who );
			if ( ! $user && strpos( $who, '@' ) !== FALSE ) {
				// Got an email address
				$user = self::get_by_email( $who );
			}
		}
		// $user will be a user object, or false depending on the
		// results of the get_by_* method called above
		return $user;
	}

	/**
	 * Select a user from the database by its id
	 *
	 * @param int $id The id of a user
	 * @return User The User object of the specified user
	 */
	public static function get_by_id( $id )
	{
		if ( 0 == $id ) {
			return false;
		}

		$params = array(
			'id' => $id,
			'limit' => 1,
			'fetch_fn' => 'get_row',
			);

		return Users::get( $params );
	}

	/**
	 * Select a user from the database by its username
	 *
	 * @param string $username The name of a user
	 * @return User The User object of the specified user
	 */
	public static function get_by_name( $username )
	{
		if ( '' == $username ) {
			return false;
		}

		$params = array(
			'username' => $username,
			'limit' => 1,
			'fetch_fn' => 'get_row',
			);

		return Users::get( $params );
	}

	/**
	 * Select a user from the database by its email address
	 *
	 * @param string $email The email address of a user
	 * @return User The User object of the specified user
	 */
	public static function get_by_email( $email )
	{
		if ( '' === $email ) {
			return false;
		}

		$params = array(
			'email' => $email,
			'limit' => 1,
			'fetch_fn' => 'get_row',
			);

		return Users::get( $params );
	}

	/**
	 * Return the id of a user
	 *
	 * @param mixed $user The id, name, or email address of a user
	 * @return integer The id of the specified user or false if that user doesn't exist
	 */
	public static function get_id( $user )
	{
		if( is_int( $user ) ) {
			return $user;
		}
		$user = self::get( $user );
		return $user->id;
	}

	/**
	 * Returns the number of posts written by this user
	 * @param mixed A status on which to filter posts (published, draft, etc).  If FALSE, no filtering will be performed.  Default: no filtering
	 * @return int The number of posts written by this user
	**/
	public function count_posts( $status = FALSE )
	{
		return Posts::count_by_author( $this->id, $status );
	}

	/**
	 * Returns an array of information about the commenter
	 * If this is a logged-in user, then return details from their user profile.
	 * If this is a returning commenter, then return details from their cookie
	 * otherwise return empty strings.
	 * @return Array an array of name, email and URL
	**/
	public static function commenter()
	{
		$cookie = 'comment_' . Options::get('GUID');
		$commenter = array();
		if ( self::identify() ) {
			$commenter['name'] = self::identify()->username;
			$commenter['email'] = self::identify()->email;
			$commenter['url'] = Site::get_url('habari');
		}
		elseif ( isset($_COOKIE[$cookie]) ) {
			list($commenter['name'], $commenter['email'], $commenter['url']) = explode('#', urldecode( $_COOKIE[$cookie] ) );
		}
		else {
			$commenter['name'] = '';
			$commenter['email'] = '';
			$commenter['url'] = '';
		}
		return $commenter;
	}

	/**
	 * Determine if a user has a specific token permission
	 *
	 * @param string $token The name of the token for which to check permission
	 * @param string $access The type of access to check for (read, write, full, etc.)
	 * @return boolean True if this user has the requested access, false if not
	 */
	public function can( $token, $access = 'any' )
	{
		return ACL::user_can( $this, $token, $access );
	}

	/**
	 * Determine if a user has any of a set of tokens
	 *
	 * @param array $token_access An array of tokens and the permissions to
	 * check for each of them.
	 * @return boolean True if this user has the requested access, false if not
	 */
	public function can_any( $token_access = array() )
	{
		$token_access = Utils::single_array( $token_access );

		foreach( $token_access as $token => $access ) {
			$access = Utils::single_array( $access );
			foreach( $access as $mask ) {
				if( is_bool( $mask ) ) {
					if( $this->can( $token ) ) {
						return true;
					}
				}
				else {
					if( $this->can( $token, $mask ) ) {
						return true;
					}
				}
			}
		}

		return false;
	}

	/**
	 * Determine if a user has been denied access to a specific token
	 *
	 * @param string $token The name of the token to detect
	 * @return boolean True if this user has been denied access to the requested token, false if not
	 */
	public function cannot( $token )
	{
		return ACL::user_cannot( $this, $token );
	}

	/**
	 * Assign permissions to one or more new tokens to this user
	 * @param mixed A token ID, name, or array of the same
	 * @param string The access to grant
	**/
	public function grant( $tokens, $access = 'full' )
	{
		$tokens = Utils::single_array( $tokens );
		// Use ids internally for all tokens
		$tokens = array_map( array( 'ACL', 'token_id'), $tokens );

		foreach ( $tokens as $token ) {
			ACL::grant_user( $this->id, $token, $access );
			EventLog::log( _t( 'User %1$s: Access to %2$s changed to %3$s', array( $this->username, ACL::token_name( $token ), $access ) ), 'notice', 'user', 'habari' );
		}
	}

	/**
	 * Deny permissions to one or more tokens to this user
	 * @param mixed The token ID or name to be denied, or an array of the same
	**/
	public function deny( $tokens )
	{
		$this->grant( $tokens, 'deny' );
	}

	/**
	 * Remove permissions to one or more tokens from a user
	 * @param mixed a token ID, name, or array of the same
	**/
	public function revoke( $tokens )
	{
		$tokens = Utils::single_array( $tokens );
		// get token IDs
		$tokens = array_map( array( 'ACL', 'token_id' ), $tokens );
		foreach ( $tokens as $token ) {
			ACL::revoke_user_permission( $this->id, $token );
			EventLog::log( _t( 'User %1$s: Permission to %2$s revoked.', array( $this->username, ACL::token_name( $token ) ) ), 'notice', 'user', 'habari' );
		}
	}

	/**
	 * function groups
	 * Returns an array of groups to which this user belongs
	 * @param bool Whether to refresh the cache
	 * @return Array an array of group IDs to which this user belongs
	**/
	private function list_groups( $refresh = false )
	{
		if ( ( empty( $this->group_list ) ) || $refresh ) {
			$this->group_list = DB::get_column( 'SELECT group_id FROM {users_groups} WHERE user_id=?', array( $this->id ) );
		}
		return $this->group_list;
	}

	/**
	 * function in_group
	 * Whether or not this user is is in the specified group
	 * @param int $group a group ID or name
	 * @return bool Whether or not this user is in the specified group
	**/
	public function in_group( $group )
	{
		$groups = $this->list_groups();
		return in_array( UserGroup::id($group), $groups );
	}

	/**
	 * function add_to_group
	 * @param mixed $group A group ID or name
	**/
	public function add_to_group( $group )
	{
		$group = UserGroup::get( $group );
		if ( $group instanceOf UserGroup ) {
			$group->add( $this->id );
			EventLog::log( _t( ' User %1$s: Added to %2$s group.', array( $this->username, $group->name ) ), 'notice', 'user', 'habari' );
		}
	}

	/**
	 * function remove_from_group
	 * removes this user from a group
	 * @param mixed $group A group ID or name
	**/
	public function remove_from_group( $group )
	{
		$group = UserGroup::get( $group );
		if ( $group instanceOf UserGroup ) {
			$group->remove( $this->id );
			EventLog::log( _t( ' User %1$s: Removed from %2$s group.', array( $this->username, $group->name ) ), 'notice', 'user', 'habari' );
		}
	}

	/**
	 * Capture requests for the info object so that it can be initialized properly when
	 * the constructor is bypassed (see PDO::FETCH_CLASS pecularities). Passes all other
	 * requests to parent.
	 *
	 * @param string $name requested field name
	 * @return mixed the requested field value
	 */
	public function __get( $name )
	{
		$fieldnames = array_merge( array_keys( $this->fields ), array( 'groups', 'displayname', 'loggedin', 'info') );
		if ( !in_array( $name, $fieldnames ) && strpos( $name, '_' ) !== false ) {
			preg_match( '/^(.*)_([^_]+)$/', $name, $matches );
			list( $junk, $name, $filter )= $matches;
		}
		else {
			$filter = false;
		}

		switch ($name) {
			case 'info':
				$out = $this->get_info();
				break;
			case 'groups':
				$out = $this->list_groups();
				break;
			case 'displayname':
				$out = ( empty($this->info->displayname) ) ? $this->username : $this->info->displayname;
				break;
			case 'loggedin':
				$out = $this->id != 0;
				break;
			default:
				$out = parent::__get( $name );
				break;
		}

		$out = Plugins::filter( "user_get", $out, $name, $this );
		$out = Plugins::filter( "user_{$name}", $out, $this );
		if ( $filter ) {
			$out = Plugins::filter( "user_{$name}_{$filter}", $out, $this );
		}
		return $out;
	}

	/**
	 * function get_info
	 * Gets the info object for this user, which contains data from the userinfo table
	 * related to this user.
	 * @return UserInfo object
	 */
	private function get_info()
	{
		if ( ! isset( $this->inforecords ) ) {
			// If this post isn't in the database yet...
			if(  0 == $this->id ) {
				$this->inforecords = new UserInfo();
			}
			else {
				$this->inforecords = new UserInfo( $this->id );
			}
		}
		else {
			$this->inforecords->set_key( $this->id );
		}
		return $this->inforecords;
	}

	/**
	 * Returns a set of properties used by URL::get to create URLs
	 * @return array Properties of this post used to build a URL
	 */
	public function get_url_args()
	{
		if ( !$this->url_args ) {
			$this->url_args = array_merge( URL::extract_args( $this->info, 'info_' ), $this->to_array() );
		}
		return $this->url_args;
	}

}

?>


code:
<?php
/**
 * @package Habari
 *
 */

/**
 * Habari UserGroup Class
 *
 */
class UserGroup extends QueryRecord
{
	// These arrays hold the current membership and permission settings for this group
	// $member_ids is not NOT matched key and value pairs ( like array('foo'=>'foo') )
	private $member_ids = null;
	private $permissions;

	/**
	 * get default fields for this record
	 * @return array an array of the fields used in the UserGroup table
	 */
	public static function default_fields()
	{
		return array(
			'id' => '',
			'name' => ''
		);
	}

	/**
	 * Constructor for the UserGroup class
	 * @param array $paramarray an associative array of UserGroup fields
	 */
	public function __construct( $paramarray = array() )
	{
		$this->fields = array_merge(
			self::default_fields(),
			$this->fields
		);
		parent::__construct( $paramarray );

		// exclude field keys from the $this->fields array that should not be updated in the database on insert/update
		$this->exclude_fields( array( 'id' ) );
	}

	/**
	 * Create a new UserGroup object and save it to the database
	 * @param array $paramarray An associative array of UserGroup fields
	 * @return UserGroup the UserGroup that was created
	 * @todo Make this function accept only a name, since you can't set an id into an autoincrement field, and we don't try.
	 */
	public static function create( $paramarray )
	{
		$usergroup = new UserGroup( $paramarray );
		if ( $usergroup->insert() ) {
			return $usergroup;
		}
		else {
			// Does the group already exist?
			if(isset($paramarray['name'])) {
				$exists = DB::get_value('SELECT count(1) FROM {groups} WHERE name = ?', array($paramarray['name']));
				if($exists) {
					return UserGroup::get_by_name($paramarray['name']);
				}
			}
			return false;
		}
	}

	/**
	 * Save a new UserGroup to the UserGroup table
	 */
	public function insert()
	{
		$exists = DB::get_value('SELECT count(1) FROM {groups} WHERE name = ?', array($this->name));
		if($exists) {
			return false;
		}

		$allow = true;
		// plugins have the opportunity to prevent insertion
		$allow = Plugins::filter('usergroup_insert_allow', $allow, $this);
		if ( ! $allow ) {
			return false;
		}
		Plugins::act('usergroup_insert_before', $this);
		$this->exclude_fields('id');
		$result = parent::insertRecord( DB::table('groups') );
		$this->fields['id'] = DB::last_insert_id();

		$this->set_member_list();

		EventLog::log( sprintf(_t('New group created: %s'), $this->name), 'info', 'default', 'habari');
		Plugins::act('usergroup_insert_after', $this);
		return $result;
	}

	/**
	 * Updates an existing UserGroup in the DB
	 */
	public function update()
	{
		$allow = true;
		// plugins have the opportunity to prevent modification
		$allow = Plugins::filter('usergroup_update_allow', $allow, $this);
		if ( ! $allow ) {
			return false;
		}
		Plugins::act('usergroup_update_before', $this);

		$this->set_member_list();

		EventLog::log(sprintf(_t('User Group updated: %s'), $this->name), 'info', 'default', 'habari');
		Plugins::act('usergroup_update_after', $this);
	}

	/**
	 * Set the member list for this group
	 */
	protected function set_member_list()
	{
		$this->load_member_cache();

		// Remove all users from this group in preparation for adding the current list
		DB::query('DELETE FROM {users_groups} WHERE group_id=?', array( $this->id ) );
		// Add the current list of users into the group
		foreach( $this->member_ids as $user_id ) {
			DB::query('INSERT INTO {users_groups} (user_id, group_id) VALUES (?, ?)', array( $user_id, $this->id) );
		}
		EventLog::log( _t( 'User Group %s: Member list reset', array( $this->name ) ), 'notice', 'user', 'habari' );
	}

	/**
	 * Delete a UserGroup
	 */
	public function delete()
	{
		$allow = true;
		// plugins have the opportunity to prevent deletion
		$allow = Plugins::filter('usergroup_delete_allow', $allow, $this);
		 if ( ! $allow ) {
		 	return;
		}

		$name = $this->name;
		Plugins::act('usergroup_delete_before', $this);
		// remove all this group's permissions
		$results = DB::query( 'DELETE FROM {group_token_permissions} WHERE group_id=?', array( $this->id ) );
		// remove all this group's members
		$results = DB::query( 'DELETE FROM {users_groups} WHERE group_id=?', array( $this->id ) );
		// remove this group
		$result = parent::deleteRecord( DB::table('groups'), array( 'id' => $this->id ) );
		Plugins::act('usergroup_delete_after', $this);
		EventLog::log( _t( 'User Group %s: Group deleted.', array( $name ) ), 'notice', 'user', 'habari' );
		return $result;
	}

	/**
	 * function __get
	 * magic get function for returning virtual properties of the class
	 * @param mixed the property to get
	 * @return mixed the property
	 */
	public function __get( $param )
	{
		switch ( $param ) {
			case 'members':
				$this->load_member_cache();
				return (array) $this->member_ids;
				break;
			case 'users':
				$this->load_member_cache();
				$results = DB::get_results( 'SELECT u.* FROM {users} u INNER JOIN {users_groups} ug ON ug.user_id = u.id WHERE ug.group_id= ?', array( $this->id ), 'User' );
				if(in_array(0, $this->member_ids)) {
					$results[] = User::anonymous();
				}
				return $results;
			case 'permissions':
				$this->load_member_cache();
				return $this->permissions;
				break;
			default:
				return parent::__get( $param );
				break;
		}
	}

	/**
	 * Add one or more users to this group
	 * @param mixed $users a user ID or name, or an array of the same
	 */
	public function add( $users )
	{
		$this->load_member_cache();
		$users = Utils::single_array( $users );
		// Use ids internally for all users
		$user_ids = array_map(array('User', 'get_id'), $users);
		// Remove users from group membership
		$this->member_ids = array_merge( (array) $this->member_ids, (array) $user_ids);
		// List each group member exactly once
		$this->member_ids = array_unique($this->member_ids);
		$this->update();

		EventLog::log( _t( 'User Group %1$s: Users were added to the group.', array( $this->name ) ), 'notice', 'user', 'habari' );
	}

	/**
	 * Remove one or more user from this group
	 * @param mixed $users A user ID or name, or an array of the same
	 */
	public function remove( $users )
	{
		$this->load_member_cache();
		$users = Utils::single_array( $users );
		// Use ids internally for all users
		$users = array_map(array('User', 'get_id'), $users);
		// Remove users from group membership
		$this->member_ids = array_diff( $this->member_ids, $users);
		$this->update();

		EventLog::log( _t( 'User Group %1$s: Users were removed from the group.', array( $this->name ) ), 'notice', 'user', 'habari' );
	}

	/**
	 * Assign one or more new permission tokens to this group
	 * @param mixed A permission token ID, name, or array of the same
	 */
	public function grant( $tokens, $access = 'full' )
	{
		$tokens = Utils::single_array( $tokens );
		// Use ids internally for all tokens
		$tokens = array_map(array('ACL', 'token_id'), $tokens);

		// grant the new permissions
		foreach ( $tokens as $token ) {
			ACL::grant_group( $this->id, $token, $access );
		}
	}

	/**
	 * Deny one or more permission tokens to this group
	 * @param mixed The permission ID or name to be denied, or an array of the same
	 */
	public function deny( $tokens )
	{
		$this->grant( $tokens, 'deny' );
	}

	/**
	 * Remove one or more permissions from a group
	 * @param mixed a permission ID, name, or array of the same
	 */
	public function revoke( $tokens )
	{
		$tokens = Utils::single_array( $tokens );
		$tokens = array_map(array('ACL', 'token_id'), $tokens);

		foreach ( $tokens as $token ) {
			ACL::revoke_group_token( $this->id, $token );
		}
	}

	/**
	 * Determine whether members of a group can do something.
	 * This function should not be used to determine composite permissions among several groups
	 * @param mixed a permission ID or name
	 * @return boolean If this group has been granted and not denied this permission, return true.  Otherwise, return false.
	 * @see ACL::group_can()
	 * @see ACL::user_can()
	 */
	public function can( $token, $access = 'full' )
	{
		$token = ACL::token_id( $token );
		$this->load_permissions_cache();
		if ( isset( $this->permissions[$token] ) && ACL::access_check( $this->permissions[$token], $access ) ) {
			return true;
		}
		return false;
	}

	/**
	 * Return the access bitmask for a specific token for this group.
	 *
	 * @param string $token The
	 * @return
	 */
	public function get_access( $token )
	{
		$token = ACL::token_id( $token );
		$this->load_permissions_cache();
		if ( isset( $this->permissions[$token] ) ) {
			return ACL::get_bitmask( $this->permissions[$token]);
		}
		return false;
	}

	/**
	 * Clear permissions cache.
	 */
	public function clear_permissions_cache()
	{
		//unset( $this->permissions );
		$this->permissions = NULL;
	}

	/**
	 * Load permissions cache.
	 */
	public function load_permissions_cache()
	{
		if ( is_null( $this->permissions ) ) {
			if ( $results = DB::get_results( 'SELECT token_id, permission_id FROM {group_token_permissions} WHERE group_id=?', array( $this->id ) ) ) {
				foreach ( $results as $result ) {
					$this->permissions[$result->token_id] = $result->permission_id;
				}
			}
		}
	}

	/**
	 * Fetch a group from the database by ID or name.
	 * This is a wrapper for get_by_id() and get_by_name()
	 * @param mixed $group A group ID or name
	 * @return mixed UserGroup object, or boolean FALSE
	 */
	public static function get( $group )
	{
		if ( is_numeric( $group ) ) {
			return self::get_by_id( $group );
		}
		else {
			return self::get_by_name( $group );
		}
	}

	/**
	 * Select a group from the DB by its ID
	 * @param int A group ID
	 * @return mixed A UserGroup object, or boolean FALSE
	 */
	public static function get_by_id( $id )
	{
		return DB::get_row( 'SELECT * FROM {groups} WHERE id=?', array( $id ), 'UserGroup' );
	}

	/**
	 * Select a group from the DB by its name
	 * @param string A group name
	 * @return mixed A UserGroup object, or boolean FALSE
	 */
	public static function get_by_name( $name )
	{
		return DB::get_row( 'SELECT * FROM {groups} WHERE name=?', array( $name ), 'UserGroup' );
	}

	/**
	 * Determine whether a group exists
	 * @param mixed The name or ID of the group
	 * @return bool Whether the group exists or not
	 */
	public static function exists( $group )
	{
		return !is_null(self::id($group));
	}

	/**
	 * Given a group's ID, return its friendly name
	 * @param int a group's ID
	 * @return string the group's name or false if the group doesn't exist
	 */
	public static function name( $id )
	{
		$check_field = is_numeric( $id ) ? 'id' : 'name';
		$name = DB::get_value( "SELECT name FROM {groups} WHERE {$check_field}=?", array( $id ) );
		return $name;  // get_value returns false if no record is returned
	}

	/**
	 * Given a group's name, return its ID
	 * @param string a group's name
	 * @return int the group's ID, or false if the group doesn't exist
	 */
	public static function id( $name )
	{
		$check_field = is_numeric( $name ) ? 'id' : 'name';
		$id = DB::get_value( "SELECT id FROM {groups} WHERE {$check_field}=?", array( $name ) );
		return $id; // get_value returns false if no record is returned
	}

	/**
	 * Determine whether the specified user is a member of the group
	 * @param mixed A user ID or name
	 * @return bool True if the user is in the group, otherwise false
	 */
	public function member( $user_id )
	{
		if ( ! is_numeric( $user_id ) ) {
			$user = User::get( $user_id );
			$user_id = $user->id;
		}

		if ( in_array( $user_id, $this->member_ids ) ) {
			return true;
		}
		return false;
	}

	/**
	 * Cache the member ids that belong to this group
	 *
	 * @param boolean $refresh Optional. If true, refresh the cache
	 */
	protected function load_member_cache( $refresh = false )
	{
		// if we have an ID, load this UserGroup's members & permissions
		if ( $this->id && ( $refresh || !isset( $this->member_ids ) ) ) {
			$this->member_ids = DB::get_column( 'SELECT user_id FROM {users_groups} WHERE group_id= ?', array( $this->id ) );
		}
	}
}
?>


code:
<?php
/**
 * @package Habari
 *
 */

/**
 * Habari UserGroups Class
 *
 */
class UserGroups extends ArrayObject
{
	protected $get_param_cache; // Stores info about the last set of data fetched that was not a single value

	/**
	 * Returns a group or grops based on supplied parameters.
	 * @todo This class should cache query results!
	 *
	 * @param array $paramarray An associated array of parameters, or a querystring
	 * @return array An array of UserGroup objects, or a single UserGroup object, depending on request
	 */
	public static function get( $paramarray = array() )
	{
		$params = array();
		$fns = array( 'get_results', 'get_row', 'get_value' );
		$select = '';
		// what to select -- by default, everything
		foreach ( UserGroup::default_fields() as $field => $value ) {
			$select .= ( '' == $select )
				? "{groups}.$field"
				: ", {groups}.$field";
		}
		// defaults
		$orderby = 'id ASC';
		$nolimit = TRUE;

		// Put incoming parameters into the local scope
		$paramarray = Utils::get_params( $paramarray );

		// Transact on possible multiple sets of where information that is to be OR'ed
		if ( isset( $paramarray['where'] ) && is_array( $paramarray['where'] ) ) {
			$wheresets = $paramarray['where'];
		}
		else {
			$wheresets = array( array() );
		}

		$wheres = array();
		$join = '';
		if ( isset( $paramarray['where'] ) && is_string( $paramarray['where'] ) ) {
			$wheres[] = $paramarray['where'];
		}
		else {
			foreach ( $wheresets as $paramset ) {
				// safety mechanism to prevent empty queries
				$where = array();
				$paramset = array_merge((array) $paramarray, (array) $paramset);

				$default_fields = UserGroup::default_fields();
				foreach ( UserGroup::default_fields() as $field => $scrap ) {
					if ( !isset( $paramset[$field] ) ) {
						continue;
					}
					switch ( $field ) {
						case 'id':
							if ( !is_numeric( $paramset[$field] ) ) {
								continue;
							}
						default:
							$where[] = "{$field} = ?";
							$params[] = $paramset[$field];
					}
				}

				if (count($where) > 0 ) {
					$wheres[] = ' (' . implode( ' AND ', $where ) . ') ';
				}
			}
		}

		// Get any full-query parameters
		$possible = array( 'fetch_fn', 'count', 'nolimit', 'limit', 'offset' );
		foreach ( $possible as $varname ) {
			if ( isset( $paramarray[$varname] ) ) {
				$$varname = $paramarray[$varname];
			}
		}

		if ( isset( $fetch_fn ) ) {
			if ( ! in_array( $fetch_fn, $fns ) ) {
				$fetch_fn = $fns[0];
			}
		}
		else {
			$fetch_fn = $fns[0];
		}

		// is a count being request?
		if ( isset( $count ) ) {
			$select = "COUNT($count)";
			$fetch_fn = 'get_value';
			$orderby = '';
		}
		// Are we asking for a single UserGroup or possibly multiple UserGroups
		$single = false;
		if ( isset( $limit ) ) {
			$single = ($limit == 1);
			$limit = " LIMIT $limit";
			if ( isset( $offset ) ) {
				$limit .= " OFFSET $offset";
			}
		}
		if ( isset( $nolimit ) ) {
			$limit = '';
		}

		$query = ' SELECT ' . $select . ' FROM {groups} ' . $join;

		if ( count( $wheres ) > 0 ) {
			$query .= ' WHERE ' . implode( " \nOR\n ", $wheres );
		}
		$query .= ( ($orderby == '') ? '' : ' ORDER BY ' . $orderby ) . $limit;

		DB::set_fetch_mode(PDO::FETCH_CLASS);

		$results = DB::$fetch_fn( $query, $params, 'UserGroup' );

		if ( 'get_results' != $fetch_fn ) {
			// return the results
			return $results;
		}
		elseif ( is_array( $results ) ) {
			$c = __CLASS__;
			$return_value = new $c( $results );
			$return_value->get_param_cache = $paramarray;
			return $return_value;
		}
	}

	/**
	 * Select all groups from the database
	 *
	 * @return Groups
	 */
	public static function get_all()
	{

		$params = array(
			'orderby' => 'name ASC'
			);

		return self::get( $params );
	}

}
?>


code:
<?php
/**
 * @package Habari
 *
 */

/**
 * Habari UserHandler Class
 *
 */
class UserHandler extends ActionHandler
{

	/**
	 * Either just display the login form; or check a user's credentials, and
	 * create a session for them; or handle a password reset request.
	 */
	public function act_login()
	{
		// If we're a reset password request, do that.
		if ( isset( $_POST['submit_button']) && $_POST['submit_button'] === _t( 'Reset password' ) ) {
			Utils::check_request_method( array( 'POST' ) );

			$name = $this->handler_vars['habari_username'];
			if ( $name !== NULL ) {
				if ( !is_numeric($name) && $user = User::get($name) ) {
					$hash = Utils::random_password();

					$user->info->password_reset = md5($hash);
					$user->info->commit();
					$message = _t('Please visit %1$s to reset your password.', array(URL::get('user', array('page' => 'password_reset', 'id' => $user->id, 'hash' => $hash))));

					Utils::mail($user->email, _t('[%1$s] Password reset request for %2$s', array(Options::get('title'), $user->displayname)), $message);
				}
				// Moving this inside the check for user existence would allow attackers to test usernames, so don't
				Session::notice(_t('A password reset request has been sent to the user.'));
			}
		}
		// Back to actual login.
		else {
			Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) );
			$name = $_POST['habari_username'];
			$pass = $_POST['habari_password'];

			if ( ( NULL != $name ) || ( NULL != $pass ) ) {
				$user = User::authenticate( $name, $pass );

				if ( ( $user instanceOf User ) && ( FALSE != $user ) ) {
					/* Successfully authenticated. */
					// Timestamp last login date and time.
					$user->info->authenticate_time = date( 'Y-m-d H:i:s' );
					$user->update();

					// Remove left over expired session error message.
					if ( Session::has_errors( 'expired_session' ) ) {
						Session::remove_error( 'expired_session' );
					}

					$login_session = Session::get_set( 'login' );
					if ( ! empty( $login_session ) ) {
						/* Now that we know we're dealing with the same user, transfer the form data so he does not lose his request */
						if ( ! empty( $login_session['post_data'] ) ) {
							Session::add_to_set( 'last_form_data', $last_form_data['post'], 'post' );
						}
						if ( ! empty( $login_session['get_data'] ) ) {
							Session::add_to_set( 'last_form_data', $last_form_data['get'], 'get' );
						}

						/* Redirect to the correct admin page */
						$dest = explode( '/', MultiByte::substr( $login_session['original'], MultiByte::strpos( $login_session['original'], 'admin/' ) ) );
						if ( '' == $dest[0] ) {
							$login_dest = Site::get_url( 'admin' );
						}
						else {
							// Replace '?' with '&' in $dest[1] before call URL::get()
							// Therefore calling URL::get() with a query string
							$dest[1] = str_replace( '?', '&', $dest[1] );
							$login_dest = URL::get( 'admin', 'page=' . $dest[1] );
						}
					}
					else {
						$login_session = null;
						$login_dest = Site::get_url( 'admin' );
					}

					// filter the destination
					$login_dest = Plugins::filter( 'login_redirect_dest', $login_dest, $user, $login_session );

					// finally, redirect to the destination
					Utils::redirect( $login_dest );

					return TRUE;
				}

				/* Authentication failed. */
				// Remove submitted password, see, we're secure!
				$_POST['habari_password'] = '';
				$this->handler_vars['error'] = _t('Bad credentials');
			}
		}

		// Display the login form.
		$this->login_form($name);
	}

	/**
	* function logout
	* terminates a user's session, and deletes the Habari cookie
	* @param string the Action that was in the URL rule
	* @param array An associative array of settings found in the URL by the URL
	*/
	public function act_logout()
	{
		Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) );

		// get the user from their cookie
		$user = User::identify();
		if ( $user->loggedin ) {
			Plugins::act( 'user_logout', $user );
			// delete the cookie, and destroy the object
			$user->forget();
			$user = null;
		}
		Utils::redirect(Site::get_url('habari'));
	}

	/**
	 * Display the login form
	 *
	 * @param string $name Pre-fill the name field with this name
	 */
	protected function login_form($name)
	{
		// Display the login form.
		$this->theme = Themes::create();
		if ( !$this->theme->template_exists( 'login' ) ) {
			$this->theme = Themes::create( 'admin', 'RawPHPEngine', Site::get_dir( 'admin_theme', TRUE ) );
			$this->theme->assign( 'admin_page', 'login' );
		}
		$request = new StdClass();
		foreach ( URL::get_active_rules() as $rule ) {
			$request->{$rule->name} = ( $rule->name == URL::get_matched_rule()->name );
		}

		if (isset($this->handler_vars['error'])) {
		    $this->theme->assign( 'error', Utils::htmlspecialchars( $this->handler_vars['error'] ) );
		}

		$this->theme->assign( 'request', $request );
		$this->theme->assign( 'habari_username', htmlentities($name, ENT_QUOTES, 'UTF-8') );
		$this->display( 'login' );
		return TRUE;
	}

	/**
	 * Helper function which automatically assigns all handler_vars
	 * into the theme and displays a theme template
	 *
	 * @param template_name Name of template to display (note: not the filename)
	 */
	protected function display( $template_name )
	{
		$this->theme->display($template_name);
	}

	/**
	 * Handle password reset confirmations
	 */
	public function act_password_reset()
	{
		Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) );

		$id = $this->handler_vars['id'];
		$hash = $this->handler_vars['hash'];
		$name = '';

		if ( $user = User::get($id) ) {
			if ( $user->info->password_reset == md5($hash)) {
				// Send a new random password
				$password = Utils::random_password();

				$user->password = Utils::crypt( $password );
				if ( $user->update() ) {
					$message = _t("Your password for %1\$s has been reset.  Your credentials are as follows---\nUsername: %2\$s\nPassword: %3\$s", array(Site::get_url('habari'), $user->username, $password));

					Utils::mail($user->email, _t('[%1$s] Password has been reset for %2$s', array(Options::get('title'), $user->displayname)), $message);
					Session::notice(_t('A new password has been sent to the user.'));
				}
				else {
					Session::notice(_t('There was a problem resetting the password.  It was not reset.'));
				}

				// Clear the request - it should only work once
				unset($user->info->password_reset);
				$user->info->commit();

				$name = $user->username;
			}
			else {
				Session::notice(_t('The supplied password reset token has expired or is invalid.'));
			}
		}
		// Display the login form.
		$this->login_form($name);
	}

}
?>


code:
<?php
/**
 * @package Habari
 *
 */

/**
 * Habari UserInfo class
 *
 * User metadata
 */
class UserInfo extends InfoRecords
{
	function __construct ( $user_id = NULL )
	{
		// call parent with appropriate  parameters
		parent::__construct ( DB::table('userinfo'), 'user_id', $user_id );
	}
}

?>


code:
<?php
/**
 * @package Habari
 *
 */

/**
 * Habari Users Class
 *
 */
class Users extends ArrayObject
{
	protected $get_param_cache; // Stores info about the last set of data fetched that was not a single value

	/**
	 * Returns a user or users based on supplied parameters.
	 * @todo This class should cache query results!
	 *
	 * @param array $paramarray An associated array of parameters, or a querystring
	 * @return array An array of User objects, or a single User object, depending on request
	 **/
	public static function get( $paramarray = array() )
	{
		$params = array();
		$fns = array( 'get_results', 'get_row', 'get_value' );
		$select = '';
		// what to select -- by default, everything
		foreach ( User::default_fields() as $field => $value ) {
			$select .= ( '' == $select )
				? "{users}.$field"
				: ", {users}.$field";
		}
		// defaults
		$orderby = 'id ASC';
		$nolimit = TRUE;

		// Put incoming parameters into the local scope
		$paramarray = Utils::get_params( $paramarray );

		// Transact on possible multiple sets of where information that is to be OR'ed
		if ( isset( $paramarray['where'] ) && is_array( $paramarray['where'] ) ) {
			$wheresets = $paramarray['where'];
		}
		else {
			$wheresets = array( array() );
		}

		$wheres = array();
		$join = '';
		if ( isset( $paramarray['where'] ) && is_string( $paramarray['where'] ) ) {
			$wheres[] = $paramarray['where'];
		}
		else {
			foreach ( $wheresets as $paramset ) {
				// safety mechanism to prevent empty queries
				$where = array();
				$paramset = array_merge((array) $paramarray, (array) $paramset);

				$default_fields = User::default_fields();
				foreach ( User::default_fields() as $field => $scrap ) {
					if ( !isset( $paramset[$field] ) ) {
						continue;
					}
					switch ( $field ) {
						case 'id':
							// ilo, allow searching for groups, passing a list of group member ids
							if ( is_array($paramset[$field])) {
								$where[] = "{$field} IN (". implode( ', ', $paramset[$field]) .")";
							}
							if ( !is_numeric( $paramset[$field] ) ) {
								continue;
							}
						default:
							$where[] = "{$field} = ?";
							$params[] = $paramset[$field];
					}
				}

				if ( isset( $paramset['info'] ) && is_array( $paramset['info'] ) ) {
					$join .= 'INNER JOIN {userinfo} ON {users}.id = {userinfo}.user_id';
					foreach ( $paramset['info'] as $info_name => $info_value ) {
						$where[] = '{userinfo}.name = ? AND {userinfo}.value = ?';
						$params[] = $info_name;
						$params[] = $info_value;
					}
				}

				if ( count($where) > 0 ) {
					$wheres[] = ' (' . implode( ' AND ', $where ) . ') ';
				}
			}
		}

		// Get any full-query parameters
		$possible = array( 'fetch_fn', 'count', 'nolimit', 'limit', 'offset' );
		foreach ( $possible as $varname ) {
			if ( isset( $paramarray[$varname] ) ) {
				$$varname = $paramarray[$varname];
			}
		}

		if ( isset( $fetch_fn ) ) {
			if ( ! in_array( $fetch_fn, $fns ) ) {
				$fetch_fn = $fns[0];
			}
		}
		else {
			$fetch_fn = $fns[0];
		}

		// is a count being request?
		if ( isset( $count ) ) {
			$select = "COUNT($count)";
			$fetch_fn = 'get_value';
			$orderby = '';
		}
		if ( isset( $limit ) ) {
			$limit = " LIMIT $limit";
			if ( isset( $offset ) ) {
				$limit .= " OFFSET $offset";
			}
		}
		if ( isset( $nolimit ) ) {
			$limit = '';
		}

		$query = '
			SELECT ' . $select
			. ' FROM {users} '
			. $join;

		if ( count( $wheres ) > 0 ) {
			$query .= ' WHERE ' . implode( " \nOR\n ", $wheres );
		}
		$query .= ( ($orderby == '') ? '' : ' ORDER BY ' . $orderby ) . $limit;
		//Utils::debug($paramarray, $fetch_fn, $query, $params);

		DB::set_fetch_mode(PDO::FETCH_CLASS);
		DB::set_fetch_class('User');
		$results = DB::$fetch_fn( $query, $params, 'User' );

		if ( 'get_results' != $fetch_fn ) {
			// return the results
			return $results;
		}
		elseif ( is_array( $results ) ) {
			$c = __CLASS__;
			$return_value = new $c( $results );
			$return_value->get_param_cache = $paramarray;
			return $return_value;
		}
	}

	/**
	 * Select a user from the database by userinfo
	 *
	 * @param string|array $key
	 * @param string|null $value
	 * @return Users|bool
	 */
	public static function get_by_info( $key, $value = NULL )
	{
		// If no value was specified, check if several info were passed
		if ( NULL === $value ) {
			if ( is_array( $key ) ) {
				$params['info'] = $key;
			}
			else {
				// We need a value to compare to
				return false;
			}
		}
		else {
			$params['info'] = array( $key => $value );
		}

		return self::get( $params );
	}

	/**
	 * Select all users from the database
	 *
	 * @return Users
	 */
	public static function get_all()
	{
		$params = array(
			'orderby' => 'username ASC'
		);

		return self::get( $params );
	}

	/**
	 * Parses a search string for info, and group. Returns an associative array
	 * which can be passed to Users::get(). If multiple info or groups are
	 * specified, we assume an implicit OR such that (e.g.) any user that matches
	 * would be returned.
	 *
	 * Currently it handles group:groupname
	 *
	 * @param string $search_string The search string
	 * @return array An associative array which can be passed to Users::get()
	 */
	public static function search_to_get( $search_string )
	{
		// if adding to this array, make sure you update the consequences of a search on this below in the switch.
		$keywords = array( 'group' => 1, 'info' => 1, );

		// Get Group information
		$allgroups = UserGroups::get_all();
		$groups = array();
		foreach ($allgroups as $group) {
			$groups[strtolower($group->name)] = $group->id;
		}

		$arguments = array(
			'info' => array(),
			'group' => array()
		);
		$criteria = '';

		// this says, find stuff that has the keyword at the start, and then some term straight after.
		// the terms should have no whitespace, or if it does, be ' delimited.
		// ie info:foo or info:'foo bar'
		$flag_regex = '/(?P<flag>' . implode( '|', array_keys( $keywords ) ) . '):(?P<value>[^\'"][^\s]*(?:\s|$)|([\'"]+)(?P<quotedvalue>[^\3]+)(?<!\\\)\3)/Uui';

		// now do some matching.
		preg_match_all( $flag_regex , $search_string, $matches, PREG_SET_ORDER );

		// now we remove those terms from the search string, otherwise the keyword search below has issues. It will pick up things like
		// from tag:'pair of' -> matches of'
		$criteria = trim(preg_replace( $flag_regex, '', $search_string));

		// go through flagged things.
		foreach ($matches as $match) {
			// switch on the type match. ie status, type et al.
			// also, trim out the quote marks that have been matched.
			if( isset($match['quotedvalue']) && $match['quotedvalue'] ) {
				$value = stripslashes($match['quotedvalue']);
			}
			else {
				$value = $match['value'];
			}
			$value = trim( $value );

			switch( strtolower($match['flag']) )  {
				// ilo: support search users by group
				case 'group':
					if ( isset( $groups[strtolower($value)] ) ) {
						$member_ids = DB::get_column( 'SELECT user_id FROM {users_groups} WHERE group_id= ?', array( $groups[strtolower($value) ] ) );
						if ( is_array( $member_ids ) && count( $member_ids ) ) {
						  $arguments['id'][] = $member_ids;
						}
					}
					break;
				case 'info':
					if( strpos($value, ':') !== FALSE ) {
						list( $infokey, $infovalue ) = explode( ':', $value, 2 );
						$arguments['info'][] = array($infokey=>$infovalue);
					}
					break;
			}
		}

		// flatten keys that have single-element or no-element arrays
		foreach ( $arguments as $key => $arg ) {
			switch ( count( $arg ) ) {
				case 0:
					unset( $arguments[$key] );
					break;
				case 1:
					$arguments[$key] = $arg[0];
					break;
			}
		}

		if ( $criteria != '' ) {
			$arguments['username'] = $criteria;
		}

		return $arguments;
	}

}
?>


code:
<?php
/**
 * @package Habari
 *
 */

/**
 * Class for handling default user theme actions.
 *
 * @note Any theme will be able to override these default actions by registering an override function with Theme->act_some_function()
 */
class UserThemeHandler extends ActionHandler
{

	/**
	 * Constructor for the default theme handler.
	 */
	public function __construct()
	{
		$this->setup_theme();
	}

	/**
	 * The UserThemeHandler's act() method differs from ActionHandler's
	 * act() method in one distinct way: if the Handler's theme variable
	 * registers an override action via Theme->register_action(), then
	 * that function is called instead of the default handler action.
	 *
	 * @param   action  the action that was in the URL rule
	 * @return  bool    did the action succeed?
	 */
	public function act( $action )
	{
		$this->action = $action;

		$action_method = 'act_' . $action;
		$before_action_method = 'before_' . $action_method;
		$after_action_method = 'after_' . $action_method;

		$this->theme->$before_action_method();
		try {
			$handled = false;
			$handled = Plugins::filter('theme_act_' . $action, $handled, $this->theme);
			if ( !$handled ) {
				$this->theme->$action_method();
			}
		}
		catch(Error $e) {
			EventLog::log($e->humane_error(), 'error', 'theme', 'habari', print_r($e, 1) );
			Session::error($e->humane_error()); //Should we display any error here?
			if ( DEBUG ) {
				Utils::debug($e);
			}
		}
		$this->theme->$after_action_method();
	}

	/**
	 * Helper function which automatically assigns all handler_vars
	 * into the theme and displays a theme template
	 *
	 * @param template_name Name of template to display ( note: not the filename )
	 */
	protected function display( $template_name )
	{
		/*
		 * Assign internal variables into the theme ( and therefore into the theme's template
		 * engine.  See Theme::assign().
		 */
		foreach ( $this->handler_vars as $key => $value ) {
			$this->theme->assign( $key, $value );
		}
		try {
			$this->theme->display( $template_name );
		}
		catch(Error $e) {
			EventLog::log($e->humane_error(), 'error', 'theme', 'habari', print_r($e, 1) );
		}
	}
}

?>