Location: PHPKode > projects > SkyBlueCanvas Lightweight CMS > includes/core.php
<?php defined('SKYBLUE') or die(basename(__FILE__));

/**
* @version		1.1 RC1 2008-11-20 21:18:00 $
* @package		SkyBlueCanvas
* @copyright	Copyright (C) 2005 - 2008 Scott Edwin Lewis. All rights reserved.
* @license		GNU/GPL, see COPYING.txt
* SkyBlueCanvas is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYING.txt for copyright notices and details.
*/

/**
* @author Scott Lewis <hide@address.com>
* @version $Id: core.php, v 0.1 2006/08/14 05:17:00 scottlewis Exp $
* @copyright (C) 2006 Bright-Crayon, LLC
* @license Commercial
* @package SkyBlue
*
* The Core class contains 'core' functionality that is used throughout
* the SkyBlue system. This class includes sub-classes to perform various
* tasks such as XML parsing and generation, mail functions and HTML generation.
*
* The Core class also includes functionality to work with the file system, 
* language support, string manipulation, array manipulation ...
*/

class Core extends Observer {

    /**
     * Used to pass messages about the result of a user action from one
     * request (page) to another.
     *
     * @access public
     * @var    string
     */
    
    var $MSG = '...';
    
    /** @var RESET A flag to indicate whether or not to reset the
    *   MSG variable. Deprecated.
    */
    
    /**
     * Helper class to handle XML-related tasks. Stores the xmlHandler
     * object as defined by {@link xml.parser.php}.
     *
     * @access public
     * @var    object
     */
    
    var $xmlHandler = null;

    /**
     * Not implemented. DataSource will eventually be the bridge between 
     * the core and the data storage abstraction classes.
     *
     * @access public
     * @var    object
     */
    
    var $datasource = null;

    /**
     * Sub-class to handle Mail-related tasks. 
     *
     * @access public
     * @var    object
     */
    
    var $postmaster = null;
    
    /**
     * Helper class for sorting arrays of objects.
     * {@link plugin.quicksort.php}
     *
     * @access public
     * @var    object
     */
    
    var $quicksort = null;

    /**
     * A unique one-time page-level identifier to make sure that
     * any form submitted to the site, was created by the site.
     *
     * @access public
     * @var    string
     */

    var $token = null;

    /**
     * Array to hold the installed language package.
     *
     * @access public
     * @var    array
     */
    
    var $terms = null;

    /**
     * The relative path of the Core depending on whether
     * the Core is being loaded by the back end or front end.
     *
     * @access public
     * @var    string
     */
        
    var $path = null;
    
    /**
     * SkyBlue can display pop-up dialogs using HTML
     * and the $_SESSION array. This is dependent on the Skin class.
     *
     * @access public
     * @var    string
     */
    
    var $dialog = null;
    
    /**
    * Allowed upload MIME types
    */
    
    var $allowed_types = array();
    
    /**
    * The events on which callbacks can be fired
    */

    var $events = array();
    
    /**
    * The session lifetime
    */
    
    var $lifetime = 0;
    
    function __construct($options=array()) {
        $this->path = Filter::get($options, 'path', '');
        
        if (!defined('SB_BASE_PATH'))
        {
            define('SB_BASE_PATH', $this->path);
        }
        
        $this->declareEvents($options);
        $this->LoadConstants();
        $this->IsValidSite();
        $this->LoadHelperClasses();
        $this->loadConfigFile();
        $this->GetActiveSkin();
        $this->InitSession();
    }
    
    function Core($options=array()) {
        $this->__construct($options);
    }
    
    function loadConfigFile() {
        $config = $this->xmlHandler->ParserMain(
            SB_XML_DIR . "configuration.xml"
        );
        $this->config = Filter::get($config, 0);
    }
    
    function declareEvents($config) {
        if (isset($config['events'])) {
            $events = $config['events'];
            for ($i=0; $i<count($events); $i++) {
                $this->addEvent($events[$i]);
            }
        }
    }
    
    function RegisterEvent($event, $callback) {
        $this->register($event, $callback);
    }

    function IsValidSite() {
        if (!file_exists(SB_SITE_DATA_DIR)) {
            die("No data directory was found for this installation");
        }
    }
    
    function editor($selector, &$html, $editor) {
        $code = null;
        
        $selectors = array($selector);
        if (strpos($selector, ',') !== false) {
            $selectors = explode(',', $selector);
        }
		for ($i=0; $i<count($selectors); $i++) {
			if ($this->hasSelector(trim($selectors[$i]), $html)) {
				$code = FileSystem::buffer(SB_EDITORS_DIR . "$editor/header.php");
			}
		}
        $html = str_replace(TOKEN_EDITOR, $code, $html);
    }
    
    function hasSelector($selector, &$html) {
        $attr = 'class';
        if ($selector{0} == '#') {
            $attr = 'id';
        }
        $selector = substr($selector, 1, strlen($selector)-1);        
		preg_match_all("/$attr=\"([^\"]+)\"/i", $html, $matches);
		if (count($matches) == 2) {
			$elements = array();
			$matches = $matches[1];
			for ($i=0; $i<count($matches); $i++) {
				$bits = explode(" ", $matches[$i]);
				for ($j=0; $j<count($bits); $j++) {
					if (trim($bits[$j]) != "") {
						$elements[] = $bits[$j];
					}
				}
			}
			if (in_array($selector, $elements)) {
				return true;
			}
		}
		return false;
    }
    
    function GetActiveSkin() {
        if (file_exists(SB_XML_DIR.'activeskin.xml')) {
            $skins = $this->xmlHandler->ParserMain(SB_XML_DIR.'activeskin.xml');
            if (count($skins)) {
                $skin  = $skins[0];
                if (isset($skin->activeskin) && !empty($skin->activeskin))
                {
                    $activeskin = $skin->activeskin;
                }
            }
        }

        defined('ACTIVE_SKIN_DIR') or
        define('ACTIVE_SKIN_DIR', SB_SKINS_DIR.$activeskin.'/');
        
        defined('ACTIVE_SKIN_CSS_DIR') or
        define('ACTIVE_SKIN_CSS_DIR', ACTIVE_SKIN_DIR.'css/');
        
        defined('ACTIVE_SKIN_IMG_DIR') or
        define('ACTIVE_SKIN_IMG_DIR', ACTIVE_SKIN_DIR.'images/');
        
        defined('MEDIA_CSS_DIR') or
        define('MEDIA_CSS_DIR', ACTIVE_SKIN_DIR.'media.styles/');
    }
    
    function LoadHelperClasses() {
        $this->DoInclude(SB_XML_PARSER_FILE,   __LINE__, __FUNCTION__);
        $this->DoInclude(SB_DATASOURCE_FILE,   __LINE__, __FUNCTION__);
        $this->DoInclude(SB_POSTMASTER_FILE,   __LINE__, __FUNCTION__);
        $this->DoInclude(SB_HTML_FACTORY_FILE, __LINE__, __FUNCTION__);
    
        $this->xmlHandler = new xmlHandler;
        $this->datasource = new datasource;
        $this->postmaster = new postmaster;
        $this->HTML = new html;
    }
    
    function FileNotFound($missingFile, $line, $func) {
        die('<b>Fatal Error</b><br />'.
             $missingFile.' was not found.<br />'.
             '<b>Line:</b> '.$line.'<br />'.
             '<b>Func:</b> '.$func
       );
    }
    
    function DoInclude($file, $lineNumber, $funcName) {
        if (file_exists($file)) {
            require_once($file);
        } 
        else {
            $this->FileNotFound($file, $lineNumber, $funcName);
        }
    }

    function LoadConstants() {
        define('SB_CONF_DIR',     SB_BASE_PATH.'configs/');
        define('SB_CONF_SERVER',  SB_CONF_DIR.'server.consts.php');
        define('SB_CONF_DIRS',    SB_CONF_DIR.'dirs.consts.php');
        define('SB_CONF_FILES',   SB_CONF_DIR.'files.consts.php');
        define('SB_CONF_STRINGS', SB_CONF_DIR.'strings.consts.php');
        define('SB_CONF_TOKENS',  SB_CONF_DIR.'tokens.consts.php');
        define('SB_CONF_REGEX',   SB_CONF_DIR.'regex.consts.php');

        $this->DoInclude(SB_CONF_SERVER,  __LINE__, __FUNCTION__);
        $this->DoInclude(SB_CONF_DIRS,    __LINE__, __FUNCTION__);
        $this->DoInclude(SB_CONF_FILES,   __LINE__, __FUNCTION__);
        $this->DoInclude(SB_CONF_STRINGS, __LINE__, __FUNCTION__);
        $this->DoInclude(SB_CONF_TOKENS,  __LINE__, __FUNCTION__);
        $this->DoInclude(SB_CONF_REGEX,   __LINE__, __FUNCTION__);

        $this->DefineUserURL();
    }
    
