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

/**
 * Habari Utility Class
 *
 */
class Utils
{
	public static $debug_defined = false;

	/**
	 * Utils constructor
	 * This class should not be instantiated.
	 */
	private function __construct()
	{
	}

	/**
	 * function get_params
	 * Returns an associative array of parameters, whether the input value is
	 * a querystring or an associative array.
	 * @param mixed An associative array or querystring parameter list
	 * @return array An associative array of parameters
	 */
	public static function get_params( $params )
	{
		if ( is_array( $params ) || $params instanceof ArrayObject || $params instanceof ArrayIterator ) {
			return $params;
		}
		$paramarray = array();
		parse_str( $params, $paramarray );
		return $paramarray;
	}

	/**
	 * function end_in_slash
	 * Forces a string to end in a single slash
	 * @param string A string, usually a path
	 * @return string The string with the slash added or extra slashes removed, but with one slash only
	 */
	public static function end_in_slash( $value )
	{
		return rtrim( $value, '\\/' ) . '/';
	}

	/**
	 * function redirect
	 * Redirects the request to a new URL
	 * @param string $url The URL to redirect to, or omit to redirect to the current url
	 * @param boolean $continue Whether to continue processing the script (default false for security reasons, cf. #749)
	 */
	public static function redirect( $url = '', $continue = false )
	{
		if ( $url == '' ) {
			$url = Controller::get_full_url();
		}
		header( 'Location: ' . $url, true, 302 );

		if ( ! $continue ) exit;
	}

	/**
	 * function atomtime
	 * Returns RFC-3339 time from a time string or integer timestamp
	 * @param mixed A string of time or integer timestamp
	 * @return string An RFC-3339 formatted time
	 */
	public static function atomtime( $t )
	{
		if ( ! is_numeric( $t ) ) {
			$t = strtotime( $t );
		}
		$vdate = date( DATE_ATOM, $t );
		// If the date format used for timezone was O instead of P...
		if ( substr( $vdate, -3, 1 ) != ':' ) {
			$vdate = substr( $vdate, 0, -2 ) . ':' . substr( $vdate, -2, 2 );
		}
		return $vdate;
	}

	/**
	 * function nonce
	 * Returns a random 12-digit hex number
	 */
	public static function nonce()
	{
		return sprintf( '%06x', rand( 0, 16776960 ) ) . sprintf( '%06x', rand( 0, 16776960 ) );
	}

	/**
	 * function WSSE
	 * returns an array of tokens used for WSSE authentication
	 *    http://www.xml.com/pub/a/2003/12/17/dive.html
	 *    http://www.sixapart.com/developers/atom/protocol/atom_authentication.html
	 * @param String a nonce
	 * @param String a timestamp
	 * @return Array an array of WSSE authentication elements
	 */
	public static function WSSE( $nonce = '', $timestamp = '' )
	{
		if ( '' === $nonce ) {
			$nonce = Utils::crypt( Options::get( 'GUID' ) . Utils::nonce() );
		}
		if ( '' === $timestamp ) {
			$timestamp = date('c');
		}
		$user = User::identify();
		$wsse = array(
			'nonce' => $nonce,
			'timestamp' => $timestamp,
			'digest' => base64_encode( pack( 'H*' , sha1( $nonce . $timestamp . $user->password ) ) )
		);
		return $wsse;
	}

	/**
	 * function stripslashes
	 * Removes slashes from escaped strings, including strings in arrays
	 */
	public static function stripslashes( $value )
	{
		if ( is_array( $value ) ) {
			$value = array_map( array( 'Utils', 'stripslashes' ) , $value );
		}
		elseif ( !empty( $value ) && is_string( $value ) ) {
			$value = stripslashes( $value );
		}
		return $value;
	}

	/**
	 * function addslashes
	 * Adds slashes to escape strings, including strings in arrays
	 */
	public static function addslashes( $value )
	{
		if ( is_array( $value ) ) {
			$value = array_map( array( 'Utils', 'addslashes' ), $value );
		}
		else if ( !empty( $value ) && is_string( $value ) ) {
			$value = addslashes( $value );
		}
		return $value;
	}

	/**
	 * function de_amp
	 * Returns & entities in a URL querystring to their previous & glory, for use in redirects
	 * @param string $value A URL, maybe with a querystring
	 */
	public static function de_amp( $value )
	{
		$url = InputFilter::parse_url( $value );
		$url[ 'query' ] = str_replace( '&', '&', $url[ 'query' ] );
		return InputFilter::glue_url( $url );
	}

	/**
	 * function revert_magic_quotes_gpc
	 * Reverts magicquotes_gpc behavior
	 */
	public static function revert_magic_quotes_gpc()
	{
		/* We should only revert the magic quotes once per page hit */
		static $revert = true;
		if ( get_magic_quotes_gpc() && $revert ) {
			$_GET = self::stripslashes( $_GET );
			$_POST = self::stripslashes( $_POST );
			$_COOKIE = self::stripslashes( $_COOKIE );
			$revert = false;
		}
	}

	/**
	 * function quote_spaced
	 * Adds quotes around values that have spaces in them
	 * @param string A string value that might have spaces
	 * @return string The string value, quoted if it has spaces
	 */
	public static function quote_spaced( $value )
	{
		return ( strpos( $value, ' ' ) === false ) ? $value : '"' . $value . '"';
	}

	/**
	 * function implode_quoted
	 * Behaves like the implode() function, except it quotes values that contain spaces
	 * @param string A separator between each value
	 * @param	array An array of values to separate
	 * @return string The concatenated string
	 */
	public static function implode_quoted( $separator, $values )
	{
		if ( ! is_array( $values ) ) {
			$values = array();
		}
		$values = array_map( array( 'Utils', 'quote_spaced' ), $values );
		return implode( $separator, $values );
	}

	/**
	 * Returns a string of question mark parameter
	 * placeholders.
	 *
	 * Useful when building, for instance, an IN() list for SQL
	 *
	 * @param		count		Number of placeholders to put in the string
	 * @return	string	Placeholder string
	 */
	public static function placeholder_string( $count )
	{
		if ( Utils::is_traversable( $count ) ) {
			$count = count( $count );
		}
		return rtrim( str_repeat( '?,', $count ), ',' );
	}

	/**
	 * function archive_pages
	 * Returns the number of pages in an archive using the number of items per page set in options
	 * @param integer Number of items in the archive
	 * @param integer Number of items per page
	 * @returns integer Number of pages based on pagination option.
	 */
	public static function archive_pages( $item_total, $items_per_page = NULL )
	{
		if ( $items_per_page ) {
			return ceil( $item_total / $items_per_page );
		}
		return ceil( $item_total / Options::get( 'pagination' ) );
	}

	/**
	* Used with array_map to create an array of PHP stringvar-style search/replace strings using optional pre/postfixes
	*
	* $mapped_values= array_map(array('Utils', 'map_array'), $values);
	*
	* @param string $value The value to wrap
	* @param string $prefix The prefix for the returned value
	* @param string $postfix The postfix for the returned value
	* @return string The wrapped value
	*/
	public static function map_array( $value, $prefix = '{$', $postfix = '}' )
	{
		return $prefix . $value . $postfix;
	}

	/**
	 * Helper function used by debug()
	 * Not for external use.
	 */
	public static function debug_reveal( $show, $hide, $debugid, $close = false )
	{
		$reshow = $restyle = $restyle2 = '';
		if ( $close ) {
			$reshow = "onclick=\"debugtoggle('debugshow-{$debugid}');debugtoggle('debughide-{$debugid}');return false;\"";
			$restyle = "";
			$restyle2 = "";
		}
		return "$show{$restyle}$hide{$restyle2}";
	}