    function DefineUserURL() {
        define('SB_MY_URL',   '');
        define('SB_RSS_FEED', SB_RSS_DIR);
        define('SB_ADMIN_URL', '');
    }
    
    /**
    * LoadConfig loads the configuration files. Non-GUI-editable configs
    * are stored in ~/configs/. GUI-editable configs are stored in
    * ~/xml.system/configuration.xml.
    * @access private
    * @return array
    */
    
    function LoadConfig() {

        $config = array();
        
        $configObj = $this->config;
        
        if (!empty($configObj)) {
			foreach($configObj as $k=>$v) {
				$config[$k] = trim($v);
			}
		}
        
        $config = $this->DefineBaseURI($config);
        
        if (!defined('USE_SEF_URLS')) {
            define('USE_SEF_URLS', file_exists($this->path . '.htaccess') ? 1 : 0 );
        }
        
        if (isset($config['site_url']) && !empty($config['site_url'])) {
            $url = $config['site_url'];
            if (strpos($url, 'http://') === false) {
                $url = "http://$url";
            }
            if ($url{strlen($url)-1} != '/') {
                $url .= '/';
            }
            $config['site_url'] = $url;
            define('FULL_URL', $config['site_url']);
        } 
        else {
            define('FULL_URL', null);
        }
        
        if (isset($config['site_name']) && !empty($config['site_name'])) {
            define('SB_SITE_NAME', $config['site_name']);
        } 
        else {
            define('SB_SITE_NAME', null);
        }
        
        if (isset($config['site_slogan']) && !empty($config['site_slogan'])) {
            define('SB_SITE_SLOGAN', $config['site_slogan']);
        } 
        else {
            define('SB_SITE_SLOGAN', null);
        }
        
        $this->get_allowed_mime_types();
        
        return $config;
    }
    
    function get_allowed_mime_types() {
        $this->allowedTypes = FileSystem::read_config("configs/mimetypes.php");
    }
    
    function DefineBaseURI($config) {
        $config['base_uri'] = SB_MY_URL;
        define('BASE_URI', SB_MY_URL);
        return $config;
    }
    
    /**
    * Determines if the instance of SkyBlue is a new or un-configured
    * installation. If true, the "Start" screen is loaded. If no user-installed
    * skin is found this function will create a skin directory tree and install
    * the first basic structural HTML file for the skin. The directory tree
    * is installed automatically so that the system does not encounter any
    * errors arising from trying to access something that does not exist.
    * @access public
    * @return void
    */

    function CheckInstall() {
        $files   = array(
            SB_LOGIN_FILE,
            SB_CONFIG_XML_FILE,
            SB_PAGE_FILE,
            SB_MENU_GRP_FILE
        );
        for ($i=0; $i<count($files); $i++) {
            if (!file_exists($files[$i])) {
                $this->SBRedirect(SB_SETUP_PAGE);
            }
        }
    }

    function GetUnreadMailCount() {
	    $count = 0;
        if (is_dir(SB_SITE_EMAIL_DIR)) {
            $files = $this->ListFilesOptionalRecurse(SB_SITE_EMAIL_DIR, 0, array());
            for ($i=0; $i<count($files); $i++) {
                if ($files[$i] != SB_EMAIL_ERROR_LOG) {
	                $name = basename($files[$i]);
                    if ($name{0} == '~') $count++;
                }
            }
        }
        return $count;
    }
    
    /**
    * Ensures that the newly added item has a unique ID property.
    * @access public
    * @param refernce $objs a reference to the array of objects.
    * @return integer
    */

    function GetNewID(&$objs) {
        $ids = array();
        for ($i=0; $i<count($objs); $i++) {
            if (is_object($objs[$i])) {
                array_push($ids, $objs[$i]->id);
            }
            else {
                array_push($ids, $i);
            }
        }
        sort($ids);
        $id = count($ids) > 0 ? intval($ids[count($ids)-1]) : 1 ;
        $id++;
        return $id;
    }
    
    /**
    * Loads the currently installed localization package.
    * @access private
    * @return void
    */
    
    function LoadLanguage() {
        $this->terms = array();
        if (file_exists(SB_LANG_FILE)) {
            include(SB_LANG_FILE);
            $this->terms = $TERMS;
        }
    }
    
    /**
    * Loads the plugin specified by $plugin.
    * All plugins must reside in their own directory in ~/plugins/. The plugin
    * directory name must match the plugin name exactly. The plugin
    * class file name must be named <name>.class.php.
    * @access public
    * @param string $plugin the name of the plugin to load.
    * @return void
    */
    
    function LoadPlugin($plugin) {
		global $config;
		
		if (!empty($plugin)) {
			$path = SB_PLUGIN_DIR.$plugin.'/'.$plugin.'.class.php';
			set_include_path(get_include_path() . PATH_SEPARATOR . $path);
			if (file_exists($path)) {
				require($path);
				return new $plugin;
			} 
			else {
				trigger_error(
					'SkyBlueCanvas Says: File '.$plugin.' does not exist. '
					.  __FILE__ . ': on line ' . __LINE__
				);
			}
		} 
		else {
			trigger_error(
				'SkyBlueCanvas Says: No Plugin specified. '
				. __FILE__ . ': on line ' . __LINE__
			);
		}
    }

    function LoadUserPlugins() {
	    if (is_dir(SB_USER_PLUGINS_DIR)) {
	        $files = $this->ListFilesOptionalRecurse(SB_USER_PLUGINS_DIR, 0, array());
	        for ($i=0; $i<count($files); $i++) {
	            $name = basename($files[$i]);
	            if ($name{0} == '_') continue;
	            include_once($files[$i]);
	        }
	    }
	}
	    
    function UpdateSitemap() {
        $this->LoadPlugin(SB_SITEMAPPER_CLASS);
    }
    
    /**
    * Verifies that the object being acted on has a unique ID.
    * This function should be called before any Save or Delete action, 
    * otherwise, unexpected results may occur.
    * @access public
    * @param integer $id the local ID variable.
    * @return void
    */
    
    function RequireID($id, $redirect) {
        if (empty($id)) {
            $this->ExitEvent(3, $redirect);
        }
    }
    
    /**
    * A globally available and generic Cancel function.
    * @access public
    * @param string $redirect The URL to which to redirect the user agent.
    * @return void
    */
    
    function Cancel($redirect) {
        $this->ExitEvent(2, $redirect);
    }
    
    /**
    * @deprecated Use Uplaoder class
    */
    
    function UploadFile($file, $dest, $allowtypes, $maxsize=5000000, $targets=array()) {
        $Uploader = new Uploader($allowtypes, $targets);
        return $Uploader->upload($file, $dest);
    }
    
    /**
    * Peforms upload and move tasks when uploading multiple files
    * via an HTML form.
    *
    * @param array - the file input value from the HTML form.
    * @param array - the destination for the new file.
    * @param array - the file types to allow to be uploaded
    * @param int   - the maximum filesize to allow to be uploaded.
    */
    
    function UploadMultipleFiles(
        $files=array(), 
        $dest=array(), 
        $allowTypes=array(), 
        $maxsize=5000000,
        $targets=array()
    ) {
        $exitCodes = array();
        
        $count = count($files['upload']['name']);
        
        for ($i=0; $i<$count; $i++) {
            $fname = $files['upload']['name'][$i];
            $ftype = $files['upload']['type'][$i];
            
            if (!in_array($ftype, $allowTypes)) {
                list($exitCodes[], $newFiles[]) = array('-1', '');
            } 
            else {
                $this->CheckTrailingSlash($dest[$i]);
                
                $file['name']     = $files['upload']['name'][$i];
                $file['type']     = $files['upload']['type'][$i];
                $file['tmp_name'] = $files['upload']['tmp_name'][$i];
                $file['error']    = $files['upload']['error'][$i];
                $file['size']     = $files['upload']['size'][$i];
                
                list($exitCodes[], $newFiles[]) = 
                  $this->UploadFile($file, $dest[$i], $allowTypes, $maxsize, $targets);
            }
        }
        return array($exitCodes, $newFiles);
    }
    
    /**
    * Adds a trailing slash to a path string when needed.
    *
    * @param string - the path to which to add the trailing slash.
    */
    
    function CheckTrailingSlash(&$path) {
        $path .= $path{strlen($path)-1} != '/' ? '/' : '' ;
    }
    
    /**
    * Nearly every class (manager) in SkyBlue uses the SetSessionMessage() function
    * to display the result of any action. This function was created to streamline
    * the code and to have a universal method for exiting an action.
    * 
    * New exitcodes (cases) can be added as long as they are 
    * added to the end of the list.
    * 
    * ExitEvent() should be called when some user action/event is completed
    * and the page needs to be redirected to some location such as the main
    * screen of a manager.
    * 
    * Examples:
    * 
    * - After saving an item
    * - After deleting an item
    * - When some required information is missing and the
    *   action/event should not be allowed to proceed.
    * - When the user Cancels an action/event
    * 
    * Cases (exit codes) 0-3 are fixed and should not be changed.
    *
    * @param int    - the exit code for the last action.
    * @param string - the URL to which to redirect the browser.
    */
    
    function ExitEvent($code, $redirect) {
        $msg = null;
        $class = null;
        if (intval($code) > 0)  {
			$code  = 'EXITCODE_'.$code;
			$msg   = $this->terms[$code]['str'];
			$class = $this->terms[$code]['class'];
        }
        $this->SetSessionMessage($msg, $class);
        $this->SBRedirect($redirect);
    }
    
    function ExitDemoEvent($redirect) {
        $this->SetSessionMessage(MSG_FEATURE_DISABLED_IN_DEMO, 'warning');
        $this->SBRedirect($redirect);
    }
    
    function ExitRestrictedEvent($redirect, $msg) {
        $this->SetSessionMessage($msg, 'warning');
        $this->SBRedirect($redirect);
    }
    
    function ExitWithWarning($msg, $redirect) {
        $this->SetSessionMessage($msg, 'warning');
        $this->SBRedirect($redirect);
    }
    
    function ExitWithErrorMessage($msg, $redirect) {
        $this->SetSessionMessage($msg, 'error');
        $this->SBRedirect($redirect);
    }
    
    function ExitWithError($msg, $redirect) {
        $_SESSION['LASTERROR'] = "<div class=\"error\"><p>$msg</p></div>\n";
        $this->SBRedirect($redirect);
    }
    
    function GetLastError() {
        if (trim($this->MSG) == '...') {
            $this->MSG = null;
        }
        if (isset($_SESSION['LASTERROR'])) {
            $this->MSG .= $_SESSION['LASTERROR'];
            unset($_SESSION['LASTERROR']);
        }
    }

	function DefineDefaultPage() {
	    $defaultPage = null;
	    $pages = $this->xmlHandler->ParserMain(SB_PAGE_FILE);
	    foreach ($pages as $p) {
	        if (intval($p->isdefault) == 1) {
	            $defaultPage = $p->id;
	        }
	    }
	    if (empty($defaultPage) && count($pages)) {
	        $defaultPage = $pages[0]->id;
	    }
	    define('DEFAULT_PAGE', $defaultPage);
	}
	
    /**
    * Builds an HTML select element for the pages created in the 
    * SkyBlue Admin area.
    *
    * @param string - the currently selected option in the select list.
    */
    
    function PageSelector($selected='') {
        global $config;
        
        $pages = array();
        if (file_exists(SB_PAGE_FILE)) {
            $pages = $this->xmlHandler->ParserMain(SB_PAGE_FILE);
        }
        $options = array();
        $options[] = $this->MakeOption(' -- Select Page -- ', '');
        foreach ($pages as $p) {
            $s = $p->id == $selected ? 1 : 0 ;
            array_push($options, $this->MakeOption($p->title, $p->id, $s));
        }
        return $this->SelectList($options, 'page');
    }
    
    /**
    * Builds an HTML select element for the files in the specified directory.
    *
    * @param string - the path to the directory for which to create a file selector.
    * @param string - the HTML input name for the selector.
    * @param string - the currently selected option in the select list
    * @param bool   - whether or not to trim any file extensions from the 
    * files in the specified directory.
    */
    
    function BuildFileSelector($dir, $selector_name, $selected='', $trimext=0, $fullpath=1) {
        $files = $this->ListFilesOptionalRecurse($dir);
        $options = array();
        $options[] = $this->MakeOption(' -- Select A File -- ', '', 0);
        for ($i=0; $i<count($files); $i++) {
            $name = basename($files[$i]);
            $name = $trimext == 1 ? $this->TrimExtension($name) : $name ;
            if (!$fullpath) {
                $files[$i] = basename($files[$i]);
            }
            $s = $files[$i] == $selected ? 1 : 0 ;
            array_push($options, $this->MakeOption($name, $files[$i], $s));
        }
        return $this->SelectList($options, $selector_name);
    }    
    
    /**
    * Validates a piece of data for being a specified type. This function
    * is useful for validating FORM fields.
    *
    * @param string - the data string to validate.
    * @param string - the type of validation to perform.
    */
    
    function ValidateField($value, $validation) {
        switch ($validation) {
            case 'notempty':
            case 'notnull':
                return trim($value) == "" ? false : true ;
                break;
            case 'number':
                return ereg (SB_REGEX_NUM, $value);
                break;
            case 'email':
                return eregi(SB_REGEX_EMAIL, $value);
                break;
            case 'url':
                return preg_match(SB_REGEX_URL, $value);
                break;
            default:
                return true;
                break;
        }
    }
    
    /**
    * Used in pagination routines and other routines that need to capture
    * a subset of a set of data.
    *
    * @param int - the offset within the data set.
    * @param int - number of items in the data set.
    * @param int - the minimum number of items to include in the subset.
    */
    
    function CalcStartOfRange($offset, $item_count, $min=0) {
        return $this->GetNumInRange(
            ($offset * $item_count) - $item_count, 
            $item_count, 
            $min
        );
    }
    
    /**
    * Verifies that the specified number is within the range of
    * the minimum and maximum range specified.
    *
    * @param int - the number to check.
    * @param int - the maximum number in the set.
    * @param int - the minimum number in the set.
    */
    
    function GetNumInRange($num, $max, $min=1) {
        if ($num >= $min) {
            $newNum = $num <= $max ? $num : $max ;        
        } 
        else {
            $newNum = $min;
        }
        return $newNum;
    }
    
    /**
    * Performs an array_slice
    *
    * @param array - the original data set.
    * @param int   - the starting index for the slice.
    * @param int   - the number of items in the data set.
    */
    
    function GetArraySubset(&$arr, $offset, $item_count) {
        return array_slice($arr, $offset, $item_count);
    }
    
    /**
    * Calculates the number of subsets needed to hold the data
    * set as determined by the maximum number of items allowed
    * in a subset.
    *
    * @param array - the original data set.
    * @param int   - the number of items in the data set.
    */
    
    function CalcNumOfPages(&$items, $items_per_page) {
        return ceil(count($items) / $items_per_page);
    }
    
    /**
    * A useful function for retrieving a variable from any array or a default 
    * value if the array index is not set. The htmlentities function is used 
    * to prevent the injection of malicious HTML, JavaScript or SQL code. 
    *
    * @param array  - the array from which to retrieve the desired value.
    * @param string - the array index of the value to return.
    * @param string - the default value to return if the index is not found.
    * @param bool   - whether or not to HTML encode the returned value.
    * @param bool   - whether or not to enforce alpha-numeric values.
    */
    
    /*
    * @deprecated  User Filter class
    */
    
    function GetVar($arr, $index, $default='', $htmlfilter=1, $alphafilter=0) {
        return Filter::get($arr, $index, $default, $htmlfilter);
    }
    
    /**
    * Performs the actual HTML encoding in the GetVar() function. 
    *
    * @param mixed - datum to HTML encode. Can be a string or an array.
    * If an array, all values in the array will be encoded.
    */
    