	/**
	 * Outputs a call stack with parameters, and a dump of the parameters passed.
	 * @params mixed Any number of parameters to output in the debug box.
	 */
	public static function debug()
	{
		$debugid = md5( microtime() );
		$tracect = 0;

		$fooargs = func_get_args();
		echo "
";
		if ( !self::$debug_defined ) {
			$output = "

			";
			echo $output;
			self::$debug_defined = true;
		}
		if ( function_exists('debug_backtrace') ) {
			$output = "
";
			$backtrace = array_reverse(debug_backtrace(), true);
			$odd = '';
			$tracect = 0;
			foreach ( $backtrace as $trace ) {
				$file = $line = $class = $type = $function = '';
				$args = array();
				extract( $trace );
				if ( isset( $class ) ) $fname = $class . $type . $function; else $fname = $function;
				if ( !isset( $file ) || $file=='' ) $file = '[Internal PHP]'; else $file = basename( $file );
				$odd = $odd == '' ? 'class="utils__odd"' : '';
				$output .= "
{$file} ({$line}):	{$fname}(";
				$comma = '';
				foreach ( (array)$args as $arg ) {
					$tracect++;
					$argout = print_r( $arg, 1 );
					$output .= $comma . Utils::debug_reveal( gettype( $arg ), htmlentities( $argout ), $debugid . $tracect, true );
					$comma = ', ';
				}
				$output .= ");
";
			}
			$output .= "
";
			echo Utils::debug_reveal( 'Call Stack', $output, $debugid );
		}
		echo "
";
		foreach ( $fooargs as $arg1 ) {
			echo '' . gettype( $arg1 ) . ' ';
			echo htmlentities( print_r( $arg1, TRUE ) ) . "
";
		}
		echo "

";
	}

	/**
	 * Outputs debug information like ::debug() but using Firebug's Console.
	 * @params mixed Any number of parameters to output in the debug box.
	 */
	public static function firedebug()
	{
		$fooargs = func_get_args();
		$output = "";
		echo $output;
	}

	/**
	 * Utils::firebacktrace()
	 *
	 * @param array $backtrace An array of backtrace details from debug_backtrace()
	 * @return string Javascript output that will display the backtrace in the Firebug console.
	 */
	public static function firebacktrace( $backtrace )
	{
		$output = '';
		extract( end( $backtrace ) );
		if ( isset( $class ) ) $fname = $class . $type . $function; else $fname = $function;
		if ( !isset( $file ) || $file=='' ) $file = '[Internal PHP]'; else $file = basename( $file );
		$output .= "console.group(\"%s(%s):  %s(...)\", \"" . basename( $file ) . "\", \"{$line}\", \"{$fname}\");\n";
		foreach ( $backtrace as $trace ) {
			$file = $line = $class = $type = $function = '';
			$args = array();
			extract( $trace );
			if ( isset( $class ) ) $fname = $class . $type . $function; else $fname = $function;
			if ( !isset( $file ) || $file=='' ) $file = '[Internal PHP]'; else $file = basename( $file );

			$output .= "console.group(\"%s(%s):  %s(%s)\", \"{$file}\", \"{$line}\", \"{$fname}\", \"";

			$output2 = $comma = $argtypes = '';
			foreach ( (array)$args as $arg ) {
				$argout = str_replace( "\n", '\n', addslashes( print_r( $arg,1 ) ) );
				//$output .= $comma . Utils::debug_reveal( gettype($arg), htmlentities($argout), $debugid . $tracect, true );
				$argtypes .= $comma . gettype( $arg );
				$output2 .= "console.log(\"$argout\");\n";
				$comma = ', ';
			}
			$argtypes = trim( $argtypes );
			$output .= "{$argtypes}\");\n{$output2}";
			$output .= "console.groupEnd();\n";
			//$output .= ");
";
		}
		return $output;
	}

	/**
	 * Crypt a given password, or verify a given password against a given hash.
	 *
	 * @todo Enable best algo selection after DB schema change.
	 *
	 * @param string $password the password to crypt or verify
	 * @param string $hash (optional) if given, verify $password against $hash
	 * @return crypted password, or boolean for verification
	 */
	public static function crypt( $password, $hash = NULL )
	{
		if ( $hash == NULL ) {
			return self::ssha512( $password, $hash );
		}
		elseif ( strlen( $hash ) > 3 ) { // need at least {, } and a char :p
			// verify
			if ( $hash{0} == '{' ) {
				// new hash from the block
				$algo = strtolower( substr( $hash, 1, strpos( $hash, '}', 1 ) - 1 ) );
				switch ( $algo ) {
					case 'sha1':
					case 'ssha':
					case 'ssha512':
					case 'md5':
						return self::$algo( $password, $hash );
					default:
						Error::raise( sprintf( _t( 'Unsupported digest algorithm "%s"' ), $algo ) );
						return FALSE;
				}
			}
			else {
				// legacy sha1
				return ( sha1( $password ) == $hash );
			}
		}
		else {
			Error::raise( _t( 'Invalid hash' ) );
		}
	}

	/**
	 * Crypt or verify a given password using SHA.
	 *
	 * Passwords should not be stored using this method, but legacy systems might require it.
	 */
	public static function sha1( $password, $hash = NULL )
	{
		$marker = '{SHA1}';
		if ( $hash == NULL ) {
			return $marker . sha1( $password );
		}
		else {
			return ( sha1( $password ) == substr( $hash, strlen( $marker ) ) );
		}
	}

	/**
	 * Crypt or verify a given password using MD5.
	 *
	 * Passwords should not be stored using this method, but legacy systems might require it.
	 */
	public static function md5( $password, $hash = NULL )
	{
		$marker = '{MD5}';
		if ( $hash == NULL ) {
			return $marker . md5( $password );
		}
		else {
			return ( md5( $password ) == substr( $hash, strlen( $marker ) ) );
		}
	}

	/**
	 * Crypt or verify a given password using SSHA.
	 * Implements the {Seeded,Salted}-SHA algorithm as per RfC 2307.
	 *
	 * @param string $password the password to crypt or verify
	 * @param string $hash (optional) if given, verify $password against $hash
	 * @return crypted password, or boolean for verification
	 */
	public static function ssha( $password, $hash = NULL )
	{
		$marker = '{SSHA}';
		if ( $hash == NULL ) { // encrypt
			// create salt (4 byte)
			$salt = '';
			for ( $i = 0; $i < 4; $i++ ) {
				$salt .= chr( mt_rand( 0, 255 ) );
			}
			// get digest
			$digest = sha1( $password . $salt, TRUE );
			// b64 for storage
			return $marker . base64_encode( $digest . $salt );
		}
		else { // verify
			// is this a SSHA hash?
			if ( ! substr( $hash, 0, strlen( $marker ) ) == $marker ) {
				Error::raise( _t( 'Invalid hash' ) );
				return FALSE;
			}
			// cut off {SSHA} marker
			$hash = substr( $hash, strlen( $marker ) );
			// b64 decode
			$hash = base64_decode( $hash );
			// split up
			$digest = substr( $hash, 0, 20 );
			$salt = substr( $hash, 20 );
			// compare
			return ( sha1( $password . $salt, TRUE ) == $digest );
		}
	}

	/**
	 * Crypt or verify a given password using SSHA512.
	 * Implements a modified version of the {Seeded,Salted}-SHA algorithm
	 * from RfC 2307, using SHA-512 instead of SHA-1.
	 *
	 * Requires the new hash*() functions.
	 *
	 * @param string $password the password to crypt or verify
	 * @param string $hash (optional) if given, verify $password against $hash
	 * @return crypted password, or boolean for verification
	 */
	public static function ssha512( $password, $hash = NULL )
	{
		$marker = '{SSHA512}';
		if ( $hash == NULL ) { // encrypt
			$salt = '';
			for ( $i = 0; $i < 4; $i++ ) {
				$salt .= chr( mt_rand( 0, 255 ) );
			}
			$digest = hash( 'sha512', $password . $salt, TRUE );
			return $marker . base64_encode( $digest . $salt );
		}
		else { // verify
			if ( ! substr( $hash, 0, strlen( $marker ) ) == $marker ) {
				Error::raise( _t( 'Invalid hash' ) );
				return FALSE;
			}
			$hash = substr( $hash, strlen( $marker ) );
			$hash = base64_decode( $hash );
			$digest = substr( $hash, 0, 64 );
			$salt = substr( $hash, 64 );
			return ( hash( 'sha512', $password . $salt, TRUE ) == $digest );
		}
	}

	/**
	 * Return an array of date information
	 * Just like getdate() but also returns 0-padded versions of day and month in mday0 and mon0
	 * @param integer $timestamp A unix timestamp
	 * @return array An array of date data
	 */
	public static function getdate( $timestamp )
	{
		$info = getdate( $timestamp );
		$info[ 'mon0' ] = substr( '0' . $info[ 'mon' ], -2, 2 );
		$info[ 'mday0' ] = substr( '0' . $info[ 'mday' ], -2, 2 );
		return $info;
	}

	/**
	 * Return a formatted date/time trying to use strftime() AND date()
	 * @param string $format The format for the date.  If it contains non-escaped percent signs, it uses strftime(),	otherwise date()
	 * @param integer $timestamp The unix timestamp of the time to format
	 * @return string The formatted time
	 */
	public static function locale_date( $format, $timestamp )
	{
		$matches = preg_split( '/((?<!\\\\)%[a-z]\\s*)/iu', $format, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
		$output = '';
		foreach ( $matches as $match ) {
			if ( $match{0} == '%' ) {
				$output .= strftime( $match, $timestamp );
			}
			else {
				$output .= date( $match, $timestamp );
			}
		}
		return $output;
	}

	/**
	 * Return a sanitized slug, replacing non-alphanumeric characters to dashes
	 * @param string $string The string to sanitize. Non-alphanumeric characters will be replaced by dashes
	 * @param string $separator The slug separator, '-' by default
	 * @return string The sanitized slug
	 */
	public static function slugify( $string, $separator = '-' )
	{
		// Decode HTML entities
		// Replace non-alphanumeric characters to dashes. Exceptions: %, _, -
		// Note that multiple separators are collapsed automatically by the preg_replace.
		// Convert all characters to lowercase.
		// Trim spaces on both sides.
		$slug = rtrim( MultiByte::strtolower( preg_replace( '/[^\p{L}\p{N}_]+/u', $separator, preg_replace( '/\p{Po}/u', '', html_entity_decode( $string ) ) ) ), $separator );
		// Let people change the behavior.
		$slug = Plugins::filter( 'slugify', $slug, $string );

		return $slug;
	}

	/**
	 * Create an HTML select tag with options and a current value
	 *
	 * @param string $name The name and id of the select control
	 * @param array $options An associative array of values to use as the select options
	 * @param string $current The value of the currently selected option
	 * @param array $properties An associative array of additional properties to assign to the select control
	 * @return string The select control markup
	 */
	public static function html_select( $name, $options, $current = NULL, $properties = array())
	{
		$output = '<select id="' . $name . '" name="' . $name . '"';
		foreach ( $properties as $key => $value ) {
			$output .= " {$key}=\"{$value}\"";
		}
		$output .= ">\n";
		foreach ( $options as $value => $text ){
			$output .= '<option value="' . $value . '"';
			if ( $current == (string)$value ) {
				$output .= ' selected="selected"';
			}
			$output .= '>' . $text . "</option>\n";
		}
		$output .= '</select>';
		return $output;
	}

	/**
	 * Creates one or more HTML checkboxes
	 * @param string The name of the checkbox element.  If there are
	 *	multiple checkboxes for the same name, this method will
	 *	automatically apply "[]" at the end of the name
	 * @param array An array of checkbox options.  Each element should be
	 *	an array containing "name" and "value".  If the checkbox
	 *	should be checked, it should have a "checked" element.
	 * @return string The HTML of the checkboxes
	 */
	public static function html_checkboxes( $name, $options )
	{
		$output = '';
		$multi = false;
		if ( count( $options > 1 ) ) {
			$multi = true;
		}
		foreach ( $options as $option ) {
			$output .= '<input type="checkbox" id="' . $option[ 'name' ] . '" name="' . $option[ 'name' ];
			if ( $multi ) {
				$output .= '[]';
			}
			$output .= '" value="' . $option[ 'value' ] . '"';
			if ( isset( $option[ 'checked' ] ) ) {
				$output .= ' checked';
			}
			$output .= '>';
		}
		return $output;
	}

	/**
	 * Trims longer phrases to shorter ones with elipsis in the middle
	 * @param string The string to truncate
	 * @param integer The length of the returned string
	 * @param bool Whether to place the ellipsis in the middle (true) or
	 * at the end (false)
	 * @return string The truncated string
	 */
	public static function truncate( $str, $len = 10, $middle = TRUE )
	{
		// make sure $len is a positive integer
		if ( ! is_numeric( $len ) || ( 0 > $len ) ) {
			return $str;
		}
		// if the string is less than the length specified, bail out
		if ( MultiByte::strlen( $str ) <= $len ) {
			return $str;
		}

		// okay.  Shuold we place the ellipse in the middle?
		if ( $middle ) {
			// yes, so compute the size of each half of the string
			$len = round( ( $len - 3 ) / 2 );
			// and place an ellipse in between the pieces
			return MultiByte::substr( $str, 0, $len ) . '...' . MultiByte::substr( $str, -$len );
		}
		else {
			// no, the ellipse goes at the end
			$len = $len - 3;
			return MultiByte::substr( $str, 0, $len ) . '...';
		}
	}

	/**
	 * Check the PHP syntax of the specified code.
	 * Performs a syntax (lint) check on the specified code testing for scripting errors.
	 *
	 * @param string $code The code string to be evaluated. It does not have to contain PHP opening tags.
	 * @return bool Returns TRUE if the lint check passed, and FALSE if the link check failed.
	 */
	public static function php_check_syntax( $code, &$error = NULL )
	{
		$b = 0;

		foreach ( token_get_all( $code ) as $token ) {
			if ( is_array( $token ) ) {
				$token = token_name( $token[0] );
			}
			switch ( $token ) {
				case 'T_CURLY_OPEN':
				case 'T_DOLLAR_OPEN_CURLY_BRACES':
				case 'T_CURLY_OPENT_VARIABLE': // This is not documented in the manual. (11.05.07)
				case '{':
					++$b;
					break;
				case '}':
					--$b;
					break;
			}
		}

		if ( $b ) {
			$error = _t( 'Unbalanced braces.' );
			return false; // Unbalanced braces would break the eval below
		}
		else {
			ob_start(); // Catch potential parse error messages
			$display_errors = ini_set( 'display_errors', 'on' ); // Make sure we have something to catch
			$error_reporting = error_reporting( E_ALL ^ E_NOTICE );
			$code = eval( ' if(0){' . $code . '}' ); // Put $code in a dead code sandbox to prevent its execution
			ini_set( 'display_errors', $display_errors ); // be a good citizen
			error_reporting( $error_reporting );
			$error = ob_get_clean();

			return false !== $code;
		}
	}

	/**
	 * Check the PHP syntax of (and execute) the specified file.
	 *
	 * @see Utils::php_check_syntax()
	 */
	public static function php_check_file_syntax( $file, &$error = NULL )
	{
		// Prepend and append PHP opening tags to prevent eval() failures.
		$code = ' ?>' . file_get_contents( $file ) . '<?php ';

		return self::php_check_syntax( $code, $error );
	}

	/**
	 * Replacement for system glob that returns an empty array if there are no results
	 *
	 * @param string $pattern The glob() file search pattern
	 * @param integer $flags Standard glob() flags
	 * @return array An array of result files, or an empty array if no results found
	 */
	public static function glob( $pattern, $flags = 0 )
	{
		if ( ! defined( 'GLOB_NOBRACE' ) || ! ( ( $flags & GLOB_BRACE ) == GLOB_BRACE ) ) {
			// this platform supports GLOB_BRACE out of the box or GLOB_BRACE wasn't requested
			$results = glob( $pattern, $flags );
		}
		elseif ( ! preg_match_all( '/\{.*?\}/', $pattern, $m ) ) {
			// GLOB_BRACE used, but this pattern doesn't even use braces
			$results = glob( $pattern, $flags ^ GLOB_BRACE );
		}
		else {
			// pattern uses braces, but platform doesn't support GLOB_BRACE
			$braces = array();
			foreach ( $m[0] as $raw_brace ) {
				$braces[ preg_quote( $raw_brace ) ] = '(?:' . str_replace( ',', '|', preg_quote( substr( $raw_brace, 1, -1 ), '/' ) ) . ')';
			}
			$new_pattern = preg_replace( '/\{.*?\}/', '*', $pattern );
			$pattern = preg_quote( $pattern, '/' );
			$pattern = str_replace( '\\*', '.*', $pattern );
			$pattern = str_replace( '\\?', '.', $pattern );
			$regex = '/' . str_replace( array_keys( $braces ), array_values( $braces ), $pattern ) . '/';
			$results = preg_grep( $regex, Utils::glob( $new_pattern, $flags ^ GLOB_BRACE) );
		}

		if ( $results === false ) $results = array();
		return $results;
	}

	/**
	 * Produces a human-readable size string.
	 * For example, converts 12345 into 12.34KB
	 *
	 * @param integer $bytesize Number of bytes
	 * @return string Human-readable string
	 */
	public static function human_size( $bytesize )
	{
		$sizes = array(
			' bytes',
			'KiB',
			'MiB',
			'GiB',
			'TiB',
			'PiB'
			);
		$tick = 0;
		$max_tick = count( $sizes ) - 1;
		while ( $bytesize > 1024 && $tick < $max_tick ) {
			$tick++;
			$bytesize /= 1024;
		}

		return sprintf( '%0.2f%s', $bytesize, $sizes[ $tick ] );
	}

	public static function truncate_log()
	{
		// Truncate the log table
		$date = HabariDateTime::date_create()->modify( '-14 days' );
		return DB::query( 'DELETE FROM {log} WHERE timestamp < ?', array( $date->sql ) );
	}

	/**
	 * Convert a single non-array variable into an array with that one element
	 *
	 * @param mixed $element Some value, either an array or not
	 * @return array Either the original array value, or the passed value as the single element of an array
	 */
	public static function single_array( $element )
	{
		if ( !is_array( $element ) ) {
			return array( $element );
		}
		return $element;
	}

	/**
	 * Return the mimetype of a file
	 *
	 * @param string $filename the path of a file
	 * @return string The mimetype of the file.
	 */
	public static function mimetype( $filename )
	{
		$mimetype = NULL;
		if ( function_exists( 'finfo_open' ) ) {
			$finfo = finfo_open( FILEINFO_MIME );
			$mimetype = finfo_file( $finfo, $filename );
			/* FILEINFO_MIME Returns the mime type and mime encoding as defined by RFC 2045.
			 * So only return the mime type, not the encoding.
			 */
			if ( ( $pos = strpos( $mimetype, ';' ) ) !== FALSE ) {
				$mimetype = substr( $mimetype, 0, $pos );
			}
			finfo_close( $finfo );
		}

		if ( empty( $mimetype ) ) {
			$pi = pathinfo( $filename );
			switch ( strtolower( $pi[ 'extension' ] ) ) {
				// hacky, hacky, kludge, kludge...
				case 'jpg':
				case 'jpeg':
					$mimetype = 'image/jpeg';
					break;
				case 'gif':
					$mimetype = 'image/gif';
					break;
				case 'png':
					$mimetype = 'image/png';
					break;
				case 'mp3':
					$mimetype = 'audio/mpeg3';
					break;
				case 'wav':
					$mimetype = 'audio/wav';
					break;
				case 'mpg':
				case 'mpeg':
					$mimetype = 'video/mpeg';
					break;
				case 'swf':
					$mimetype = 'application/x-shockwave-flash';
					break;
			}
		}
		$mimetype = Plugins::filter( 'get_mime_type', $mimetype, $filename );
		return $mimetype;
	}

	/**
	 * Returns a trailing slash or a string, depending on the value passed in
	 *
	 * @param mixed $value A trailing string value
	 * @return string A slash if true, the value if value passed, emptystring if false
	 */
	public static function trail( $value = false )
	{
		if ( $value === true ) {
			return '/';
		}
		elseif ( $value ) {
			return $value;
		}
		return '';
	}

	/**
	 * Send email
	 *
	 * @param string $to The destination address
	 * @param string $subject The subject of the message
	 * @param string $message The message itself
	 * @param array $headers An array of key=>value pairs for additional email headers
	 * @param string $parameters Additional parameters to mail()
	 * @return boolean True if sending the message succeeded
	 */
	public static function mail( $to, $subject, $message, $headers = array(), $parameters = '' )
	{
		$mail = array(
			'to' => $to,
			'subject' => $subject,
			'message' => $message,
			'headers' => $headers,
			'parameters' => $parameters,
		);
		$mail = Plugins::filter('mail', $mail);

		$handled = false;
		$handled = Plugins::filter('send_mail', $handled, $mail);
		if ( $handled ) {
			return true;
		}
		else {
			$additional_headers = array();
			foreach ( $headers as $header_key => $header_value ) {
				$header_key = trim( $header_key );
				$header_value = trim( $header_value );
				if ( strpos( $header_key . $header_value, "\n" ) === false ) {
					$additional_headers[] = "{$header_key}: {$header_value}";
				}
			}
			$additional_headers = implode( "\r\n", $additional_headers );
		}
		return mail( $to, $subject,$message, $additional_headers, $parameters );
	}

	/**
	 * Create a random password of a specific length
	 *
	 * @param integer $length Length of the password, if not provded, 10
	 * @return string A random password
	 */
	public static function random_password( $length = 10 )
	{
		$password = '';
		$character_set = '1234567890!@#$^*qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVNBM';
		$data = str_split($character_set);
		for ( $i = 0; $i < $length; $i++ ) {
			$password .= $data[rand(1, strlen($character_set)) - 1];
		}
		return $password;
	}

	/**
	 * Does a bitwise OR of all the numbers in an array
	 * @param array $input An array of integers
	 * @return int The bitwise OR of the input array
	 */
	public static function array_or( $input )
	{
		return array_reduce( $input, array( 'Utils', 'ror' ), 0 );
	}

	/**
	 * Helper function for array_or
	 */
	public static function ror( $v, $w )
	{
		return $v |= $w;
	}

	/**
	 * Checks whether the correct HTTP method was used for the request
	 *
	 * @param array $expected Expected HTTP methods for the request
	 */
	public static function check_request_method( $expected )
	{
		if ( !in_array( $_SERVER['REQUEST_METHOD'], $expected ) ) {
			if ( in_array( $_SERVER['REQUEST_METHOD'], array( 'GET', 'HEAD', 'POST', 'PUT', 'DELETE' ) ) ) {
				header( 'HTTP/1.1 405 Method Not Allowed', true, 405 );
			}
			else {
				header( 'HTTP/1.1 501 Method Not Implemented', true, 501 );
			}
			header( 'Allow: ' . implode( ',', $expected ) );
			exit;
		}
	}

	/**
	 * Returns a regex pattern equivalent to the given glob pattern
	 *
	 * @return string regex pattern with '/' delimiter
	 */
	public static function glob_to_regex( $glob )
	{
		$pattern = $glob;
		// braces need more work
		$braces = array();
		if ( preg_match_all( '/\{.*?\}/', $pattern, $m ) ) {
			foreach ( $m[0] as $raw_brace ) {
				$braces[ preg_quote( $raw_brace ) ] = '(?:' . str_replace( ',', '|', preg_quote( substr( $raw_brace, 1, -1 ), '/' ) ) . ')';
			}
		}
		$pattern = preg_quote( $pattern, '/' );
		$pattern = str_replace( '\\*', '.*', $pattern );
		$pattern = str_replace( '\\?', '.', $pattern );
		$pattern = str_replace( array_keys( $braces ), array_values( $braces ), $pattern );
		return '/' . $pattern . '/';
	}

	/**
	 * Return the port used for a specific URL scheme
	 *
	 * @param string $scheme The scheme in question
	 * @return integer the port used for the scheme
	 */
	public static function scheme_ports( $scheme = NULL )
	{
		$scheme_ports = array(
			'ftp' => 21,
			'ssh' => 22,
			'telnet' => 23,
			'http' => 80,
			'pop3' => 110,
			'nntp' => 119,
			'news' => 119,
			'irc' => 194,
			'imap3' => 220,
			'https' => 443,
			'nntps' => 563,
			'imaps' => 993,
			'pop3s' => 995,
		);
		if ( is_null( $scheme ) ) {
			return $scheme_ports;
		}
		return $scheme_ports[ $scheme ];
	}

	/**
	 * determines if the given that is travesable in foreach
	 *
	 * @param mixed $data
	 * @return bool
	 */
	public static function is_traversable( $data )
	{
		return ( is_array( $data ) || ( $data instanceof Traversable && $data instanceof Countable ) );
	}

	/**
	* Get the remote IP address, but try and take into account users who are
	* behind proxies, whether they know it or not.
	* @return The client's IP address.
	*/
	public static function get_ip()
	{
		if ( $_SERVER["HTTP_CLIENT_IP"] ) {
			return $_SERVER["HTTP_CLIENT_IP"];
		}
		else if ( $_SERVER["HTTP_FORWARDED"] ) {
			return $_SERVER["HTTP_FORWARDED"];
		}
		else if ( $_SERVER["HTTP_X_FORWARDED"] ) {
			return $_SERVER["HTTP_X_FORWARDED"];
		}
		else if ( $_SERVER["HTTP_X_FORWARDED_FOR"] ) {
			return $_SERVER["HTTP_X_FORWARDED_FOR"];
		}
		else {
			return $_SERVER["REMOTE_ADDR"];
		}
	}

	/**
	* Convenience function to call htmlspecialchars() with the correct flags and encoding.
	* See http://php.net/manual/en/function.htmlspecialchars.php for details on the parameters
	* and purpose of the function.
	*
	* @param $string. string. The string to escape
	* @param $quote_flag. integer. Sets what quotes and doublequotes are replaced
	* @param $encoding. string. The encoding of the passed string
	* @param $double_enc. boolean. Flag to tell whether to double escape affected characters or not
	*
	* @return The escaped string
	*/
	public static function htmlspecialchars( $string, $quote_flag = ENT_COMPAT, $encoding = 'UTF-8' )
	{
		return htmlspecialchars( $string, $quote_flag, $encoding );
	}

	/**
	* Convenience function to find a usable PCRE regular expression
	* delimiter for a particular string.  (I.e., some character that
	* *isn't* found in the string.)
	*
	* @param $string. string. The string for which to find a delimiter.
	* @param $choices. string. Delimiters from which to choose one.
	* @param $encoding. string. The encoding of the passed string
	*
	* @return A valid regex delimiter, or null if none of the choices work.
	*/
	public static function regexdelim( $string, $choices = NULL )
	{
		/*
		 * Supply some default possibilities for delimiters if we
		 * weren't given an explicit list.
		 */
		if ( ! isset( $choices ) )
		{
			$choices = sprintf( '%c%c%c%c%c%c%c',
					   167,      /* § */
					   164,      /* ¤ */
					   165,      /* ¥ */
					   ord( '`' ),
					   ord( '~' ),
					   ord( '%' ),
					   ord( '#' )
			    );
		}
		$a_delims = str_split( $choices );
		/*
		 * Default condition is 'we didn't find one.'
		 */
		$delim = NULL;
		/*
		 * Check for each possibility by scanning the text for it.
		 * If it isn't found, it's a valid choice, so break out of the
		 * loop.
		 */
		foreach ( $a_delims as $tdelim )
		{
			if ( ! strstr( $string, $tdelim ) )
			{
				$delim = $tdelim;
				break;
			}
		}
		return $delim;
	}
}
?>


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

/**
 * Class to create and format UUIDs.
 *
 */
class UUID
{
	private $uuid = array();

	/**
	 * Create a UUID (Universally Unique IDentifier) as per RfC 4122.
	 *
	 * Currently, only version 4 UUIDs are supported (Section 4.4,
	 * "Algorithms for Creating a UUID from Truly Random or Pseudo-Random Numbers").
	 *
	 * @param int $version UUID version to generate (currently, only version 4 is supported)
	 */
	public function __construct( $version = 4 )
	{
		$uuid = array();
		for ( $i = 0; $i < 16; $i++ ) {
			$uuid[] = mt_rand( 0, 255 );
		}
		// variant (bit 6 = 1, bit 7 = 0)
		$uuid[8] = ( $uuid[8] & 0x3f ) | 0x80;
		/* // weird byte orders make my head hurt!
		// version (bits 4-7 = 0100);
		$uuid[7] = ( $uuid[7] & 0x0f ) | 0x40;
		*/
		// version (bits 12-15 = 0100)
		$uuid[6] = ( $uuid[6] & 0x0f ) | 0x40;

		$this->uuid = $uuid;
	}

	/**
	 * @return a string representation of this object.
	 */
	public function __toString()
	{
		return $this->get_hex();
	}

	/**
	 * @return the generated UUID as an array of bytes
	 */
	public function get_array()
	{
		return $this->uuid;
	}

	/**
	 * @return the generated UUID as a string of bytes
	 */
	public function get_raw()
	{
		return implode( '', array_map( 'chr', $this->uuid ) );
	}

	/**
	 * @return the canonical hexadecimal representation of the generated UUID
	 */
	public function get_hex()
	{
		$uuid_hex = '';
		for ( $i = 0; $i < 16; $i++ ) {
			if ( 4==$i || 6==$i || 8==$i || 10==$i ) {
				$uuid_hex.= '-';
			}
			$uuid_hex.= sprintf( '%02x' , $this->uuid[$i] );
		}

		return $uuid_hex;
	}

	/**
	 * Create a UUID and return its canonical hexadecimal representation.
	 *
	 * @return the canonical hexadecimal representation of the generated UUID
	 */
	public static function get()
	{
		$uuid = new UUID();
		return $uuid->get_hex();
	}

}

?>

repo_name: DaSourcerer/habari


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

/**
* Habari Version Class
*
* Base class for managing metadata about various Habari objects
*
*/
class Version
{
	// DB and API versions are aligned with the SVN revision
	// number in which they last changed.
	const DB_VERSION = 4226;
	const API_VERSION = 4077;

	const HABARI_VERSION = '0.7-alpha';

	// This string contains the URL to the Habari SVN repository used for this working copy or export
	const HABARI_SVN_HEAD_URL = '$HeadURL$';
	// This string contains the SVN revision this file was last updated in
	const HABARI_SVN_REV = '$Revision$';

	/**
	 * Get the database version
	 *
	 * @return integer The revision in which the most recent database change took place
	 */
	public static function get_dbversion()
	{
		return Version::DB_VERSION;
	}

	/**
	 * Get the API version
	 *
	 * @return integer The revision in which the most recent API change took place
	 */
	public static function get_apiversion()
	{
		return Version::API_VERSION;
	}

	/**
	 * Get the Habari version
	 *
	 * @return string A version_compare()-compatible string of this version of Habari
	 * @see version_compare
	 */
	public static function get_habariversion()
	{
		return Version::HABARI_VERSION;
	}

	/**
	 * Determine whether this working copy or export was created from a subversion development branch
	 *
	 * @return boolean True if this is a development version, false if not
	 */
	public static function is_devel()
	{
		return strpos(Version::HABARI_SVN_HEAD_URL, '/trunk/') !== false || strpos(Version::HABARI_SVN_HEAD_URL, '/branches/') !== false;
	}

	/**
	 * Store the current database version in the options table
	 */
	public static function save_dbversion()
	{
		Options::set('db_version', Version::DB_VERSION);
	}

	/**
	 * Determine if the database needs to be updated based on the source database version being newer than the schema last applied to the database
	 *
	 * @return boolean True if an update is needed
	 */
	public static function requires_upgrade()
	{
		if (Options::get('db_version') < Version::DB_VERSION){
			return true;
		}
		return false;
	}

	/**
	 * Attempt to return the checkout revision number of the source tree
	 *
	 * @return int The revision number
	 */
	public static function get_svn_revision()
	{
		$rev = 0;
		// Cheating!
		$stash_file = HABARI_PATH . '/.svn/entries';
		if(file_exists($stash_file)) {
			$info = file_get_contents($stash_file);
			$info = explode("\n", $info);
			if(strpos($info[4], 'svn.habariproject.org/habari/') !== false) {
				$rev = intval(trim($info[3]));
			}
		}
		if($rev == 0) {
			$rev = intval(preg_replace('/[^0-9]/', '', Version::HABARI_SVN_REV));
		}
		return $rev;
	}
}

?>


code:
<?php

/**
 * Vocabulary Class
 *
 * Vocabulary is part of the taxonomy system. A vocabulary holds terms and has features.
 *
 */

class Vocabulary extends QueryRecord
{
	/**
	 *
	 * @var Array of strings $features. An array of the features that limit the behaviour of the vocabulary.
	 * Default values can include:
	 *		hierarchical:	The vocabulary's terms exist in a parent child hierarchy
	 *		required:
	 *		multiple:		More than one term in the vocabulary can be associated with an object
	 *		free:			Terms within the vocabulary can have any value
	 */
	public static $features = array( 'hierarchical', 'required', 'multiple', 'free' );

	/**
	 * Return the defined database columns for a Vocabulary.
	 * @return array Array of columns in the Vocabulary table
	 **/
	public static function default_fields()
	{
		return array(
			'id' => 0,
			'name' => '',
			'description' => '',
			'features' => array(),
		);
	}

	/**
	 * Vocabulary constructor
	 * Creates a Vocabulary instance
	 *
	 * @param array $paramarray an associative array of initial vocabulary values
	 **/
	public function __construct( $paramarray = array() )
	{
		// Defaults
		$this->fields = array_merge(
			self::default_fields(),
			$this->fields
		);

		parent::__construct( $paramarray );

		$this->exclude_fields( 'id' );
		if( is_string( $this->features ) ) {
			$this->features = unserialize( $this->features );
		}
	}

	/**
	 * Create a vocabulary and save it.
	 *
	 * @param array $paramarray An associative array of vocabulary fields
	 * @return Vocabulary The new vocabulary object
	 */
	static function create( $paramarray )
	{
		$vocabulary = new Vocabulary( $paramarray );
		$vocabulary->insert();
		return $vocabulary;
	}

	/**
	 * function __get
	 * Overrides QueryRecord __get to implement custom object properties
	 * @param string Name of property to return
	 * @return mixed The requested field value
	 **/
	public function __get( $name )
	{
		$out = parent::__get( $name );
		switch($name) {
			case 'features':
				if ( ! is_array( $out ) ) {
					$out = unserialize( $out );
				}
				break;
		}
		if ( is_null( $out ) ) {
			$out = in_array( $name, $this->features );
		}
		return $out;
	}

	/**
	 * Return a Vocabulary by name.
	 * @return Vocabulary The requested vocabulary
	 **/
	public static function get( $name )
	{
		return DB::get_row( 'SELECT * FROM {vocabularies} WHERE name=?', array( $name ), 'Vocabulary' );
	}

	/**
	 * Return a Vocabulary by id
	 *
	 * @param integer $id The id of the vocabulary
	 * @return Vocabulary The object requested
	 */
	public static function get_by_id( $id )
	{
		return DB::get_row( 'SELECT * FROM {vocabularies} WHERE id=?', array( $id ), 'Vocabulary' );
	}

	/**
	 * Return all vocabularies as Vocabulary objects
	 *
	 * @return array An array of Vocabulary objects
	 */
	public static function get_all()
	{
		return DB::get_results( 'SELECT * FROM {vocabularies}', array(), 'Vocabulary' );
	}

	/**
	 * Rename a Vocabulary.
	 * @return boolean true if the Vocabulary was renamed, false otherwise
	 **/
	public static function rename( $name, $newname )
	{
		$vocab = Vocabulary::get( $name );
		$vocab->name = $newname;
		$result = $vocab->update();

		return $result;
	}

	/**
	 * Return the names of all vocabularies
	 * @return array Array of Vocabulary names
	 **/
	public static function names()
	{
		$names = array();
		$vocabs = DB::get_results( 'SELECT name FROM {vocabularies}' );
		foreach ( $vocabs as $vocab ) {
			$names[] = $vocab->name;
		}
		return $names;
	}

	/**
	 * Return the Term objects associated to that type of object with that id in any vocabulary.
	 * For example, return all terms associated with a particular post, from all vocabularies.
	 *
	 * @return array Array of Vocabulary names
	 **/
	public static function get_all_object_terms( $object_type, $id )
	{
		$results = DB::get_results(
			'SELECT id, term, term_display, vocabulary_id, mptt_left, mptt_right FROM {terms}
			JOIN {object_terms} ON {terms}.id = {object_terms}.term_id
			WHERE {object_terms}.object_type_id = ?
				AND {object_terms}.object_id = ?',
			array( self::object_type_id( $object_type ), $id ),
			'Term'
		);

		return $results;
	}

	/**
	 * Determine whether a vocabulary exists
	 * @param string $name a vocabulary name
	 * @return bool whether the vocabulary exists or not
	**/
	public static function exists( $name )
	{
		return ( (int) DB::get_value( "SELECT COUNT(id) FROM {vocabularies} WHERE name=?", array( $name ) ) > 0 );
	}

	/**
	 * function insert
	 * Saves a new vocabulary to the vocabularies table
	 */
	public function insert()
	{
		// Don't allow duplicate vocabularies
		if ( Vocabulary::exists( $this->fields['name'] ) ) {
			return false;
		}

		// Let plugins disallow and act before we write to the database
		$allow = true;
		$allow = Plugins::filter( 'vocabulary_insert_allow', $allow, $this );
		if ( !$allow ) {
			return false;
		}
		Plugins::act( 'vocabulary_insert_before', $this );

		// Serialize features before they are stored
		if ( isset( $this->newfields['features'] ) ) {
			$this->newfields['features'] = serialize( $this->newfields['features'] );
		}
		if ( isset( $this->fields['features'] ) ) {
			$this->fields['features'] = serialize( $this->fields['features'] );
		}
		$result = parent::insertRecord( DB::table( 'vocabularies' ) );

		// Make sure the id is set in the vocabulary object to match the row id
		$this->newfields['id'] = DB::last_insert_id();

		// Update the vocabulary's fields with anything that changed
		$this->fields = array_merge( $this->fields, $this->newfields );
		// And unserialize the features
		if ( isset( $this->fields['features'] ) ) {
			$this->fields['features'] = unserialize( $this->fields['features'] );
		}

		// We've inserted the vocabulary, reset newfields
		$this->newfields = array();

		EventLog::log( sprintf( _t( 'New vocabulary %1$s (%2$s)' ), $this->id, $this->name ), 'info', 'content', 'habari' );

		// Let plugins act after we write to the database
		Plugins::act( 'vocabulary_insert_after', $this );

		return $result;
	}

	/**
	 * function update
	 * Updates an existing vocabulary in the vocabularies table
	 */
	public function update()
	{
		// Don't allow duplicate vocabularies
		if ( isset( $this->newfields['name'] ) && Vocabulary::exists( $this->newfields['name'] ) ) {
			return false;
		}

		// Let plugins disallow and act before we write to the database
		$allow = true;
		$allow = Plugins::filter( 'vocabulary_update_allow', $allow, $this );
		if ( !$allow ) {
			return;
		}
		Plugins::act( 'vocabulary_update_before', $this );

		if ( isset( $this->newfields['features'] ) ) {
			$this->newfields['features'] = serialize( $this->newfields['features'] );
		}
		if ( isset( $this->fields['features'] ) ) {
			$this->fields['features'] = serialize( $this->fields['features'] );
		}
		$result = parent::updateRecord( '{vocabularies}', array( 'id' => $this->id ) );

		$this->fields = array_merge( $this->fields, $this->newfields );
		$this->newfields = array();

		if ( isset( $this->fields['features'] ) ) {
			$this->fields['features'] = unserialize( $this->fields['features'] );
		}

		// Let plugins act after we write to the database
		Plugins::act( 'vocabulary_update_after', $this );

		return $result;
	}

	/**
	 * Delete an existing vocabulary
	 */
	public function delete()
	{
		// Let plugins disallow and act before we write to the database
		$allow = true;
		$allow = Plugins::filter( 'vocabulary_delete_allow', $allow, $this );
		if ( !$allow ) {
			return;
		}
		Plugins::act( 'vocabulary_delete_before', $this );

		// Get the ids for all this vocabulary's terms
		$ids = DB::get_column('SELECt id FROM {terms} WHERE vocabulary_id = ?', array( $this->id ) );
		// Delete the records from object_terms for those ids
		$placeholder = Utils::placeholder_string( count( $ids ) );
		DB::query("DELETE FROM {object_terms} WHERE term_id IN ($placeholder)", $ids );

		// Delete this vocabulary's terms
		DB::delete( '{terms}', array( 'vocabulary_id' => $this->id ) );

		// Finally, delete the vocabulary
		$result = parent::deleteRecord( '{vocabularies}', array( 'id'=>$this->id ) );
		EventLog::log( sprintf(_t('Vocabulary %1$s (%2$s) deleted.'), $this->id, $this->name), 'info', 'content', 'habari' );

		// Let plugins act after we write to the database
		Plugins::act( 'vocabulary_delete_after', $this );
		return $result;
	}

	/**
	 * Adds a term to the vocabulary. Returns a Term object. null parameters append the term to the end of any hierarchies.
	 * @return Term The Term object added
	 **/
	public function add_term( $term, $target_term = null, $before = FALSE )
	{
		$new_term = $term;
		if ( is_string( $term ) ) {
			$new_term = new Term( array( 'term_display' => $term ) );
		}

		$new_term->vocabulary_id = $this->id;

		$ref = 0;
		DB::begin_transaction();

		// If there are terms in the vocabulary, work out the reference point
		if ( !$this->is_empty() ) {

			if ( $this->hierarchical ) {
				// If no parent is specified, put the new term after the last term
				if ( null == $target_term ) {
					$ref = DB::get_value( 'SELECT mptt_right FROM {terms} WHERE vocabulary_id=? ORDER BY mptt_right DESC LIMIT 1', array($this->id) );
				}
				else {
					if ( ! $before ) {
						$ref = $target_term->mptt_right - 1;
					}
					else {
						$ref = $target_term->mptt_left - 1;
					}
				}
			}
			else {
				// If no before_term is specified, put the new term after the last term
				if ( ! $before ) {
					$ref = DB::get_value( 'SELECT mptt_right FROM {terms} WHERE vocabulary_id=? ORDER BY mptt_right DESC LIMIT 1', array($this->id) );
				}
				else {
					$ref = $target_term->mptt_left - 1;
				}
			}

			// Make space for the new node
			$params = array( 'vocab_id' => $this->id, 'ref' => $ref);
			$res = DB::query('UPDATE {terms} SET mptt_right=mptt_right+2 WHERE vocabulary_id=:vocab_id AND mptt_right>:ref', $params);
			if( ! $res ) {
				DB::rollback();
				return FALSE;
			}
			$res = DB::query('UPDATE {terms} SET mptt_left=mptt_left+2 WHERE vocabulary_id=:vocab_id AND mptt_left>:ref', $params);
			if( ! $res ) {
				DB::rollback();
				return FALSE;
			}

		}

		// Set the right and left appropriately
		$new_term->mptt_left = $ref + 1;
		$new_term->mptt_right = $ref + 2;

		// Insert the new node
		$result = $new_term->insert();
		if ( $result ) {
			DB::commit();
			return $new_term;
		}
		else {
			DB::rollback();
			return FALSE;
		}

	}

	/**
	 * Gets the term object by id. No parameter returns the root Term object.
	 * @param integer $term_id The id of the term to fetch, or null for the root node
	 * @return Term The Term object requested
	 **/
	public function get_term($term_id = null)
	{
		return Term::get($this->id, $term_id);
	}

	/**
	 * Gets the Term objects associated to that type of object with that id in this vocabulary
	 * For example, return all terms in this vocabulary that are associated with a particular post
	 *
	 * @param String the name of the object type
	 * @param integer The id of the object for which you want the terms
	 * @return Array The Term objects requested
	 **/
	public function get_object_terms($object_type, $id)
	{
		$results = DB::get_results(
			'SELECT id, term, term_display, vocabulary_id, mptt_left, mptt_right FROM {terms}
			JOIN {object_terms} ON {terms}.id = {object_terms}.term_id
			WHERE {terms}.vocabulary_id = ?
				AND {object_terms}.object_type_id = ?
				AND {object_terms}.object_id = ?',
			array( $this->id, self::object_type_id( $object_type ), $id ),
			'Term'
		);

		return $results;
	}

	/**
	 * Sets the Term objects associated to that type of object with that id in this vocabulary
	 *
	 * @param String the name of the object type
	 * @param Integer The id of the object for which you want the terms
	 * @param Array. The names of the terms to associate
	 *
	 * @return boolean. Whether the associations were successful or not
	 **/
	public function set_object_terms( $object_type, $id, $terms )
	{
		// no terms? then let's get out'a'here
		if ( count( $terms ) == 0 ) {
			Plugins::act( 'term_detach_all_from_object_before', $this->id );

			$results = $this->get_object_terms( $object_type, $id );
			foreach ( $results as $term ) {
				$term->dissociate( $object_type, $id );
			}

			Plugins::act( 'term_detach_all_from_object_after', $this->id );
			return TRUE;
		}
		/*
		 * First, let's clean the incoming tag text array, ensuring we have
		 * a unique set of tag texts and slugs.
		 */
		$term_ids_to_object = $clean_terms = array();
		foreach ( ( array ) $terms as $term )
			if ( ! in_array( $term, array_keys( $clean_terms ) ) )
				if ( ! in_array( $slug = Utils::slugify( $term ), array_values( $clean_terms ) ) )
					$clean_terms[$term] = $slug;

		/* Now, let's insert any *new* term display text or slugs into the terms table */
		$placeholders = Utils::placeholder_string( count( $clean_terms ) );
		$sql_terms_exist = "SELECT id, term_display, term
			FROM {terms}
			WHERE term_display IN ({$placeholders})
			OR term IN ({$placeholders})
			AND vocabulary_id = ?";
		$params = array_merge( array_keys( $clean_terms ), array_values( $clean_terms ), (array)$this->id );
		$existing_terms = DB::get_results( $sql_terms_exist, $params, 'Term' );
		if ( count( $existing_terms ) > 0 ) {
			/* Terms exist which match the term text or the term */
			foreach ( $existing_terms as $existing_term ) {
				/*
				 * Term exists.
				 * Attach object to term, then remove the term from creation list.
				 */
				$existing_term->associate( $object_type, $id );
				$term_ids_to_object[] = $existing_term->id;

				/*
				 * We remove it from the clean_terms collection as we only
				 * want to add to the terms table those terms which don't already exist
				 */
				if ( in_array( $existing_term->term_display, array_keys( $clean_terms ) ) ) {
					unset( $clean_terms[$existing_term->term_display] );
				}
				if ( in_array( $existing_term->term, array_values( $clean_terms ) ) ) {
					foreach ( $clean_terms as $text => $slug ) {
						if ( $slug == $existing_term->term ) {
							unset( $clean_terms[$text] );
							break;
						}
					}
				}
			}
		}

		/*
		 * $clean_terms now contains an associative array of terms
		 * we need to add to the main terms table, so add them
		 *
		 */
		foreach ( $clean_terms as $new_term_text => $new_term_slug ) {
			$term = new Term( array( 'term_display' => $new_term_text, 'term' => $new_term_slug ) );
			$this->add_term( $term );
			$term->associate( $object_type, $id );
			$term_ids_to_object[] = $term->id;
		}

		/*
		 * Finally, remove the terms which are no longer associated with the object.
		 */
		$repeat_questions = Utils::placeholder_string( count( $term_ids_to_object ) );
		$sql_delete = "SELECT term_id FROM {object_terms}
			JOIN {terms} ON term_id = {terms}.id
			WHERE object_id = ? AND term_id NOT IN ({$repeat_questions}) AND object_type_id = ?
			AND {terms}.vocabulary_id = ?";
		$params = array_merge( (array) $id, array_values( $term_ids_to_object ), (array)Vocabulary::object_type_id( $object_type ), (array)$this->id );

		$result = DB::get_column( $sql_delete, $params );

		foreach ( $result as $t ) {
			$term = $this->get_term( $t );
			$term->dissociate( $object_type, $id );
		}

		return TRUE;
	}

	/**
	 * Remove the term from the vocabulary.  Convenience method to ->get_term('foo')->delete().
	 *
	 **/
	public function delete_term( $term )
	{
		if ( ! is_object( $term ) ) {
			$term = $this->get_term( $term );
		}

		// TODO How should we handle deletion of a term with descendants?
		// Perhaps a $keep_children flag to move descendants to be descendants of
		// the deleted term's parent? Terms should not change the left and right
		// values of other terms, and thus their deletion should only occur through
		// the vocabulary to which they belong. Is it feasible to restrict this?
		// For the moment, just delete the descendants
		$params = array( $this->id, $term->mptt_left, $term->mptt_right );
		DB::query( 'DELETE from {terms} WHERE vocabulary_id=? AND mptt_left>? AND mptt_right<?', $params );

		// Fix mptt_left and mptt_right values for other nodes in the vocabulary
		$offset = $term->mptt_right - $term->mptt_left + 1;
		$ref = $this->mptt_left;
		$params = array( $offset, $this->id, $term->mptt_left );

		// Delete the term
		$term->delete();

		// Renumber left and right values of other nodes appropriately
		DB::query( 'UPDATE {terms} SET mptt_right=mptt_right-? WHERE vocabulary_id=? AND mptt_right>?', $params );
		DB::query( 'UPDATE {terms} SET mptt_left=mptt_left-? WHERE vocabulary_id=? AND mptt_left>?', $params );

	}

	/**
	 * Check if this vocabulary is empty.
	 *
	 **/
	public function is_empty()
	{
		return ( (int) DB::get_value( "SELECT COUNT(id) FROM {terms} WHERE vocabulary_id=?", array( $this->id ) ) == 0 );
	}

	/**
	 * Retrieve the vocabulary
	 * @return Array The Term objects in the vocabulary, in tree order
	 **/
	public function get_tree($orderby = 'mptt_left ASC')
	{
		return DB::get_results( "SELECT * FROM {terms} WHERE vocabulary_id=:vid ORDER BY {$orderby}", array('vid' => $this->id), 'Term' );
	}

	/**
	 * Retrieve the vocabulary as an associative array suitable for FormUI select controls
	 * @return Array The Term objects in the vocabulary, in tree order
	 **/
	public function get_options()
	{
		$tree = $this->get_tree( 'mptt_left ASC' );
		$output = array();
		$lastright = $lastleft = reset($tree)->mptt_left;
		$indent = 0;
		$stack = array();
		foreach( $tree as $term ) {
			while ( count( $stack ) > 0 && end( $stack )->mptt_right < $term->mptt_left ) {
				array_pop( $stack );
			}
			$output[$term->id] = str_repeat( '- ', count( $stack )) . $term->term_display;
			$stack[] = $term;
		}

		return $output;
	}

	/**
	 * Get all root elements in this vocabulary
	 * @return Array The root Term objects in the vocabulary
	 */
	public function get_root_terms()
	{
		/**
		 * If we INNER JOIN the terms table with itself on ALL the descendants,
		 * then descendants one level down are listed once, two levels down are listed twice,
		 * etc. If we return only those terms which appear once, we get root elements.
		 * ORDER BY NULL to avoid the MySQL filesort.
		 */
		$query = <<<SQL
SELECT child.term as term,
	child.term_display as term_display,
	child.mptt_left as mptt_left,
	child.mptt_right as mptt_right,
	child.vocabulary_id as vocabulary_id
FROM {terms} as parent
INNER JOIN {terms} as child
	ON child.mptt_left BETWEEN parent.mptt_left AND parent.mptt_right
	AND child.vocabulary_id = parent.vocabulary_id
WHERE parent.vocabulary_id = ?
GROUP BY child.term
HAVING COUNT(child.term)=1
ORDER BY NULL
SQL;
		return DB::get_results( $query, array( $this->id ), 'Term' );
	}

	/**
	 * inserts a new object type into the database, if it doesn't exist
	 * @param string The name of the new post type
	 * @param bool Whether the new post type is active or not
	 * @return none
	**/
	public static function add_object_type( $type )
	{
		$params = array( 'name' => $type );
		if ( ! DB::exists( "{object_types}", $params ) ) {
			DB::insert( "{object_types}", $params );
		}
	}

	/**
	 * Return the object type id for a named object, such as a post
	 *
	 * @param string $name The type of object
	 * @return integer The id of the object type
	*/
	public static function object_type_id( $type )
	{
		$id = (int)DB::get_value( "SELECT id FROM {object_types} WHERE name = ?", array( $type ) );
		return $id;

	}

	/**
	 * Moves a term within the vocabulary. Returns a Term object. null parameters append the term to the end of any hierarchies.
	 * @return Term The Term object moved
	 **/
	public function move_term( $term, $target_term = null, $before = FALSE )
	{
		// We assume that the arguments passed are valid terms. Check them before calling this.

		// If there are terms in the vocabulary, work out the reference point
		if ( !$this->is_empty() ) {

			$source_left = $term->mptt_left;
			$source_right = $term->mptt_right;
			$range = $source_right - $source_left + 1;

			$nodes_moving = ( $range ) / 2; // parent and descendants

 			DB::begin_transaction();

			// Move the source nodes out of the way by making mptt_left and mptt_right negative
			$source_to_temp = $source_right + 1; // move the terms so that the rightmost one's mptt_right is -1
			$params = array( 'displacement' => $source_to_temp, 'vocab_id' => $this->id, 'range_left' => $source_left, 'range_right' => $source_right );
			$res = DB::query( '
				UPDATE {terms}
				SET
					mptt_left = mptt_left - :displacement,
					mptt_right = mptt_right - :displacement
				WHERE
					vocabulary_id = :vocab_id
					AND mptt_left BETWEEN :range_left AND :range_right
				',
				$params
			);

 			if( ! $res ) {
 				DB::rollback();
				return FALSE;
			}

			// Close the gap in the tree created by moving those nodes out
			$params = array( 'range' => $range, 'vocab_id' => $this->id, 'source_left' => $source_left );
			$res = DB::query( 'UPDATE {terms} SET mptt_left=mptt_left-:range WHERE vocabulary_id=:vocab_id AND mptt_left > :source_left', $params );
			if( ! $res ) {
 				DB::rollback();
				return FALSE;
			}

			$res = DB::query( 'UPDATE {terms} SET mptt_right=mptt_right-:range WHERE vocabulary_id=:vocab_id AND mptt_right > :source_left', $params );
			if( ! $res ) {
 				DB::rollback();
				return FALSE;
			}

			// Determine the insertion point mptt_target
			if ( $this->hierarchical ) {
				if ( null == $target_term ) {
					// If no target is specified, put the new term after the last term
					$mptt_target = DB::get_value( 'SELECT MAX(mptt_right) FROM {terms} WHERE vocabulary_id=?', array( $this->id ) ) + 1;
				}
				else {
					if ( FALSE == $before ) {
						$mptt_target = DB::get_value( 'SELECT mptt_right FROM {terms} WHERE vocabulary_id=? AND id = ?', array( $this->id, $target_term->id ) );
					}
					else {
						$mptt_target = DB::get_value( 'SELECT mptt_left FROM {terms} WHERE vocabulary_id=? AND id = ?', array( $this->id, $target_term->id ) );
					}
				}
			}
			else {
				// vocabulary is not hierarchical
				if ( FALSE != $before ) {
					$mptt_target = DB::get_value( 'SELECT mptt_left FROM {terms} WHERE vocabulary_id=? AND id = ?', array( $this->id, $target_term->id ) );
				}
				else {
					$mptt_target = DB::get_value( 'SELECT MAX(mptt_right) FROM {terms} WHERE vocabulary_id=?', array( $this->id ) ) + 1;
				}
			}
			$temp_left = $source_left - $source_to_temp;
			$temp_to_target = $mptt_target - $temp_left;

			// Create space in the tree for the insertion
			$params = array( 'vocab_id' => $this->id, 'range' => $range, 'mptt_target' => $mptt_target );
			$res = DB::query( 'UPDATE {terms} SET mptt_left=mptt_left+:range WHERE vocabulary_id=:vocab_id AND mptt_left >= :mptt_target', $params );
			if( ! $res ) {
 				DB::rollback();
				return FALSE;
			}

			$res = DB::query( 'UPDATE {terms} SET mptt_right=mptt_right+:range WHERE vocabulary_id=:vocab_id AND mptt_right >= :mptt_target', $params );
			if( ! $res ) {
 				DB::rollback();
				return FALSE;
			}

			// Move the temp nodes into the space created for them
			$params = array( 'vocab_id' => $this->id, 'temp_to_target' => $temp_to_target );
			$res = DB::query( 'UPDATE {terms} SET mptt_left=mptt_left+:temp_to_target WHERE vocabulary_id=:vocab_id AND mptt_left < 0', $params );
			if( ! $res ) {
 				DB::rollback();
				return FALSE;
			}

			$res = DB::query( 'UPDATE {terms} SET mptt_right=mptt_right+:temp_to_target WHERE vocabulary_id=:vocab_id AND mptt_right < 0', $params );
			if( ! $res ) {
 				DB::rollback();
				return FALSE;
			}

			// Success!
 			DB::commit();

			// @todo: need to return the updated term
			return $term;
		}
		return FALSE;
	}

}

?>


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

/**
 * XMLRPC Binary type
 * Used to hold binary data for transmission in XMLRPC calls.
 *
 */
class XMLRPCBinary
{
	public $data;

	public function load_from_file($filename)
	{
		$this->data = file_get_contents($filename);
	}

	public function __construct($data = null)
	{
		if(isset($data)) {
			$this->data = $data;
		}
	}
}

?>


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

/**
 * Create an XMLRPC client connection
 *
 * To Use:
 *
 * // Create the object using the XMLRPC entrypoint and scope.
 * $rpc= new XMLRPCClient('http://rpc.pingomatic.com', 'weblogUpdates');
 * // Make a weblogUpdates.ping call on the client.
 * $result= $rpc->ping('Blog name', 'http://example.com');
 *
 * // Change the scope on the existing client object.
 * $rpc->set_scope('system');
 * // Make a system.listMethods call on the client.
 * $methods= $rpc->listMethods();
 *
 *
 * @todo Handle XMLRPC fault results by returning some error value.
 */
class XMLRPCClient
{
	private $entrypoint;
	private $scope = 'system';

	/**
	 * Create the XMLRPCClient
	 *
	 * @param string $xmlrpc_entrypoint The entrypoint of the remote server
	 */
	public function __construct($xmlrpc_entrypoint, $scope = null)
	{
		$this->entrypoint = $xmlrpc_entrypoint;
		if(isset($scope)) {
			$this->scope = $scope;
		}
	}

	/**
	 * Set the scope of any subsequent function calls
	 * The default scope is 'system'.
	 *
	 * @param string $scope The scope to use
	 */
	public function set_scope($scope)
	{
		$this->scope = $scope;
	}

	/**
	 * Allow method overloading for this class.
	 * This method allows any method name to be called on this object.  The method
	 * called is the method called via RPC, within the scope defined in $this->scope.
	 *
	 * @param string $fname The function name to call
	 * @param array $args An array of arguments that were called with the function
	 * @return array The result array
	 */
	public function __call($fname, $args)
	{
		if($this->scope != '') {
			$rpc_method = "{$this->scope}.{$fname}";
		}
		else {
			$rpc_method = $fname;
		}

		$rpx = new SimpleXMLElement('<methodCall/>');
		$rpx->addChild('methodName', $rpc_method);
		if(count($args) > 0) {
			$params = $rpx->addchild('params');
			foreach($args as $arg) {
				$param = $params->addchild('param');
				XMLRPCUtils::encode_arg($param, $arg);
			}
		}

		$request = new RemoteRequest($this->entrypoint, 'POST');
		$request->add_header('Content-Type: text/xml;charset=utf-8');
		$request->set_body($rpx->asXML());

		$request->execute();

		if ( $request->executed() ) {
			$response = $request->get_response_body();
			$enc = mb_detect_encoding($response);
			$responseutf8 = mb_convert_encoding($response, 'UTF-8', $enc);
			try {
				$bit = ini_get('error_reporting');
				error_reporting($bit && !E_WARNING);
				$responsexml = new SimpleXMLElement($responseutf8);
				error_reporting($bit);
				$tmp = $responsexml->xpath('//params/param/value');
				if (!$responsestruct = reset($tmp)) {
					$tmp = $responsexml->xpath('//fault/value');
					if (!$responsestruct = reset($tmp)) {
						throw new Exception(_t('Invalid XML response.'));
					}
				}
				return XMLRPCUtils::decode_args($responsestruct);
			}
			catch (Exception $e){
				//Utils::debug($response, $e);
				error_reporting($bit);
				return false;
			}
		}
	}

	/**
	 * Allow scoped functions to be called in shorthand
	 * Example:
	 *
	 * // Create the XMLRPC object
	 * $rpc= new XMLRPCClient('http://rpc.pingomatic.com');
	 * // Call weblogUpdates.ping RPC call
	 * $rpc->weblogUpdates->ping('Blog name', 'http://example.com');
	 *
	 *
	 * @param string $scope The scope to set this object to.
	 * @return XMLRPCClient This object instance
	 **/
	public function __get($scope)
	{
		$this->set_scope($scope);
		return $this;
	}

	/**
	 * Convenience method to create a new XMLRPCClient object
	 * Example:
	 *
	 * XMLRPCClient::open('http://rpc.pingomatic.com')->weblogUpdates->ping('Blog name', 'http://example.com');
	 *
	 * @param string $xmlrpc_entrypoint The entrypoint of the remote server
	 * @return XMLRPCClient The created client object
	 **/
	public static function open( $xmlrpc_entrypoint )
	{
		return new XMLRPCClient( $xmlrpc_entrypoint );
	}

}

?>


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

/**
 * XMLRPC Date type
 * Used to hold dates for transmission in XMLRPC calls.
 *
 */
class XMLRPCDate
{
	private $rpcdate;

	public function __set($name, $value)
	{
		switch($name) {
		case 'date':
			if(is_numeric($value)) {
				$this->rpcdate = $value;
			}
			else {
				$this->rpcdate = strtotime($value);
			}
		}
	}

	public function __get($name)
	{
		switch($name) {
		case 'date':
			return $this->rpcdate;
		}
	}

	public function __construct($date = null)
	{
		if(isset($date)) {
			$this->date = $date;
		}
	}
}

?>


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

/**
 * A custom exception for XMLRPC failure results
 */
class XMLRPCException extends Exception
{
	/**
	 * The exception constructor, called to create this excpetion
	 * @param integer $code The error code to produce
	 * @param string $message Optional The message to display with the error
	 **/
	public function __construct($code, $message = null)
	{
		// make sure everything is assigned properly
		if(empty($message)) {
			$message = $this->get_message($code);
		}
		parent::__construct($message, $code);
	}

	/**
	 * Return a test-based error description for a numeric error code
	 * @param integer $code The error code to search for
	 * @return string A localized text-based error message.
	 **/
	private function get_message($code)
	{
		switch ( $code ) {

		//Generic XMLRPC errors
		case -32700:
			return _t('parse error. not well formed');
		case -32701:
			return _t('parse error. unsupported encoding');
		case -32702:
			return _t('parse error. invalid character for encoding');
		case -32600:
			return _t('server error. invalid xml-rpc. not conforming to spec.');
		case -32601:
			return _t('server error. requested method not found');
		case -32602:
			return _t('server error. invalid method parameters');
		case -32603:
			return _t('server error. internal xml-rpc error');
		case -32500:
			return _t('application error');
		case -32400:
			return _t('system error');
		case -32300:
			return _t('transport error');

		// Pingback errors
		case 16:
			return _t('The source URI does not exist.');
		case 17:
			return _t('The source URI does not contain a link to the target URI, and so cannot be used as a source.');
		case 32:
			return _t('The specified target URI does not exist.');
		case 33:
			return _t('The specified target URI cannot be used as a target.');
		case 48:
			return _t('The pingback has already been registered.');
		case 49:
			return _t('Access denied.');
		case 50:
			return _t('The server could not communicate with an upstream server, or received an error from an upstream server, and therefore could not complete the request.');

		// Additional standard errors
		case 1:
			return _t('This XMLRPC server only accepts POST requests.');

		default:
			return Plugins::filter( 'xmlrpcexception_get_message', _t('Unknown XMLRPC Exception'), $code );
		}
	}

	/**
	 * Represent this exception as a string
	 **/
	public function __toString()
	{
		return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
	}

	/**
	 * Send an XML-RPC fault output and quit.
	 **/
	public function output_fault_xml()
	{
		$xmltext = '<?xml version="1.0"?'.'><methodResponse><fault><value><struct><member><name>faultCode</name><value><int>' . $this->code . '</int></value></member><member><name>faultString</name><value><string>' . $this->message . '</string></value></member></struct></value></fault></methodResponse>';
		ob_end_clean();
		header('Content-Type: text/xml;charset=utf-8');
		echo trim($xmltext);
		exit;
	}
}
?>


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

/**
 * A handler for incoming XMLRPC calls - an XMLRPC server
 *
 */
class XMLRPCServer extends ActionHandler
{

	/**
	 * Handle all incoming XMLRPC requests.
	 */
	public function act_xmlrpc_call()
	{
		if( $_SERVER['REQUEST_METHOD'] != 'POST' ) {
			$exception = new XMLRPCException(1);
			$exception->output_fault_xml(); // dies here
		}
		$input = file_get_contents( 'php://input' );

		$xml = new SimpleXMLElement( $input );

		$function = $xml->methodName;
		$params = array();
		$foundParams = $xml->xpath( '//params/param/value' );
		if ( is_array( $foundParams ) ) {
			foreach( $foundParams as $param ) {
				$params[] = XMLRPCUtils::decode_args( $param );
			}
		}

		$returnvalue = false;

		Plugins::register(array($this, 'system_listMethods'), 'xmlrpc', 'system.listMethods');
		$returnvalue = Plugins::xmlrpc("{$function}", $returnvalue, $params, $this);

		$response = new SimpleXMLElement('<?xml version="1.0"?'.'><methodResponse><params><param></param></params></methodResponse>');
		XMLRPCUtils::encode_arg($response->params->param, $returnvalue);

		ob_end_clean();
		header('Content-Type: text/xml;charset=utf-8');
		echo trim($response->asXML());
		exit;
	}

	/**
	 * A plugin sink to return a list of XML-RPC methods on a call to system.listMethods
	 * Allows plugins to add their own methods to the list.
	 * @param mixed $returnvalue The value that will be returned to the remote caller.
	 * @param mixed $params The parameters that were called with the remote call.
	 * @return array An array of supported XML-RPC methods.
	 **/
	public function system_listMethods($returnvalue, $params)
	{
		$res = array(
			'system.listMethods',
		);
		$res = Plugins::filter('xmlrpc_methods', $res);
		return $res;
	}

}

?>


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

/**
 * XMLRPC Struct type
 * Used to hold struct types (objects) that are returned in XMLRPC requests.
 *
 */
class XMLRPCStruct
{
	private $fields = array();

	/**
	 * Property setter for the XMLRPCStruct class.
	 * This allows the following:
	 * <code>
	 * $struct = new XMLRPCStruct();
	 * $struct->foo = 'bar'; // This is done by __set() and assigns 'bar' into $this->fields['foo']
	 * </code>
	 *
	 * @param string $name The name of the property on this object to set
	 * @param mixed $value The value to set in the property
	 * @return
	 */
	public function __set($name, $value)
	{
		$this->fields[$name] = $value;
	}

	/**
	 * Property getter for the XMLRPCStruct class.
	 * Returns the value of $this->fields for the specified porperty name.
	 *
	 * @param string $name The name of the property
	 * @return mixed The value stored for the requested property
	 */
	public function __get($name)
	{
		return $this->fields[$name];
	}

	/**
	 * Magic isset for XMLRPCStruct, returns whether a property value is set.
	 * @param string $name The name of the parameter
	 * @return boolean True if the value is set, false if not
	 */
	public function __isset($name)
	{
		return isset( $this->fields[$name] );
	}

	/**
	 * Get the list of properties that this object contains.
	 *
	 * @return array List of object properties, as stored in $this->fields.
	 */
	public function get_fields()
	{
		return array_keys($this->fields);
	}

	/**
	 * Constructor for XMLRPCStruct
	 *
	 * @param array $fields Default field values to set into properties.
	 */
	public function __construct($fields = array())
	{
		$this->fields = $fields;
	}
}

?>


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

/**
 * XMLRPC Utility class
 *
 */
class XMLRPCUtils
{

	/**
	 * Encode a variable value into the parameters of the XML tree
	 *
	 * @param SimpleXMLElement $params The parameter to add the value elements to.
	 * @param mixed $arg The value to encode
	 */
	public static function encode_arg($params, $arg)
	{
		switch(true) {
		case is_array($arg):
			$data = $params->addchild('value')->addchild('array')->addchild('data');
			foreach($arg as $element) {
				self::encode_arg($data, $element);
			}
			break;
		case ( $arg instanceof XMLRPCDate ):
			$params->addchild( 'value' )->addchild( 'dateTime.iso8601', date( 'c', $arg->date ) );
			break;
		case ($arg instanceof XMLRPCBinary):
			$params->addchild('value')->addchild('base64', base64_encode($arg->data));
			break;
		case ($arg instanceof XMLRPCStruct):
			$struct = $params->addchild('value')->addchild('struct');
			$object_vars = $arg->get_fields();
			foreach($object_vars as $field) {
				$member = $struct->addchild('member');
				$member->addchild('name', $field);
				self::encode_arg($member, $arg->$field);
			}
			break;
		case is_object($arg):
			$struct = $params->addchild('value')->addchild('struct');
			$object_vars = get_object_vars($arg);
			foreach($object_vars as $key=>$value) {
				$member = $struct->addchild('member');
				$member->addchild('name', $key);
				self::encode_arg($member, $value);
			}
			break;
		case is_integer($arg):
			$params->addchild('value')->addchild('i4', $arg);
			break;
		case is_bool($arg):
			$params->addchild('value')->addchild('boolean', $arg ? '1' : '0');
			break;
		case is_string($arg):
			$params->addchild('value')->addchild('string', $arg);
			break;
		case is_float($arg):
			$params->addchild('value')->addchild('double', $arg);
			break;
		}
	}

	/**
	 * Decode the value of a response parameter using the datatype specified in the XML element.
	 *
	 * @param SimpleXMLElement $value A "value" element from the XMLRPC response
	 * @return mixed The value of the element, decoded from the datatype specified in the xml element
	 */
	public static function decode_args($value)
	{
		if(count($value->children())) {
			$value = $value->xpath('*');
			$value = $value[0];
		}
		switch($value->getName()) {
		case 'data':
		case 'array':
			$result_array = array();
			foreach($value->xpath('//data/value') as $array_value) {
				$result_array[] = self::decode_args($array_value);
			}
			return $result_array;
			break;
		case 'struct':
			$result_struct = new XMLRPCStruct();
			foreach($value->xpath('//member') as $struct_value) {
				$property_name = (string)$struct_value->name;
				$children = $struct_value->value->children();
				if(count($children) > 0) {
					$result_struct->$property_name = self::decode_args($children[0]);
				}
				else {
					$result_struct->$property_name = (string)$struct_value->value;
				}
			}
			return $result_struct;
			break;
		case 'string':
			return (string)$value;
		case 'i4':
		case 'integer':
			return (int)$value;
		case 'double':
			return (double)$value;
		case 'boolean':
			return ((int)$value == 1) ? true : false;
		case 'dateTime.iso8601':
			return strtotime((string)$value);
		default:
			return (string)$value;
		}
	}

}

?>


code:
<?php
/**
 * Habari Index
 *
 * This is where all the magic happens:
 * 1. Validate the installation
 * 2. Set the locale
 * 3. Load the active plugins
 * 4. Parse and handle the incoming request
 * 5. Run the cron jobs
 * 6. Dispatch the request to the found handler
 *
 * @package Habari
 */

// Fail out if not included from root
if ( !defined( 'HABARI_PATH' ) ) {
	header('HTTP/1.1 403 Forbidden', true, 403);
	die ( );
}

// Compares PHP version against our requirement.
if ( !version_compare( PHP_VERSION, '5.2.0', '>=' ) ) {
	die ( 'Habari needs PHP 5.2.x or higher to run. You are currently running PHP ' . PHP_VERSION . '.' );
}

// Increase the error reporting level: E_ALL, E_NOTICE, and E_STRICT
error_reporting( E_ALL | E_NOTICE | E_STRICT );

// set a default timezone for PHP. Habari will base off of this later on
date_default_timezone_set( 'UTC' );

/**
 * Start the profile time
 */
$profile_start = microtime(true);

/**
 * Make GLOB_BRACE available on platforms that don't have it. Use Utils::glob().
 */
if ( !defined( 'GLOB_BRACE' ) ) {
	define( 'GLOB_NOBRACE', true );
	define( 'GLOB_BRACE', 128 );
}

// We start up output buffering in order to take advantage of output compression,
// as well as the ability to dynamically change HTTP headers after output has started.
ob_start();

require dirname(__FILE__) . '/autoload.php';
spl_autoload_register('habari_autoload');

// Replace all of the $_GET, $_POST and $_SERVER superglobals with object
// representations of each.  Unset $_REQUEST, which is evil.
// $_COOKIE must be set after sessions start
SuperGlobal::process_gps();

// Use our own error reporting class.
if ( !defined( 'SUPPRESS_ERROR_HANDLER' ) ) {
	Error::handle_errors();
}

/*
 * Initiate install verifications
 */

// Retrieve the configuration file's path.
$config = Site::get_dir( 'config_file' );

/**
 * We make sure the configuration file exist.
 * If it does, we load it and check it's validity.
 *
 * @todo Call the installer from the database classes.
 */
if ( file_exists( $config ) ) {
	require_once $config;

	// Set the default locale.
	HabariLocale::set( isset($locale) ? $locale : 'en-us' );

	if ( !defined( 'DEBUG' ) ) {
		define( 'DEBUG', false );
	}

	// Make sure we have a DSN string and database credentials.
	// db_connection is an array with necessary informations to connect to the database.
	if ( !Config::exists( 'db_connection' ) ) {
		$installer = new InstallHandler();
		$installer->begin_install();
	}

	// The connection details are registered. Try to connect to the database.
	try {
		DB::connect();
		// Make sure Habari is installed properly.
		// If the 'installed' option is missing, we assume the database tables are missing or corrupted.
		// @todo Find a decent solution, we have to compare tables and restore or upgrade them.
		if ( !@ Options::get( 'installed' ) ) {
			$installer = new InstallHandler();
			$installer->begin_install();
		}
	}
	catch( PDOException $e ) {
		// Error template.
		$error_template = "<title>%s</title>
%s

%s
";

		// Format page with localized messages.
		$error_page = sprintf($error_template,
			_t( "Habari General Error" ), # page title
			_t( "An error occurred" ), # H1 tag
			_t( "Unable to connect to database." ) # Error message.
		);

		// Set correct HTTP header and die.
		header( 'HTTP/1.1 500 Internal Server Error', true, 500 );
		die( $error_page );
	}
}
else {
	if ( !defined( 'DEBUG' ) ) {
		define( 'DEBUG', false );
	}

	// The configuration file does not exist.
	// Therefore we load the installer to create the configuration file and install a base database.
	$installer = new InstallHandler();
	$installer->begin_install();
}

/* Habari is installed and we established a connection with the database */

// Set the locale from database or default locale
if ( Options::get('locale') ) {
	HabariLocale::set( Options::get('locale') );
}
else {
	HabariLocale::set( 'en-us' );
}
if ( Options::get( 'system_locale' ) ) {
	HabariLocale::set_system_locale( Options::get( 'system_locale' ) );
}

// Verify if the database has to be upgraded.
if ( Version::requires_upgrade() ) {
	$installer = new InstallHandler();
	$installer->upgrade_db();
}

// If we're doing unit testing, stop here
if ( defined( 'UNIT_TEST' ) ) {
	return;
}

// if this is an asyncronous call, ignore abort.
if ( isset( $_GET['asyncronous'] ) && Utils::crypt( Options::get( 'guid' ), $_GET['asyncronous'] ) ) {
	ignore_user_abort( true );
}

// Send the Content-Type HTTP header.
// @todo Find a better place to put this.
header( 'Content-Type: text/html;charset=utf-8' );

// Load all the active plugins.
spl_autoload_register( array('Plugins', '_autoload') );
Plugins::load_active();

// All plugins loaded, tell the plugins.
Plugins::act('plugins_loaded');

// Start the session.
Session::init();

// Replace the $_COOKIE superglobal with an object representation
SuperGlobal::process_c();

// Initiating request handling, tell the plugins.
Plugins::act('init');

// Parse and handle the request.
Controller::parse_request();

// Run the cron jobs asyncronously.
CronTab::run_cron(true);

// Dispatch the request (action) to the matched handler.
Controller::dispatch_request();

// Flush (send) the output buffer.
$buffer = ob_get_clean();
$buffer = Plugins::filter('final_output', $buffer);
echo $buffer;
?>


code:
<?php include( 'header.php' ); ?>
<form action="" method="post">
<?php
foreach ($this->engine_vars as $key => $val) {
	if ( is_scalar( $val ) ) {
		echo '<input type="hidden" name="' . htmlentities($key, ENT_COMPAT, 'UTF-8') . '" value="' . htmlentities($val, ENT_COMPAT, 'UTF-8') . '">';
	}
}
?>
<input type="hidden" name="locale" value="<?php echo Utils::htmlspecialchars($locale); ?>">

Config.php(<?php _e('help'); ?>)

			<?php _e('Your config.php file is not writable. In order to successfully install Habari, please paste the following into'); ?> <?php echo $config_file; ?>:

			<textarea class="config"><?php echo $file_contents; ?></textarea>

				<?php _e('config.php is a file that tells Habari how to connect to your database. Habari is not able to write this file to your server automatically, so you must create this file yourself to continute the installation.'); ?>
				<?php _e('Learn More...'); ?>

<?php _e('Install'); ?>

<?php _e('When you have successfully placed the config file, run the installer again.'); ?>

			<input type="submit" name="submit" value="<?php _e('Restart Installer'); ?>" />

</form>

<?php include( 'footer.php' ); ?>


code:
<?php include( 'header.php' ); ?>
<?php if ( !isset( $form_errors ) ) $form_errors = array(); ?>
<?php function elem_if_set($a, $k) { if (isset($a[$k])) { return $a[$k]; } } ?>

<form action="" method="post" id="installform">
<input type="hidden" name="locale" value="<?php echo Utils::htmlspecialchars($locale); ?>" />

<?php _e('Database Setup'); ?>

	<?php _e('Help'); ?>

			<?php _e('Database Type'); ?> *
			<select id="db_type" name="db_type" tabindex="<?php echo $tab++; ?>">
			<?php
				foreach($pdo_drivers as $value => $text){
					echo '<option value="'.$value.'"';
					if($db_type == (string)$value) {
						echo ' selected="selected"';
					}
					echo '>' . $text . "</option>\n";
				}
				foreach($pdo_missing_drivers as $value => $text){
					echo '<optgroup label="' . _t('%1$s - missing PDO driver, see help', array($text)) . '"></optgroup>';
				}
			?>
			</select>

				<?php _e('Database Type specifies the type of database to which Habari will connect.  Changing this setting may affect the other fields that are available here.  If the database engine that you wanted to use is not in this list, you may need to install a PDO driver to enable it.'); ?>
				<?php _e('Learn More...'); ?>

<?php _e('MySQL Settings'); ?>

<?php _e('Use the settings below only if you have selected MySQL as your database engine.'); ?>

			<?php _e('Database Host'); ?> *
			<input type="text" id="mysqldatabasehost" name="mysql_db_host" value="<?php echo $db_host; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'mysql_db_host'); ?>

				<?php _e('Database Host is the host (domain) name or server IP address of the server that runs the MySQL database to which Habari will connect.  If MySQL is running on your web server, and most of the time it is, "localhost" is usually a good value for this field.'); ?>
				<?php _e('Learn More...'); ?>

			<?php _e('Username'); ?> *
			<input type="text" id="mysqldatabaseuser" name="mysql_db_user" value="<?php echo $db_user; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'mysql_db_user'); ?>

				<?php _e('Database User is the username used to connect Habari to the MySQL database.'); ?>
				<?php _e('Learn More...'); ?>

			<?php _e('Password'); ?> *
			<input type="password" id="mysqldatabasepass" name="mysql_db_pass" value="<?php /* echo $db_pass; */ ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'mysql_db_pass'); ?>

				<?php _e('Database Password is the password used to connect the specified user to the MySQL database.'); ?>
				<?php _e('Learn More...'); ?>

			<?php _e('Database Name'); ?> *
			<input type="text" id="mysqldatabasename" name="mysql_db_schema" value="<?php echo $db_schema; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'mysql_db_schema'); ?>

				<?php _e('Database Name is the name of the MySQL database to which Habari will connect.'); ?>
				<?php _e('Learn More...'); ?>

<?php _e('PostgreSQL Settings'); ?>

<?php _e('Use the settings below only if you have selected PostgreSQL as your database engine.'); ?>

			<?php _e('Database Host'); ?> *
			<input type="text" id="pgsqldatabasehost" name="pgsql_db_host" value="<?php echo $db_host; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'pgsql_db_host'); ?>

				<?php _e('Database Host is the host (domain) name or server IP address of the server that runs the PostgreSQL database to which Habari will connect.  If PostgreSQL is running on your web server, and most of the time it is, "localhost" is usually a good value for this field.'); ?>
				<?php _e('Learn More...'); ?>

			<?php _e('Username'); ?> *
			<input type="text" id="pgsqldatabaseuser" name="pgsql_db_user" value="<?php echo $db_user; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'pgsql_db_user'); ?>

				<?php _e('Database User is the username used to connect Habari to the PostgreSQL database.'); ?>
				<?php _e('Learn More...'); ?>

			<?php _e('Password'); ?> *
			<input type="password" id="pgsqldatabasepass" name="pgsql_db_pass" value="<?php /* echo $db_pass; */ ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'pgsql_db_pass'); ?>

				<?php _e('Database Password is the password used to connect the specified user to the PostgreSQL database.'); ?>
				<?php _e('Learn More...'); ?>

			<?php _e('Database Name'); ?> *
			<input type="text" id="pgsqldatabasename" name="pgsql_db_schema" value="<?php echo $db_schema; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'pgsql_db_schema'); ?>

				<?php _e('Database Name is the name of the PostgreSQL database to which Habari will connect.'); ?>
				<?php _e('Learn More...'); ?>

<?php _e('SQLite Settings'); ?> *

<?php _e('Use the settings below only if you have selected SQLite as your database engine.'); ?>

			<?php _e('Data file'); ?> *
			<input type="text" id="databasefile" name="db_file" value="<?php echo $db_file; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'db_file'); ?>

				<?php _e('Data file is the SQLite file that will store your Habari data.  This should be the path to where your data file resides, relative to the Habari user directory.'); ?> <?php _e('Learn More...'); ?>

			<?php _e('Table Prefix'); ?>
			<input type="text" id="tableprefix" name="table_prefix" value="<?php echo $table_prefix; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'table_prefix'); ?>

				<?php _e('Table Prefix is a prefix that will be appended to each table that Habari creates in the database, making it easy to distinguish those tables in the database from those of other installations.'); ?>
				<?php _e('Learn More...'); ?>

			<button type="button" name="check_db_connection" id="check_db_connection" tabindex="<?php echo $tab++ ?>"><?php _e('Check Database Connection'); ?></button>

<?php _e('Site Configuration'); ?>

	<?php _e('Help'); ?>

			<?php _e('Site Name'); ?> *
			<input type="text" id="sitename" name="blog_title" value="<?php echo $blog_title; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'blog_title'); ?>

				<?php _e('Site Name is the name of your site as it will appear
				to your visitors.'); ?>  <?php _e('Learn More...'); ?>

			<?php _e('Username'); ?> *
			<input type="text" id="adminuser" name="admin_username" value="<?php echo $admin_username; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'admin_username'); ?>

				<?php _e('Username is the username of the initial user in Habari.'); ?>  <?php _e('Learn More...'); ?>

			<?php _e('Password'); ?> *
			<input type="password" id="adminpass1" name="admin_pass1" value="<?php echo $admin_pass1; ?>" tabindex="<?php echo $tab++ ?>" />
			<?php _e('Password (again)'); ?> *
			<input type="password" id="adminpass2" name="admin_pass2" value="<?php echo $admin_pass2; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'admin_pass'); ?>

				<?php _e('Password is the password of the initial user in Habari. Both password fields must match.'); ?>  <?php _e('Learn More...'); ?>

			<?php _e('Admin Email'); ?> *
			<input type="text" id="adminemail" name="admin_email" value="<?php echo $admin_email; ?>" tabindex="<?php echo $tab++ ?>" />
			<img class="status" src="<?php Site::out_url( 'habari' ); ?>/system/installer/images/ready.png" alt="" />

<?php echo elem_if_set( $form_errors, 'admin_email'); ?>

				<?php _e('Admin Email is the email address of the first user account.'); ?>
				<?php _e('Learn More...'); ?>

<?php _e('Plugin Activation'); ?>

	<?php _e('Help'); ?>

		<?php foreach($plugins as $plugin) { ?>
			<?php if ( !isset($plugin['info']) ) { ?>

<?php printf( _t('The plugin file %s is a legacy plugin, and does not include an XML info file.'), $plugin['file'] ); ?>

			<?php } else { ?>

						<input type="checkbox" name="plugin_<?php echo $plugin['plugin_id']; ?>"
						id="plugin_<?php echo $plugin['plugin_id']; ?>"<?php if ($plugin['recommended']) echo ' checked="checked"'; ?> tabindex="<?php echo $tab++ ?>" /><label for="plugin_<?php echo $plugin['plugin_id']; ?>" class="name"><?php echo $plugin['info']->name; ?> <?php echo $plugin['info']->version; ?>

<?php echo $plugin['info']->description; ?>

			<?php } ?>
		<?php } ?>

			<input type="checkbox" name="checkbox_controller" id="checkbox_controller" tabindex="<?php echo $tab++ ?>" />
			None Selected

<?php _e('Install'); ?>

<?php _e('Habari now has all of the information needed to install itself on your server.'); ?>

			<input type="submit" name="submit" value="<?php _e('Install Habari'); ?>" id="submitinstall" tabindex="<?php echo $tab++ ?>" />

</form>

<?php include( 'footer.php' ); ?>


code:
	</div><!-- end wrapper -->
</body>
</html>


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<meta http-equiv="Content-Language" content="en"/>
	<meta name="robots" content="no index,no follow" />
	<link rel="shortcut icon" href="<?php Site::out_url('habari'); ?>/favicon.ico" />
	<link href="<?php Site::out_url('habari'); ?>/system/installer/style.css" rel="stylesheet" type="text/css" />

	<title><?php _e('Install Habari'); ?></title>

		<?php $tab = 1; ?>

Habari

<a href="<?php Site::out_url( 'habari' ); ?>/doc/manual/index.html" onclick="popUp(this.href); return false;" title="<?php _e('Read the user manual'); ?>" tabindex="<?php echo $tab++ ?>"><?php _e('Manual'); ?> ·
					<a href="http://wiki.habariproject.org/" title="<?php _e('Read the Habari wiki'); ?>" tabindex="<?php echo $tab++ ?>">Wiki ·
					<a href="http://groups.google.com/group/habari-users" title="<?php _e('Ask the community'); ?>" tabindex="<?php echo $tab++ ?>"><?php _e('Mailing List'); ?>

		<?php include "locale_dropdown.php"; ?>


code:
<?php include( 'header.php' ); ?>

<form action="" method="post">
<input type="hidden" name="locale" value="<?php echo Utils::htmlspecialchars($locale); ?>">

.htaccess(<?php _e('help'); ?>)

			<?php _e('Your .htaccess file is not writable. In order to successfully install Habari, please paste the following into'); ?> <?php echo HABARI_PATH . '/.htaccess'; ?>:

			<textarea class="config" tabindex="<?php echo $tab++ ?>"><?php echo $file_contents; ?></textarea>

				<?php _e('.htaccess is a file that tells your Apache web server to send requests to Habari for handling. Habari is not able to write this file to your server automatically, so you must create this file yourself to continue the installation.'); ?>
				<a onclick="this.target='_blank';" href="<?php Site::out_url( 'habari' ); ?>/manual/index.html#Installation"><?php _e('Learn More...'); ?>

<?php _e('Install'); ?>

<?php _e('When you have successfully placed the .htaccess file, run the installer again.'); ?>

			<input type="submit" name="submit" value="<?php _e('Restart Installer'); ?>" tabindex="<?php echo $tab++ ?>">

</form>

<?php include( 'footer.php' ); ?>


code:
<?php _e('Locale'); ?>

		<form method="post" action="" id="locale-form">

			<?php _e('Language'); ?>
			<?php
			$locs= array();
			foreach($locales as $loc):
				$locs[$loc]= $loc;
			endforeach;
			echo Utils::html_select( 'locale', $locs, $locale, array( 'tabindex' => $tab++ ) ); ?>

		</form>


code:
<?php include( 'header.php' ); ?>

<?php _e('Before you install Habari...'); ?>

<?php if (! $local_writable) {?>

<?php _e('Writable directory needed'); ?>...

	<?php if ($PHP_OS != 'WIN') {?>

			<?php _e('Before you can install Habari, you first need to make the install directory writable by php, so that the installation script can write your configuration information properly. The exact process depends on the web server and the ownership of the directory.'); ?>

			<?php _e('If your web server is part of the group which owns the directory, you\'ll need to add group write permissions to the directory. The procedure for this is as follows:'); ?>

				<?php _e('Open a terminal window, and then change to the installation directory:'); ?>

$> cd <?php echo $HABARI_PATH;?>

				<?php _e('Change the mode (permissions) of the current directory:'); ?>

$> chmod g+w .

$> chmod g+x .

					<?php _e('Note: You may need to use sudo and enter an administrator password if you do not own the directory.'); ?>

			<?php _e('If the web server is not part of the group which owns the directory, you will need to temporarily grant world write permissions to the directory:'); ?>

$> chmod o+w .

$> chmod o+x .

			<?php _e('Be sure to remove the write permissions on the directory as soon as the installation is completed.'); ?>

	<?php } else {?>
		@todo Windows instructions
	<?php }?>
<?php }?>

<?php if (! $php_version_ok) {?>

<?php _e('PHP Upgrade needed...'); ?>

		Habari <?php printf(_t('requires PHP 5.2 or newer. Your current PHP version is %s.'), PHP_VERSION); ?>

	@todo Upgrading PHP instructions
<?php }?>

<?php if (! empty($missing_extensions)) {
	foreach ($missing_extensions as $ext_name => $ext_url) {
		$missing_ext_html[]= '' . $ext_name . '';
	}
	$missing_ext_html = implode( ', ', $missing_ext_html );
?>

<?php _e('Missing Extensions'); ?>

		Habari <?php _e('requires the following PHP extensions to be installed:'); ?> <?php echo $missing_ext_html; ?>. <?php _e('Please contact your web hosting provider if you do not have access to your server.'); ?>

<?php }?>

<?php if ( extension_loaded( 'pcre' ) && ! @preg_match( '/\p{L}/u', 'a' ) ) { ?>

Unicode support needed...

		Habari <?php _e('requires PHP\'s PCRE extension to have Unicode support enabled. Please contact your web hosting provider if you do not have access to your server.'); ?>

<?php } ?>

<?php if ( ! $pdo_drivers_ok && ! array_key_exists( 'pdo', $missing_extensions )  ) { ?>

<?php _e('No PDO drivers enabled'); ?>

Habari <?php _e('requires that at least one PDO driver be installed. Please ask your hosting provider to enable one of the PDO drivers supported by Habari.'); ?>

<?php } ?>

<?php if ( ! $mod_rewrite ) { ?>

<?php _e('mod_rewrite not enabled'); ?>

 <?php _e('You have Apache, but mod_rewrite is not enabled. To use Habari, you must enable mod_rewrite in your server configuration. mod_rewrite is an Apache module that allows Habari (and other software) to handle URLs dynamically. You need a LoadModule rewrite_module line in your httpd.conf. Please ask your systems administrator or hosting provider to enable mod_rewrite.'); ?>

<?php } ?>

<?php include( 'footer.php' ); ?>