    /*
    * @deprecated  User Filter class
    */

    function SafeValue($var) {
        if (is_array($var)) {
            $safe = array();
            foreach ($var as $k=>$v) {
                $safe[$k] = trim(strip_tags($v));
            }
        } 
        else {
            $safe = trim(strip_tags($var));
        }
        return $safe;
    }
    
    /**
    * Verifies that a value is comprised only of alpha-numeric characters. 
    *
    * @param string - the text shred to check for alpha-numeracy.
    */
    
    function AlphaNumericFilter($value) {
        return ctype_alnum($value);
    }
    
    function stripslashes_deep($value) {
       $value = is_array($value) 
           ? array_map('stripslashes_deep', $value) 
           : stripslashes($value);
       return $value;
    }

    /**
    *  SBRedirect redirects the browser to a page
    *  specified by the $url argument.
    *
    * @param string - the URL to which to redirect the browser.
    */
    
    function SBRedirect($url) {
        if (headers_sent()) {
            echo "<script>document.location.href='".$url."';</script>\n";
        } 
        else {
            header("Location: $url");
        }
        exit(0);
    }

    /**
    * GetLink returns the HREF URL in the proper format depending on whether or not
    * USE_SEF_URLS is set to true or not.
    *
    * @param string $title  The text for the SEF_URL
    * @param int    $PageID The id of the page to display the object.
    * @param int    $ObjID  The id of the individual object to be displayed.
    */
    
    function SafeURLFormat($str) {
        for ($i=0; $i<strlen($str); $i++) {
            if (strpos(SB_SAFE_URL_CHARS, $str{$i}) === false) {
                $str{$i} = '-';
            }
        }
        return $str;
    }
    
    /*
    * @Deprecated  Use Router::GetLink()
    */
    
	function GetLink($title, $PageID, $ObjID, $useFullURL=0) {
        if (defined('USE_SEF_URLS') && USE_SEF_URLS == 1) {
            $search = array('[amp]', '&amp;', '&');
            $replace = array('-and-');
            $title = str_replace($search, $replace, $title);
            $title = str_replace(' - ', '-', $title);
            $title = $this->SafeURLFormat($title);
            $link  = $title.'-pg-'.$PageID.(!empty($ObjID)?'-'.$ObjID:null).'.htm';
        } 
        else {
            $link = 'index.php?pid=' . $PageID . (!empty($ObjID) ? '&amp;show='.$ObjID : null);
        }
        return ($useFullURL ? FULL_URL . $link : $link );
    }
    
    /**
    * initialize a session
    * @param int - the session lifetime in seconds.
    */
    
    function InitSession() {
        
        $lifetime = Filter::get($this->config, 'session_timeout');
       
        if (trim($lifetime) == "") {
            $lifetime = 3600;
        }
        else {
            $lifetime *= 60 ;
        }
        
        @ini_set('session.gc_maxlifetime', $lifetime); 
    
        session_id();
        session_set_cookie_params($lifetime);
        session_start();
        
        if (!isset($_SESSION['MSG'])) {
            $_SESSION['MSG']   = '...';
        } 
        else {
            $this->MSG = $_SESSION['MSG'];
        }
        if ($this->MSG != '...') {
            $_SESSION['MSG']   = '...';
        }
    }
    
    /**
    * This function is used to set a message for the result of a user action
    * in a SkyBlue Admin Component.
    *
    * @param string - the message to store in the $_SESSION for use
    * on the subsequent page.
    * @param string - the type of message. The message type should correspond
    * to a CSS selector class name.
    */
    
    function SetSessionMessage($msg, $type='confirm') {
		$class = 'generic';
		$heading = null;
        switch ($type) {
            case 'error':
				$class   = 'error';
				$heading = 'Error!';
                break;
            case 'confirm':
				$class   = 'success';
				$heading = 'Success!';
                break;
            case 'warning':
				$class   = 'warning';
				$heading = 'Warning!';
                break;
            case 'info':
				$class   = 'info';
				$heading = 'Note:';
                break;
            default:
                break;
        }
		$_SESSION['MSG'] = 
		"<div class=\"msg-$class\">\n" . 
		"    <h2>$heading</h2>\n" . 
		"    <p>$msg</p>\n" . 
		"</div>\n";
    }
    
    /**
    * validates the admin request
    * 
    * @param string - the name of the requested action.
    */
    
    function ValidateRequest($task, $return=false) {
        $username = null;
        $objs = $this->xmlHandler->ParserMain(SB_LOGIN_FILE);
        $obj = count($objs) ? $objs[0] : null ;
        if (!empty($obj)) {
            $username = isset($obj->username) ? $obj->username : null ;
        }
        
        if ($task != 'login') {
            if (isset($_SESSION)) {
                if (empty($username)) {
                    if (!$return) {
                        $this->ForceLogin();
                    }
                    else {
                        return false;
                    }
                } else if (!isset($_SESSION['USERNAME']) || $username != $_SESSION['USERNAME']) {
                    if (!$return) {
                        $this->ForceLogin();
                    }
                    else {
                        return false;
                    }
                }
                return true;
            }
            return false;
        }
    }
    
    /**
    * This function clears the $_SESSION array and redirects the
    * user to the login page. This function is only used by the Admin
    * section of SkyBlue.
    */
    
    function ForceLogin() {
        $_SESSION = array();
        $this->SBRedirect(BASE_PAGE);
    }
    
    /**
    * Loads the the Admin Component.
    * 
    * @param string - the name of the component to load.
    */
    
    function LoadContent($mgr) {
        global $config;

        if ($this->ValidatePath(SB_MANAGERS_DIR . "{$mgr}/{$mgr}.class.php")) {
            include(SB_MANAGERS_DIR . "{$mgr}/{$mgr}.class.php");
            $component = new $mgr;
        }
    }
    
    /**
    * Loads an admin module.
    */
    
    function LoadModuleAdmin2($mod) {
        if ($this->ValidatePath(SB_INC_DIR.'mod.'.$mod.'.php')) {
            include(SB_INC_DIR.'mod.'.$mod.'.php');
        }
    }
    
    
    /* ============================================= */
       // FILE, DIRECTORY & PATH HANDLING FUNCTIONS
    /* ============================================= */
    
    /**
    * Allows component and module code to capture the output of executable 
    * or HTML code in a temporary buffer so the text can be pre-processed
    * or use asynchronously.
    *
    * @param string - the path to the file to buffer.
    */
    
    /*
    * @deprecated Use FileSystem class
    */
    
    function OutputBuffer($input) {
        return FileSystem::buffer($input);
    }
    
    /**
    * A single parameter function for reading the contents of a file.
    *
    * @param string - the path to the file to read.
    */
    
    /*
    * @deprecated Use FileSystem class
    */
    
    function SBReadFile($file) {
        return FileSystem::read_file($file);
    }
    
    /**
    * Writes data to a file.
    *
    * @param string - the path to the file to write.
    * @param string - the data to write to the file.
    * @param bool   - whether or not to append the data to the file.
    */
    
    /*
    * @deprecated Use FileSystem class
    */
    
    function WriteFile($file, $str, $append=0) {
        return FileSystem::write_file($file, $str, 'w+');
    }

    /**
    * Creates the data source if it does not already exist.
    *
    * @param string $src  The file path for the data source
    * @param array  $objs An array of the data objects to be saved
    * @param string $type The name of the object type being saved
    * @return int The boolean integer result of the data source creation
    */
    
    function InitDataSource($src, $objs, $type) {
        if (!file_exists($src)) {
            $xml = $this->xmlHandler->ObjsToXML($objs, $type);
            return $this->WriteFile($src, $xml, 1);
        }
        return true;
    }
        
    /**
    * Moves or renames a file.
    *
    * @param string - the existing path to the file.
    * @param string - the nex path to the file.
    */
    
    /*
    * @deprecated Use FileSystem class
    */
    
    function MoveFile($from, $to) {
        FileSystem::move_file($from, $to);
    }
    
    /**
    * This functio strips the file extension from a file name or path.
    * 
    * @param string - the name or path of the file.
    */
    
    function TrimExtension($file) {
        $file_arr = explode('.', $file);
        return $file_arr[0];
    }
    
    /**
    * Returns the file extension of a file (an example of bad function
    * naming. It should be 'getFileExtension()'.
    *
    * @param string - the name or path of the file.
    */
    
    function GetImageExtension($file) {
        $file_arr = explode('.', $file);
        if (count($file_arr) > 1) {
            return $file_arr[(count($file_arr)-1)];
        }
        return null;
    }
    
    /**
    * Returns the name of a sub-directory withing a path specified
    * by the position of the sub-dir within the path. The positions begin
    * at zero (same as arrays).
    *
    * @param string - the full path.
    * @param int    - the offset of the desired sub-directory within the path.
    */

    function SubDirFromPath($path, $index) {
        $dirs = explode('/', $path);
        
        if ($dirs[count($dirs)-1] == '') {
            $dirs = array_slice($dirs, 0, count($dirs)-1);
        }
        
        if ($index == 'last') {
            $index = count($dirs)-1;
            return $dirs[$index];
        }
        if ($index == 'first') {
            return $dirs[0];
        }
        $index--;
        return $dirs[$index];
    }
    
    /**
    * Lists all of the files within a directory tree. This function is
    * recursive so you will get ALL of the files within the directory tree.
    * Files are returned in alphabetical order but the sorting is performed
    * on the full path so the names of directories will affect the sort order.
    *
    * @param string - the top-level directory to read.
    * @param array  - an array of file paths. Typically left null when called.
    */
    
    /*
    * @deprecated Use FileSystem class
    */

    function ListFiles($dir, $files=array()) {
        return $this->ListFilesOptionalRecurse($dir, 1, $files);
    }
    
    /**
    * Lists all of the files within a directory tree. This function can be
    * optionally recursive or not. If you need to list the files in a single
    * directory, use this function with argument 2 set to 0.
    *
    * @param string - the top-level directory to read.
    * @param bool   - whether or not to recurse the directory tree of dir.
    * @param array  - an array of file paths. Typically left null when called.
    */
    
    /*
    * @deprecated Use FileSystem class
    */
    
    function ListFilesOptionalRecurse($dir, $recurse=1, $files=array()) {
        return FileSystem::list_files($dir, $recurse, $files);
    }
    
    /**
    * Lists all of the directories within a directory tree. This function is
    * recursive so it lists all child directories of the top-level node (dir).
    *
    * @param string - the top-level directory to read.
    * @param array  - an array of directory paths. Typically left null when called.
    */
    
    /*
    * @deprecated Use FileSystem class
    */
    
    function ListDirs($dir, $dirs=array()) {
        return $this->ListDirsOptionalRecurse($dir, 1, $dirs);
    }
    
    /**
    * Lists all of the directories within a directory tree. This function is
    * optionally recursive. To list the sub-directories of only the first level
    * of the specified top-level directory, set argument 2 to 0.
    *
    * @param string - the top-level directory to read.
    * @param bool   - whether or not to recurse the directory tree.
    * @param array  - an array of directory paths. Typically left null when called.
    */
    
    /*
    * @deprecated Use FileSystem class
    */
    
    function ListDirsOptionalRecurse($dir, $recurse=1, $dirs=array()) {
        return FileSystem::list_dirs($dir, $recurse, $dirs);
    }
    
    /*
    * @deprecated Use FileSystem class
    */
    
    function InitDir($dir) {
        if (is_dir($dir)) return true;
        return FileSystem::make_dir($dir);
    }
    
    /*
    * @deprecated Use FileSystem class
    */
    
    function NukeDir($dir) {
        return FileSystem::delete_dir($dir);
    }
    
    function ExecNukeDir($dir, $ContentsOnly=1) {
		return FileSystem::delete_dir($dir, $ContentsOnly);
    }
    
    function CopyDir($from, $to) {
        return FileSystem::copy_dir($from, $to);
    }
    
    function ExecCopyDir($from, $to) {
        return FileSystem::copy_dir($from, $to);
    }
    
    // Unzip() will unpack a zip archive located in $file. Be sure to 
    // move the archive to the location in which it is to be unpacked.
    
    function Unzip($file, $destination) {
        if (!file_exists($file)) {
            return false;
        }

        if (class_exists("ZipArchive"))
        {
            $zip = new ZipArchive;
            if ($zip->open($file) === true)
            {
                $zip->extractTo($destination);
                $zip->close();
                return true;
            }
        }

        $disabled = ini_get('disabled_functions');
        $disarr = explode(',', $disabled);
        if (!in_array('exec', $disarr))
        {
            exec(BIN_UNZIP.' -d'.$destination.' '.$file, $res);
            return $res;
        }

        return false;
    }
        
    /**
    * Lists all of the directories within a directory tree down to a specified
    * depth.
    *
    * @param string - the top-level directory to read.
    * @param bool   - whether or not to recurse the directory tree.
    * @param int    - the depth to which to recurse the directory tree.
    * @param array  - an array of directory paths. Typically left null when called.
    */
    
    function ListDirsToLevel($dir, $dirs=array(), $depth='', $lvl=0) {
        $lvl++;
        ini_set('max_execution_time', 10);
        if (!is_dir($dir)) {
            die ('No such directory.');
        }
        if ($root = @opendir($dir)) {
            while ($file = readdir($root)) {
                if ($file{0} == '.') {
                    continue;
                }
                if (is_dir($dir.$file)) {
                    $dirs[] = $dir.$file.'/';
                    if ($depth > 1 && $depth < $lvl) {
                        $dirs = array_merge(
                            $dirs, 
                            $this->ListDirs($dir.$file.'/', $depth, $lvl)
                        );
                    }
                } 
                else {
                    continue;
                }
            }
        }
        return $dirs;
    }
    
    /**
    *
    * Transforms a flat array into COLS X ROWS. 
    * This is very useful for building
    * tables of specified COLS X ROWS sizes.
    *
    * For example, if you have an array of 8 items that you
    * want returned as a 3 X 3 matrix, pass the array and 
    * $colcount = 3.
    *
    * Example:
    *
    * <pre>
    * $arr = array(0, 1, 2, 3, 4, 5, 6, 7);
    * ArrayToMatrix($arr, 3);
    *
    * Returns:
    *
    * array(
    *    [0]=> array(0, 1, 2)
    *    [1]=> array(3, 4, 5)
    *    [3]=> array(6, 7, 8)
    *  )
    *
    * Which is an array representation of:
    *
    *  0 | 1 | 2
    * -----------
    *  3 | 4 | 5
    * -----------
    *  6 | 7 | 8
    *
    * If the array is smaller than the matrix size, 
    * the remaining cells will be filled with null (n) values.
    *
    * Example 7 items in a 3 X 3 matrix:
    *
    * $arr = array(0, 1, 2, 3, 4, 5, 6);
    * ArrayToMatrix($arr, 3);
    * Returns:
    *
    * array(
    *    [0]=> array(0, 1, 2)
    *    [1]=> array(3, 4, 5)
    *    [3]=> array(6, , )
    *  )
    *
    * Which is an array representation of:
    *
    *  0 | 1 | 2
    * -----------
    *  3 | 4 | 5
    * -----------
    *  6 |   |  
    * </pre>
    *
    * @param array   $arr the flat array to convert to a multi-dim array.
    * @param integer $colcount the number of columns in the matrix.
    * @return array
    */
    
    function ArrayToMatrix($arr, $colcount=3) {
        $matrix = array();
        $rowcount = ceil(count($arr)/$colcount);
        for ($i=0, $offset=0; $i<$rowcount; $i++, $offset+=$colcount) {
            for ($j=$offset; $j<($offset+$colcount); $j++) {
                $matrix[$i][$j] = isset($arr[$j]) ? $arr[$j] : null ;
            }
        }
       return $matrix;
    }
    
    /*
    * The difference between ArrayToGrid() and ArrayToMatrix() is that
    * ArrayToGrid() resests the cell indices at the beginning of each column, 
    * whereas ArrayToMatrix() numbers the cells sequentially regardless of the
    * column in which they fall.
    * 
    * Examples:
    *
    * ArrayToGrid()
    *
    *  0 | 1 | 2
    * -----------
    *  0 | 1 | 2
    * -----------
    *  0 | 1 | 2
    *
    *
    * ArrayToMatrix()
    *
    *  0 | 1 | 2
    * -----------
    *  3 | 4 | 5
    * -----------
    *  6 | 7 | 8
    *
    */
    
    function ArrayToGrid($arr, $colcount=3) {
        $grid = array();
        $rowcount = ceil(count($arr)/$colcount);
        $trueindex = 0;
        for ($i=0; $i<$rowcount; $i++) {
            for ($j=0; $j<$colcount; $j++) {
                $grid[$i][$j] = isset($arr[$trueindex]) ? $arr[$trueindex] : null ;
                $trueindex++;
            }
        }
       return $grid;
    }
    
    /**
    * Counts the number of objects in an array that have some property
    * value matching a test string.
    *
    * @param array - the array of objects to test.
    * @param bool  - the property to search on.
    * @param int   - the value to search for.
    */
    
    function CountObjs($objs, $key, $value) {
        $x=0;
        foreach ($objs as $obj) {
            if ($obj->$key == $value) {
                $x++;
            }
        }
        return $x;
    }
    
    /**
    * Binds an associative array of data to an object where the names 
    * of the object properties match the names of the array indices.
    *
    * Exmaple: Binding the values of a POSTed form to an object.
    * 
    * @param object - the object to which to bind the array.
    * @param array  - the associative array to bind to the object.
    */
    
    function ArrayToObj($obj, $arr) {
        foreach ($arr as $k=>$v) {
            if ($k == 'submit') continue;
            $obj->$k = is_array($v) ? implode(', ', $v) : $v ;
        }    
        return $obj;
    }
    
    /**
    * Updates the property values of an object with the values of
    * an associateve array. If no new values are passed in the array, 
    * the existing object properties will be maintained.
    *
    * @param object - the object to which to bind the array.
    * @param array  - the associative array to bind to the object.
    */
    
    function UpdateObjFromArray($obj, $arr) {
        $obj2 = new stdClass;
        foreach ($obj as $k=>$v) {
            if (trim($k) != '') {
                $obj2->$k = $v;
            }
        }
        foreach($arr as $k=>$v) {
            if (trim($k) != '') {
                $obj2->$k = $v;
            }
        }
        foreach($obj2 as $k=>$v) {
            if (!isset($arr[$k])) {
                unset($obj2->$k);
            }
        }
        return $obj2;
    }
    
    /**
    * Select an object by the 'id' property from an array of objects.
    *
    * @param array - the array of objects from which to select.
    * @param int   - the id of the object to select.
    */
    
    function SelectObj($objs, $id) {
        if (count($objs) < 1) {
            return false;
        }
        foreach ($objs as $obj) {
            if ($obj->id == $id) {
                return $obj;
            }
        }
        return false;
    }
    
    /**
    * Select an object by a specified property from an array of objects.
    *
    * @param array  - the array of objects from which to select.
    * @param string - the name of the property to search on.
    * @param string - the value to search for.
    */
    
    function SelectObjByKey($objs, $key, $match) {
        foreach ($objs as $obj) {
            if ($obj->$key == $match) {
                return $obj;
            }
        }
        return false;
    }
    
    /**
    * Selects all objects from an array where a named property
    * matches a specified value.
    *
    * @param array  - the array of objects.
    * @param string - the name of the property to search on.
    * @param string - the value to search for.
    */
    
    function SelectObjsByKey($objs, $key, $match) {
        $rows = array();
        foreach ($objs as $obj) {
            if ($obj->$key == $match) {
                array_push($rows, $obj);
            }
        }
        return $rows;
    }
    
    /**
    * Inserts an object into an array by matching a named property
    * with a specified value.
    *
    * @param object - the object to insert.
    * @param array  - the array of objects.
    * @param string - the name of the property to search on.
    * @param string - the value to search for.
    */
    
    function InsertObjByKey($obj, $objs, $key, $match) {
        for ($i=0; $i<count($objs); $i++) {
            if ($objs[$i]->$key == $match) {
                $objs[$i] = $obj;
            }
        }
        return $objs;
    }
    
    /**
    * purpose:   To select an item from within an array of objects or arrays
    *            where the desired object may be stored in a property/key of 
    *            some other object/array.
    *
    * Note:      This function will work with an array of objects or
    *            An array of associative arrays. Each associative array
    *            is converted to an object on the fly and is converted back
    *            to an array so the return type matches the input type.
    *
    * Warning:   This function will NOT work on an array of scalar arrays.
    *
    * example:
    *
    * $item = SelectObjFromTree($myObjs, 'id', 2, 'children');
    *
    * Array
    * (
    *     [0] => stdClass Object
    *         (
    *             [id] => 1
    *             [title] => Parent Item 1
    *             [parent] => 
    *             [children] => Array
    *                 (
    *                     [0] => stdClass Object
    *                         (
    *                             [id] => 2
    *                             [title] => Child Item 1
    *                             [parent] => 1
    *                       )
    * 
    *               )
    * 
    *       )
    * 
    *     [1] => stdClass Object
    *         (
    *             [id] => 3
    *             [title] => Parent Item 2
    *             [parent] => 
    *       )
    * 
    *     [2] => stdClass Object
    *         (
    *             [id] => 4
    *             [title] => Parent Item 3
    *             [parent] => 
    *       )
    * 
    *)    
    *
    * The example above will return $objs[0]->children[0].
    *
    * @param array  - the array of objects.
    * @param string - the object property to search on.
    * @param string - the value for which to search.
    * @param string - the name of the property potentially holding the
    * nested objects.
    */
    
    function SelectItemFromTree($objs, $key, $match, $children) {
        $returnType = 'object';
        for ($i=0; $i<count($objs); $i++) {
            $parent = $objs[$i];
            
            if (is_array($parent)) {
                $returnType = 'array';
                $parent = (object) $parent;
            }
            if ($parent->$key == $match) {
                if ($returnType == 'array') {
                    $parent = (array) $parent;
                }
                return $parent;
            } 
            else {
                if (isset($parent->$children)) {
                    foreach ($parent->$children as $child) {
                        if (is_array($child)) {
                            $returnType = 'array';
                            $child = (object) $child;
                        }
                        if (isset($child->$key) && 
                             !empty($child->$key) && 
                             $child->$key == $match)
                        {
                            if ($returnType == 'array') {
                                $child = (array) $child;
                            }
                            return $child;
                        }
                    }
                }
            }
        }
        return false;
    }
    
    /**
    * Tests whether or not an object with a named property matching
    * a specified value is in an array. Similar to PHP's in_arry().
    *
    * @param object - the object to search for.
    * @param array  - the array of objects.
    * @param string - the name of the property to match on.
    */
    
    function ObjInArray($obj, $array, $key) {
        foreach ($array as $a) {
            if ($a->$key == $obj->$key) {
                return true;
            }
        }
        return false;
    }
    
    /**
    * Returns the property of a parent object matching the search string.
    *
    * @param array  - the array of objects to search.
    * @param object - the object of whose parent the property is needed.
    * @param string - the name of the property to return.
    * @param string - the default value to return if no match is found.
    */
    
    function GetParentProperty($objs, $obj, $key, $default) {
        $property = $default;
        if (isset($obj->$key) && 
             $obj->$key != 'null' && 
             trim($obj->$key) != '')
        {
            $pid       = $obj->parent;
            $parentObj = $this->SelectObj($objs, $pid);
            $property  = $parentObj->$key;
        }
        return $property;
    }
    
    /**
    * Modified in r247 to allow $v to be an array of values to match
    *
    * Returns all objects from an array whose named property
    * matches the search string.
    *
    * @param array  - the array of objects to search.
    * @param string - the name of the property to search on.
    * @param string - the value to search for.
    */
    
    function SelectObjs($objs, $k, $v) {
        if (count($objs) == 0) return array();
        $matches = array();
        foreach($objs as $obj) {
            if (isset($obj->$k)) {
				if (is_array($v)) {
				    if (in_array($obj->$k, $v)) {
				        array_push($matches, $obj);
				    }
				}
				else {
					if (strcasecmp($obj->$k, $v) === 0) {
						array_push($matches, $obj);
					}
				}
            }
        }
        return $matches;
    }
    
    /**
    * Inserts an object into an array where the object property
    * matches the search property. If no match is found, the object
    * is inserted at the end of the array.
    *
    * @param array  - the array of objects to search.
    * @param object - the object to be inserted.
    * @param string - the object property to match on.
    */
    
    function InsertObj($objs, $obj, $match) {
        $marker = 0;
        for ($i=0; $i<count($objs); $i++) {
            if ($objs[$i]->$match == $obj->$match) {
                $objs[$i] = $obj;
                $marker = 1;    
            }
        }
        if ($marker === 0) {
            array_push($objs, $obj);
        }
        return $objs;
    }
    
    /**
    * Inserts an object into an array at a specified position.
    *
    * @param array  - the array of objects to search.
    * @param int    - the id of the object to be re-positioned.
    * @param int    - the position to which to move the object.
    */
    
    function OrderObjs($objs, $id, $index) {
        
        $last = count($objs) - 1;
        $index--;
    
        $obj = $this->SelectObj($objs, $id);
        $objs = $this->DeleteObj($objs, $id);
        $neworder = array();
        
        switch ($index) {
            case 0:
                array_push($neworder, $obj);
                $objs = array_merge($neworder, $objs);
                break;
            case $last:
                array_push($objs, $obj);
                break;
            default:
                $before = array_slice($objs, 0, $index);
                $after = array_slice($objs, $index);
                array_push($before, $obj);
                $objs = array_merge($before, $after);
                break;
        }
        
        return $objs;
    }
    
    /**
    * Deletes an object from an array.
    *
    * @param array  - the array of objects.
    * @param int    - the id of the object to be deleted.
    */
    
    function DeleteObj($objs, $id) {
        $newObjs = array();
        for ($i=0; $i<count($objs); $i++) {
            if ($objs[$i]->id != $id) {
               array_push($newObjs, $objs[$i]);
            }
        }
        return $newObjs;
    }
    
    /**
    * Replaces needle in haystack. The difference between this function
    * and PHP's str_replace() is that this function will replace needle
    * in an array of haystacks.
    *
    * @param mixed  - the subject of the replacement.
    * @param string - the string to be replaced.
    * @param string - the string with which to replace the needle.
    */
    
    function SBStrReplace($haystack, $needle, $replace) {
        if (is_array($haystack)) {
            for ($i=0; $i<count($haystack); $i++) {
                $pile = $haystack[$i];
                if (strpos($pile, $needle) !== false) {
                    $pile = str_replace($needle, $replace, $pile);
                    $haystack[$i] = $pile;
                }
            }
        } 
        else {
            $haystack = str_replace($needle, $replace, $haystack);
        }
        return $haystack;
    }
    
    /**
    * This function determines which of the width or height is larger, 
    * then determines the scale ratio and scales the image values so that
    * the larger of the image dimensions does not exceed the maximum
    * desired dimension.
    *
    * @param array - an array of current array(width, height) values of the image.
    * @param int   - the maximum width of the image.
    * @param int   - the maximum height of the image.
    */
    
    function ImageDimsToMaxDim($dims, $maxwidth, $maxheight) {
    
        $width    = $dims[0];
        $height    = $dims[1];
        
        $widthratio = 1;
        if ($width > $maxwidth) {
            $widthratio = $maxwidth/$width;
        }
        
        $heightratio = 1;
        if ($height > $maxheight) {
            $heightratio = $maxheight/$height;
        }
        
        $ratio = $heightratio;
        if ($widthratio < $heightratio) {
            $ratio = $widthratio;
        }
        
        // Scale the images
        
        $width     = ceil($width * $ratio);
        $height     = ceil($height * $ratio);
        
        // Let's tweak the scale so the new dims match the max dims exactly
        // If the ratio == 1, no need
        
        if ($ratio == $heightratio && $ratio != 1) {
            if ($height < $maxheight) {
                while ($height < $maxheight) {
                    $ratio = $ratio * 1.01;
                    $height = ceil($height * $ratio);
                }
            }
        }
        
        if ($ratio == $widthratio && $ratio != 1) {
            if ($width < $maxwidth) {
                while ($width < $maxwidth) {
                    $ratio = $ratio * 1.01;
                    $width = ceil($width * $ratio);
                }
            }
        }

        return array($width, $height);
    }
    
    /**
    * Returns the width and height of an image in the format:
    * array(width, height).
    *
    * @param string - the path to the image.
    */
    
    function ImageDims($fp) {
        if (!file_exists($fp) || is_dir($fp)) {
            return array(0, 0);
        }
        return getimagesize($fp);
    }
    
    /**
    * Returns the width of an image.
    *
    * @param string - the path to the image.
    */
    
    function ImageWidth($fp) {
        $dims = $this->ImageDims($fp);
        return $dims[0];
    }
    
    /**
    * Returns the height of an image.
    *
    * @param string - the path to the image.
    */
    
    function ImageHeight($fp) {
        $dims = $this->ImageDims($fp);
        return $dims[1];
    }
    
    function RadioOption($name, $value, $label, $checked=0) {
        $option = '<input type="radio" name="'.$name.'" ';
        $option .= 'value="'.$value.'" ';
        if ($checked) {
            $option .= ' checked="checked" ';
        }
        $option .= '/>&nbsp;'.$label;
        return $option;
    }
    
    function RadioSelector($options) {
        if (!isset($options) || empty($options)) {
            die('Core Says: No radio options given in RadioSelector()');
        }
        return implode("\r\n", $options);
    }

    /**
    * Makes an <option> element for an HTML select list.
    *
    * @param string - the innerHTML of the option element.
    * @param string - form input value of the option element.
    * @param string - the currently selected option.
    */

    function MakeOption($title, $value, $selected='') {
        $sel = '';
        if ($selected == 1) {
            $sel = ' selected="selected"';
        }
        return str_repeat(' ', 8) .
            '<option value="'.$value.'"'.$sel.'>'.$title.'</option>';
    }
    
    function SelectorOptions($opts, $selected=null) {
        $res = array();
        foreach ($opts as $k=>$v) {
            $s = $k == $selected ? ' selected="selected"' : null ;
            array_push($res, '<option value="'.$k.'"'.$s.'>'.$v.'</option>');
        }
        return $res;
    }
    
    /**
    * Makes an <option> group element for an HTML select list.
    *
    * @param array  - an array of option elements.
    * @param string - the label for the option group.
    */
    
    function MakeOptionGroup($options, $label) {
        $html = str_repeat(' ', 4).'<optgroup label="'.$label.'">'."\r\n";
        for ($i=0; $i<count($options); $i++) {
            $html .= $options[$i];
        }
        $html .= str_repeat(' ', 4).'</optgroup>';
        return $html;
    }

    function Selector($name, $keyValuePairs, $selected=null) {
		$html = "<select name=\"$name\">\n";
		for ($i=0; $i<count($keyValuePairs); $i++) {
		    $value = $keyValuePairs[$i]['value'];
			$text  = $keyValuePairs[$i]['text'];
		    $s = $value == $selected ? " selected=\"selected\"" : null ;
		    $html .= "<option value=\"$value\"$s>$text</option>\n";
		}
		$html .= "</select>\n";
		return $html;
    }
    
    /**
    * Makes an HTML select list
    *
    * @param array  - an array of option elements.
    * @param string - the name value of the select list.
    * @param int    - the number of visible options (size) of the select list.
    * @param string - optional JavaScript code for the select list.
    */
    
    function SelectList($arr, $name, $size=1, $js='') {
        $html = '<select name="'.$name.'" size="'.$size.'" '.$js.'>'."\r\n";
        for ($i=0; $i<count($arr); $i++) {
            $html .= $arr[$i]."\r\n";
        }
        $html .= '</select>'."\r\n";
        return $html;
    }
    
    /**
    * Creates an HTML select list with Yes and No values.
    *
    * @param string - the name value of the select list.
    * @param int    - the selected option.
    */
    
    function YesNoList($name, $selected=1) {
        $options = array();
        $selected = intval($selected);
        $s = $selected == 1 ? $s = 1 : 0 ;
        array_push($options, $this->MakeOption('Yes', 1, $s));
        $s = $selected == 0 ? 1 : 0 ;
        array_push($options, $this->MakeOption('No', 0, $s));
        $selector = $this->SelectList($options, $name);
        return $selector;
    }
    
    /**
    * Creates an HTML select list with AM and PM values.
    *
    * @param string - the name value of the select list.
    * @param string - the selected option.
    */
    
    function MeridianSelector($meridian='') {
        $opts = array();
        $s = $meridian == 'AM' ? 1 : 0 ;
        array_push($opts, $this->MakeOption('AM', 'AM', $s));
        $s = $meridian == 'PM' ? 1 : 0 ;
        array_push($opts, $this->MakeOption('PM', 'PM', $s));
        $merSelector = $this->SelectList($opts, 'meridian');
        return $merSelector;
    }
    
    /**
    * Returns an array of tokens of form {token} found within a 
    * text blob (for instance a skin).
    *
    * @param string - the text blob to search for tokens.
    */
    
    function GetTokenList($str) {
          preg_match_all(
              SB_REGEX_TOKEN, $str, $tokens, PREG_SPLIT_DELIM_CAPTURE
          );
          $result = array();
          for ($i=0; $i<count($tokens); $i++) {
              if (!in_array($tokens[$i][0], $result)) {
                  array_push($result, $tokens[$i][0]);
              }
          }
          return $result;
    }
    
    /**
    * NOTE: This function will likely be moved to the Skin class in 
    * a future version of SkyBlue.
    *
    * Returns an array of region tokens of form {region:token} found within a 
    * SkyBlue skin.
    *
    * @param string - the skin to search for tokens.
    */
    
    function GetPageRegions($str) {
          preg_match_all(
              SB_REGEX_REGION_TOKEN, $str, $tokens, PREG_SPLIT_DELIM_CAPTURE
          );
          $result = array();
          for ($i=0; $i<count($tokens); $i++) {
              if (!in_array($tokens[$i][0], $result)) {
                  array_push($result, $tokens[$i][0]);
              }
          }
          return $result;
    }
    
    function RegexGetTokens($pattern, $str) {
          preg_match_all(
              $pattern, $str, $tokens, PREG_SPLIT_DELIM_CAPTURE
          );
          $result = array();
          for ($i=0; $i<count($tokens); $i++) {
              if (!in_array($tokens[$i][0], $result)) {
                  array_push($result, $tokens[$i][0]);
              }
          }
          return $result;
    }
    
    /**
    * Adds localization terms to site elements such as form field labels.
    *
    * @param ref - a reference to the skin to Localize
    */
    
    function Localize(&$skin) {
        $tokens = $this->RegexGetTokens("/[TERM:[a-zA-Z0-9]+]/", $skin);
        foreach($tokens as $k=>$v) {
            $term = str_replace('[TERM:', '', $v);
            $term = str_replace(']', '', $term);
            $skin = str_replace($v, $this->terms[$term], $skin);
        }
    }
    
    /**
    * Creates an HTML selector for skin tokens. This function parses the skin
    * to find all the tokens, then creates teh HTML select element.
    *
    * @param string - the skin for which to create the token list.
    * @param string - the name of the HTML select element.
    * @param string - the currently selected token.
    */
        
    function MakeTokenSelector($skin, $name, $selected='') {
        $skinoutput = $this->SBReadFile($skin);
        $tokens     = $this->GetTokenList($skinoutput);
        $list       = array();
        array_push($list, $this->MakeOption(' -- Select Token -- ', ''));
        for ($i=0; $i<count($tokens); $i++) {
            $s = $selected == $tokens[$i] ? 1 : 0 ;
            array_push($list, $this->MakeOption($tokens[$i], $tokens[$i], $s));
        }
        return $this->SelectList($list, $name);
    }
    
    /**
    * DEPRECATED! DO NOT USE!
    * 
    * Creates an HTML selector for ordering objects in an array.
    *
    * @param array  - the array of objects for the selector.
    * @param string - the obj->title property to omit from the selector.
    */
    
    function OrderSelector($objs, $omit) {
        $selector     = '';
        if (count($objs) > 1) {
            $options = array();
            array_push($options, $this->MakeOption(' -- Select Order -- ', '', 0));
            array_push($options, $this->MakeOption('1 - First', 1, 0));
            $ticker = 0;
            foreach ($objs as $obj) {
                if ($omit != $obj->title) {
                    array_push(
                        $options,
                        $this->MakeOption(
                            ($ticker + 2).' - '.$obj->title, 
                            ($ticker + 2), ''
                        )
                    );
                    $ticker++;
                }
            }
            array_push(
                $options,
                $this->MakeOption(
                    ($ticker + 2).' - Last', ($ticker + 2), 0
                )    
            );
            
            $selector  .= $this->SelectList($options, 'order');
            $selector  .= "\r\n";
        }
        else {
            $selector = NO_ITEMS_TO_ORDER_STRING;
        }
        return $selector;
    }
    
    /**
    * Creates an HTML selector for ordering objects in an array. The difference
    * between this function and OrderSelector() is that this function can
    * match the omitted object on any property.
    *
    * @param array  - the array of objects for the selector.
    * @param string - the obj->property on which to match the omitted object.
    * @param string - the value to match on the omitted object.
    */
    
    function OrderSelector2($objs, $key, $value) {
        $selector     = '';
        if (count($objs) > 1) {
            $options = array();
            array_push($options, $this->MakeOption(' -- Select Order -- ', '', 0));
            array_push($options, $this->MakeOption('1&nbsp;&nbsp;&nbsp;- First', 1, 0));
            $ticker = 0;
            foreach ($objs as $obj) {
                if ($value != $obj->$key) {
                    $pad = $ticker + 2 < 10 ? '&nbsp;&nbsp;' : null ;
                    array_push(
                        $options,
                        $this->MakeOption(
                            ($ticker + 2).$pad.' - '.$obj->$key, 
                            ($ticker + 2), ''
                        )
                    );
                    $ticker++;
                }
            }
            array_push(
                $options,
                $this->MakeOption(
                    ($ticker + 2) . ' - Last', ($ticker + 2), 0
                )
            );
            
            $selector  .= $this->SelectList($options, 'order');
            $selector  .= "\r\n";
        }
        else {
            $selector = NO_ITEMS_TO_ORDER_STRING;
        }
        return $selector;
    }
    
    /**
    * Creates an HTML selector for all images in a directory tree.
    *
    * @param string - the name of the HTML select element.
    * @param string - the path to the directory of images.
    * @param string - the currently selected image in the select list.
    * @param string - JavaScript code for selector behaviour.
    */
    
    function ImageSelector($selname, $subdir, $match='', $js='') {
        global $config;
        
        $imgs = $this->ListFiles(SB_MEDIA_DIR.$subdir);
        $options = array();
        array_push($options, $this->MakeOption(' -- Select An Image -- ', '', 0));
        for ($i=0; $i<count($imgs); $i++) {
            $name = basename($imgs[$i]);
            $path = str_replace('../', '', $imgs[$i]);
            $selected = 0;
            if ($match == $path) {
                $selected = 1;
            }
            array_push($options, $this->MakeOption($name, $path, $selected));
        }
        return $this->SelectList($options, $selname, 1, $js);
    }    
    
    /**
    * Validates a file path as being existent or non-existent.
    *
    * @param string - the path to the directory or file.
    */

    function ValidatePath($path) {
        if (file_exists($path)) {
            return true;
        } 
        return false;
    }
        
    function TrimArrayItems($arr) {
        for ($i=0; $i<count($arr); $i++) {
            $arr[$i] = trim($arr[$i]);
        }
        return $arr;
    }
    
    function OffsetInArray($arr, $match) {
        for ($i=0; $i<count($arr); $i++) {
            if ($arr[$i] == $match) {
                return $i;
            }
        }
        return -1;
    }

    /**
    * DEPRECATED! DO NOT USE!
    *
    * This function will be moved to a State Selector
    * plugin that will support localization for countries other than
    * the United States. 
    */
    
    function StateSelector($selected=null) {
	    require_once(SB_LIB_DIR . 'lib.states.php');
		$options = $this->HTML->MakeElement(
			'option', array('value'=>''), ' -- Select State -- '
		);
		foreach ($states as $value=>$text) {
			if (strlen($value) > 2) continue;
			$attrs = array('value'=>strtoupper($value));
			if (strtoupper($value) == $selected) {
				$attrs['selected'] = 'selected';
			}
			$options .= $this->HTML->MakeElement(
				'option', $attrs, ucwords($text)
			);
		}
		return $this->HTML->MakeElement(
			'select',
			array('name'=>'state'),
			$options
		);
	}
    
    function Dump($obj) {
        die('<pre>' . print_r($obj, true) . '</pre>');
    }

}
Return current item: SkyBlueCanvas Lightweight CMS