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

/**
 * Handles Ajax requests, sending them to plugin sinks.
 *
 */
class AjaxHandler extends ActionHandler
{

	/**
	 * Handles incoming ajax requests for which the user need not be authenticated.
	 * Forwards the request to plugin actions for the "context" portion of the URL.
	 * The following example would set the context of 'foo' and trigger
	 * the plugin action 'ajax_foo'.
	 *
	 * URL::get( 'ajax', 'context=foo' );
	 *
	 */
	public function act_ajax()
	{
		/**
		 * Triggers the ajax plugin action for the context.
		 *
		 * @see AjaxHandler::act_ajax()
		 * @action ajax_{$context}
		 */
		Plugins::act('ajax_' . $this->handler_vars['context'], $this);
	}

	/**
	 * Handles incoming ajax requests for which the user must be authenticated.
	 * Forwards the request to plugin actions for the "context" portion of the URL.
	 *
	 * @see act_ajax()
	 */
	public function act_auth_ajax()
	{
		$user = User::identify();
		if ($user->loggedin) {
			/**
			 * Triggers the ajax plugin action for the context if user is authenticated.
			 *
			 * @see act_auth_ajax()
			 * @action ajax_auth_{$context}
			 */
			Plugins::act('auth_ajax_' . $this->handler_vars['context'], $this);
			exit;
		}
	}

}

?>

repo_name: DaSourcerer/habari


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

/**
 * Handles creating ajax responses, creating them in the standard format.
 *
 */
class AjaxResponse
{

	private $data;
	private $response_code;
	private $message;
	private $callback = null;

	/* By default, we have a successful operation, with no data to return. */
	function __construct($response_code = 200, $message = null, $data = null) {
		$this->response_code = $response_code;
		$this->message = $message;
		$this->data = $data;
	}

	public function __set($var, $val)
	{
		switch( $var ) {
			case 'response_code':
			case 'message':
			case 'data':
			case 'callback':
				$this->$var = $val;
				break;
		}
	}

	public function out()
	{
		$ret_array = array(
			'response_code' => $this->response_code,
			'message' => $this->message,
			'data' => $this->data,
		);

		// if some callback js has been provided, include that too.
		if ($this->callback != null)
		{
			$ret_array['callback'] = $this->callback;
		}

		header('Content-type: application/json');
		echo json_encode($ret_array);
	}

}

?>


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

/**
 *
 * Contains the APCCache class
 */
class APCCache extends Cache
{
	protected $enabled = false;
	protected $cache_data = array();
	protected $prefix;

	/**
	 * Constructor for APCCache
	 */
	public function __construct()
	{
		$this->prefix = Options::get( 'GUID' );
		$this->enabled = extension_loaded( 'apc' );
		if ( !$this->enabled ) {
			Session::error( _t("The APC Cache PHP module is not loaded - the cache is disabled.", "apccache"), 'filecache' );
			EventLog::log( _t("The APC Cache PHP module is not loaded - the cache is disabled.", "apccache"), 'notice', 'cache', 'apccache' );
		}
	}

	/**
	 * Is record with $name in the cache?
	 *
	 * @param string $name name of the cached item
	 * @return boolean TRUE if item is cached, FALSE if not
	 */
	protected function _has( $name, $group )
	{
		if ( !$this->enabled ) {
			return false;
		}
		return apc_fetch( implode( ':', array( $this->prefix, $group, $name ) ) ) !== false;
	}

	/**
	 * Returns the named value from the cache.
	 *
	 * @param string $name The name of the cached item
	 * @return mixed The item value or NULL if it doesn't exist in cache
	 */
	protected function _get( $name, $group )
	{
		if ( !$this->enabled ) {
			return null;
		}
		return apc_fetch( implode( ':', array( $this->prefix, $group, $name ) ) );
	}

	/**
	 * Returns the named values from a group of cache.
	 *
	 * @param string $name The name of the cached item
	 * @return array The cache records of the group
	 */
	protected function _get_group( $group )
	{
		if ( !$this->enabled ) {
			return null;
		}
		$cache_info = apc_cache_info( 'user' );
		$group_cache = array();

		foreach ( $cache_info['cache_list'] as $cache_item ) {
			if ( strpos( $cache_item['info'], $this->prefix . ":$group:" ) === 0 ) {
				$name = substr( $cache_item['info'], strlen( $this->prefix . ":$group:" ) );
				$group_cache[$name] = apc_fetch( $cache_item['info'] );
			}
		}
		return $group_cache;
	}

	/**
	 * Is group named $group in the cache?
	 *
	 * @param string $name name of the cached item
	 * @return boolean TRUE if group is cached, FALSE if not
	 */
	protected function _has_group( $group )
	{
		if ( !$this->enabled ) {
			return null;
		}
		$cache_info = apc_cache_info( 'user' );
		$group_cache = array();

		foreach ( $cache_info['cache_list'] as $cache_item ) {
			if ( strpos( $cache_item['info'], $this->prefix . ":$group:" ) === 0 ) {
				$group_cache[$cache_item['info']] = apc_fetch( $cache_item['info'] );
			}
		}
		return $this->_get_group( $group ) ? true : false;
	}

	protected function _set( $name, $value, $expiry, $group, $keep )
	{
		if ( !$this->enabled ) {
			return null;
		}

		Plugins::act( 'cache_set_before', $name, $group, $value, $expiry );

		$this->cache_data[$group][$name] = $value;

		if($keep) {
			$keepcache = apc_fetch( $this->prefix . ':keepcache');
			if(!is_array($keepcache)) {
				$keepcache = array();
			}
			$keepcache[$group][$name] = intval($expiry);
			apc_store( $this->prefix . ':keepcache', $keepcache);
			$expiry = 0;
		}

		apc_store( implode( ':', array( $this->prefix, $group, $name ) ), $value, intval($expiry) );

		Plugins::act( 'cache_set_after', $name, $group, $value, $expiry );
	}

	/**
	 * Expires the named value from the cache.
	 *
	 * @param string $name The name of the cached item
	 * @param string $match_mode (optional) how to match bucket names ('strict', 'regex', 'glob') (default 'strict')
	 */
	protected function _expire( $name, $group, $match_mode = 'strict' )
	{
		if ( !$this->enabled ) {
			return null;
		}
		$keys = array();
		switch ( strtolower($match_mode) ) {
			case 'glob':
				if ( array_key_exists( $group, $this->cache_data ) ) {
					$keys = preg_grep( Utils::glob_to_regex( $name ), array_keys( $this->cache_data[$group] ) );
				}
				break;
			case 'regex':
				if ( array_key_exists( $group, $this->cache_data ) ) {
					$keys = preg_grep( $name, array_keys( $this->cache_data[$group] ) );
				}
				break;
			case 'strict':
			default:
				$keys = array( $name );
				break;
		}

		foreach ( $keys as $key ) {
			Plugins::act( 'cache_expire_before', $name, $group );
			apc_delete( implode( ':', array( $this->prefix, $group, $key ) ) );
			Plugins::act( 'cache_expire_after', $name, $group );
		}
	}

	/**
	 * Return whether a named cache value has expired
	 *
	 * @param string $name The name of the cached item
	 * @param string $group The group of the cached item
	 * @return boolean true if the stored value has expired
	 */
	protected function _expired( $name, $group )
	{
		if ( !$this->enabled ) {
			return null;
		}

		// Do not check cached data, since we can return (and cache in this object) data if the cache is set to 'keep'
		$keepcache = apc_fetch( $this->prefix . ':keepcache');

		if ( !self::_has($name,$group) || !isset( $keepcache[$group][$name] ) || $keepcache[$group][$name] < time() ) {
			return true;
		}
		else {
			return false;
		}
	}

	/**
	 * Extend the expiration of the named cached value.
	 *
	 * @param string $name The name of the cached item
	 * @param integer $expiry The duration in seconds to extend the cache expiration by
	 */
	protected function _extend( $name, $expiry, $group )
	{
		if ( !$this->enabled ) {
			return null;
		}

		Plugins::act( 'cache_extend_before', $name, $group, $expiry );

		if ( $this->_has( $name, $group ) ) {
			$cache_data = $this->_get( $name, $group );
			$this->_set( implode( ':', array( $this->prefix, $group, $name ) ), $cache_data, time() + $expiry, $group );
		}

		Plugins::act( 'cache_extend_after', $name, $group, $expiry );
	}

	/**
	 * Remove all cached items
	 */
	protected function _purge()
	{
		Plugins::act( 'cache_purge_before' );

		$cache_info = apc_cache_info( 'user' );

		$delete = array();
		foreach ( $cache_info['cache_list'] as $cache_item ) {
			if ( strpos( $cache_item['info'], $this->prefix . ":" ) === 0 ) {
				$delete[] = $cache_item['info'];
			}
		}
		foreach( $delete as $item ) {
			apc_delete( $item );
		}

		Plugins::act( 'cache_purge_after' );
	}
}

?>


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

/**
 * Habari AtomHandler class
 * Produces Atom feeds and accepts Atom Publishing Protocol input
 *
 * @todo Apply system error handling
 */
class AtomHandler extends ActionHandler
{

	public $user = NULL; // Cache the username

	/**
	 * Constructor for AtomHandler class.
	 * Set some default formatting for Atom output.
	 */
	public function __construct()
	{
		Plugins::act('init_atom');
	}

	/**
	 * Check if a user is authenticated for Atom editing
	 *
	 * @todo This entire function should be put into the User class somehow.
	 * @todo X-WSSE
	 * @param bool $force Force authorization? If so, basic HTTP_AUTH is displayed if not authed
	 * @return User The logged-in user
	 */
	function is_auth( $force = FALSE )
	{
		if ( ( $this->user == NULL ) || ( $force != FALSE ) ) {
			if ( isset( $_SERVER['PHP_AUTH_USER'] ) ) {
				User::authenticate( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] );
			}

			$this->user = User::identify();
			if ( ( $force != FALSE ) && ( !$this->user->loggedin ) ) {
				header( 'HTTP/1.1 401 Unauthorized', true, 401 );
				header( 'Status: 401 Unauthorized' );
				header( 'WWW-Authenticate: Basic realm="Habari"' );
				die();
			}
		}
		return $this->user->loggedin;
	}

	/**
	 * Creates a basic Atom-format XML structure
	 *
	 * @param string $alternate the IRI of an alternate version.
	 * @param string $self The preferred URI for retrieving Atom Feed Documents representing this Atom feed.
	 * @param string $id a permanent, universally unique identifier for an the feed.
	 * @param HabariDateTime $updated The most recent update in the collection to which this feed applies.
	 *
	 * @return SimpleXMLElement The requested Atom document
	 */
	public function create_atom_wrapper( $alternate, $self, $id, $updated = NULL )
	{
		// Store handler vars since we'll be using them a lot.
		$handler_vars = Controller::get_handler_vars();

		// Retrieve the current matched rule and store its name and argument values.
		$rr = URL::get_matched_rule();
		$rr_name = $rr->name;
		$rr_args = $rr->named_arg_values;

		// Build the namespaces, plugins can alter it to override or insert their own.
		$namespaces = array( 'default' => 'http://www.w3.org/2005/Atom' );
		$namespaces = Plugins::filter( 'atom_get_collection_namespaces', $namespaces );
		$namespaces = array_map( create_function( '$value,$key', 'return ( ( $key == "default" ) ? "xmlns" : "xmlns:" . $key ) . "=\"" . $value ."\"";' ), $namespaces, array_keys($namespaces) );
		$namespaces = implode( ' ', $namespaces );

		$xml = new SimpleXMLElement( '<feed ' . $namespaces . '></feed>' );

		$feed_generator = $xml->addChild( 'generator', 'Habari' );
		$feed_generator->addAttribute( 'uri', 'http://www.habariproject.org/' );
		$feed_generator->addAttribute( 'version', Version::get_habariversion() );

		$feed_id = $xml->addChild( 'id', 'tag:' . Site::get_url('hostname') . ',' . date("Y-m-d") . ':' . $id . '/' . Options::get( 'GUID' ) );

		$feed_title = $xml->addChild( 'title', Utils::htmlspecialchars( Options::get( 'title' ) ) );

		if ( $tagline = Options::get( 'tagline' ) ) {
			$feed_subtitle = $xml->addChild( 'subtitle', Utils::htmlspecialchars( $tagline ) );
		}

		if ( $updated == NULL) {
			$feed_updated = $xml->addChild( 'updated', HabariDateTime::date_create()->get( 'c' ) );
		}
		else {
			$feed_updated = $xml->addChild( 'updated', $updated->get( 'c' ) );
		}

		$feed_link = $xml->addChild( 'link' );
		$feed_link->addAttribute( 'rel', 'alternate' );
		$feed_link->addAttribute( 'href', $alternate );

		$feed_link = $xml->addChild( 'link' );
		$feed_link->addAttribute( 'rel', 'self' );
		$feed_link->addAttribute( 'href', $self );

		Plugins::act( 'atom_create_wrapper', $xml );
		return $xml;
	}

	/**
	 * Adds pagination link rels to feeds
	 *
	 * @param SimpleXMLElement $xml The Atom feed.
	 * @param int $count The total number of items of the type in the feed.
	 * @return The altered XML feed.
	 */
	public function add_pagination_links( $xml, $count )
	{
		// Retrieve the current matched rule and store its name and argument values.
		$rr = URL::get_matched_rule();
		$rr_name = $rr->name;
		$rr_args = $rr->named_arg_values;

		$page = ( isset( $rr_args['page'] ) ) ? $rr_args['page'] : 1;
		$firstpage = 1;
		$lastpage = ceil( $count / Options::get( 'atom_entries' ) );

		if ( $lastpage > 1 ) {
			$nextpage = intval( $page ) + 1;
			$prevpage = intval( $page ) - 1;

			$rr_args['page'] = $firstpage;
			$feed_link = $xml->addChild( 'link' );
			$feed_link->addAttribute( 'rel', 'first' );
			$feed_link->addAttribute( 'href', URL::get( $rr_name, $rr_args ) );
			$feed_link->addAttribute( 'type', 'application/atom+xml' );
			$feed_link->addAttribute( 'title', _t('First Page') );

			if ( $prevpage > $firstpage ) {
				$rr_args['page'] = $prevpage;
				$feed_link = $xml->addChild( 'link' );
				$feed_link->addAttribute( 'rel', 'previous' );
				$feed_link->addAttribute( 'href', URL::get( $rr_name, $rr_args ) );
				$feed_link->addAttribute( 'type', 'application/atom+xml' );
				$feed_link->addAttribute( 'title', _t('Previous Page') );
			}

			if ( $nextpage <= $lastpage ) {
				$rr_args['page'] = $nextpage;
				$feed_link = $xml->addChild( 'link' );
				$feed_link->addAttribute( 'rel', 'next' );
				$feed_link->addAttribute( 'href', URL::get( $rr_name, $rr_args ) );
				$feed_link->addAttribute( 'type', 'application/atom+xml' );
				$feed_link->addAttribute( 'title', _t('Next Page') );
			}

			$rr_args['page'] = $lastpage;
			$feed_link = $xml->addChild( 'link' );
			$feed_link->addAttribute( 'rel', 'last' );
			$feed_link->addAttribute( 'href', URL::get( $rr_name, $rr_args ) );
			$feed_link->addAttribute( 'type', 'application/atom+xml' );
			$feed_link->addAttribute( 'title', _t('Last Page') );
		}

		return $xml;
	}

	/**
	 * Add posts as items in the provided xml structure
	 * @param SimpleXMLElement $xml The document to add to
	 * @param array $posts An array of Posts to add to the XML
	 * @return SimpleXMLElement The resultant XML with added posts
	 */
	public function add_posts($xml, $posts)
	{
		foreach ( $posts as $post ) {
			$user = User::get_by_id( $post->user_id );
			$title = ( $this->is_auth() ) ? $post->title : $post->title_atom;
			$content = ( $this->is_auth() ) ? Utils::htmlspecialchars( $post->content ) : Utils::htmlspecialchars( $post->content_atom );

			$content = Plugins::filter( 'atom_add_post', $content );

			$feed_entry = $xml->addChild( 'entry' );
			$entry_title = $feed_entry->addChild( 'title', $title );

			$entry_link = $feed_entry->addChild( 'link' );
			$entry_link->addAttribute( 'rel', 'alternate' );
			$entry_link->addAttribute( 'href', $post->permalink_atom );

			$entry_link = $feed_entry->addChild( 'link' );
			$entry_link->addAttribute( 'rel', 'edit' );
			$entry_link->addAttribute( 'href', URL::get( 'atom_entry', "slug={$post->slug}" ) );

			$entry_author = $feed_entry->addChild( 'author' );
			$author_name = $entry_author->addChild( 'name', $user->displayname );
			$author_uri = $entry_author->addChild( 'uri', Site::get_url('habari') );

			$entry_id = $feed_entry->addChild( 'id', $post->guid );

			$entry_updated = $feed_entry->addChild( 'updated', $post->updated->get('c') );
			$entry_edited = $feed_entry->addChild( 'app:edited', $post->modified->get('c'), 'http://www.w3.org/2007/app' );
			$entry_published = $feed_entry->addChild( 'published', $post->pubdate->get('c') );

			if ( $post->status == Post::status('draft') ) {
				$entry_control = $feed_entry->addChild( 'app:control', '', 'http://www.w3.org/2007/app' );
				$entry_draft = $entry_control->addChild( 'app:draft', 'yes', 'http://www.w3.org/2007/app' );
			}

			foreach ( $post->tags as $tag ) {
				$entry_category = $feed_entry->addChild( 'category' );
				$entry_category->addAttribute( 'term', $tag );
			}

			$entry_content = $feed_entry->addChild( 'content', $content );
			$entry_content->addAttribute( 'type', 'html' );
			Plugins::act( 'atom_add_post', $feed_entry, $post );
		}
		return $xml;
	}

	/**
	 * Add comments as items in the provided xml structure
	 * @param SimpleXMLElement $xml The document to add to
	 * @param array $comments An array of Comments to add to the XML
	 * @return SimpleXMLElement The resultant XML with added comments
	 */
	public function add_comments($xml, $comments)
	{
		foreach ( $comments as $comment ) {
			$content = ( $this->is_auth() ) ? Utils::htmlspecialchars( $comment->content ) : Utils::htmlspecialchars( $comment->content_atom );

			$content = Plugins::filter( 'atom_add_comment', $content );

			$item = $xml->addChild( 'entry' );
			$title = $item->addChild( 'title', Utils::htmlspecialchars( sprintf( _t( '%1$s on "%2$s"' ), $comment->name, $comment->post->title ) ) );

			$link = $item->addChild( 'link' );
			$link->addAttribute( 'rel', 'alternate' );
			$link->addAttribute( 'href', $comment->post->permalink . '#comment-' . $comment->id );

			$author = $item->addChild( 'author' );
			$author_name = $author->addChild( 'name', Utils::htmlspecialchars( $comment->name ) );
			$author_uri = $author->addChild( 'uri', Utils::htmlspecialchars( $comment->url ) );

			$id = $item->addChild( 'id', $comment->post->guid . '/' . $comment->id );

			$updated = $item->addChild( 'updated', $comment->date->get('c') );

			$content = $item->addChild( 'content', $content );
			$content->addAttribute( 'type', 'html' );
			Plugins::act( 'atom_add_comment', $item, $comment );
		}
		return $xml;
	}

	/**
	 * Handle incoming requests for Atom entry collections
	 */
	public function act_collection()
	{
		Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) );
		switch( $_SERVER['REQUEST_METHOD'] ) {
			case 'GET':
			case 'HEAD':
				$this->get_collection();
				break;
			case 'POST':
				$this->post_collection();
				break;
		}
	}

	/**
	 * function act_entry
	 * 'index' should be 'slug'
	 */
	public function act_entry()
	{
		Utils::check_request_method( array( 'GET', 'HEAD', 'POST', 'PUT', 'DELETE' ) );
		switch( $_SERVER['REQUEST_METHOD'] ) {
			case 'GET':
			case 'HEAD':
			case 'POST':
				$this->get_entry( $this->handler_vars['slug'] );
				break;
			case 'PUT':
				$this->put_entry( $this->handler_vars['slug'] );
				break;
			case 'DELETE':
				$this->delete_entry( $this->handler_vars['slug'] );
				break;
		}
	}

	/**
	 * Handle incoming requests for RSD
	 *
	 * @todo Move the internal list of supported feeds into options to allow dynamic editing of capabilities
	 */
	public function act_rsd()
	{
		Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) );

		/**
		 * List of APIs supported by the RSD
		 * Refer to namespace for required elements/attributes.
		 */
		$apis_list = array(
			'Atom' => array(
				'preferred' => 'true',
				'apiLink' => URL::get( 'atom_feed', 'index=1' ), // This should be the XML-RPC url
				'blogID' => '1',
			),
		);

		$apis_list = Plugins::filter('rsd_api_list', $apis_list);

		$cache_xml = null;

		if ( Cache::has( 'atom:rsd:apis' ) ) {
			$cache_apis = Cache::get( 'atom:rsd:apis' );
			if ( ( $cache_apis === $apis_list ) && Cache::has( 'atom:rsd:xml' ) ) {
				$cache_xml = Cache::get( 'atom:rsd:xml' );
				$cache_xml = simplexml_load_string( $cache_xml );
			}
		}
		else {
			Cache::set( 'atom:rsd:apis', $apis_list );
		}

		if ( $cache_xml instanceOf SimpleXMLElement ) {
			$xml = $cache_xml;
		}
		else {
			$xml = new SimpleXMLElement( '<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd"></rsd>' );

			$rsd_service = $xml->addChild( 'service' );
			$service_engineName = $rsd_service->addChild( 'engineName', 'Habari' );
			$service_engineLink = $rsd_service->addChild( 'engineLink', 'http://www.habariproject.org/' );
			$service_homePageLink = $rsd_service->addChild( 'homePageLink', Site::get_url('habari') );
			$service_apis = $rsd_service->addChild( 'apis' );

			if ( !isset( $apis_list ) || ( count( $apis_list ) < 1 ) ) {
				return false;
			}

			foreach ( $apis_list as $apiName => $atts ) {
				if ( !isset( $atts['preferred'], $atts['apiLink'], $atts['blogID'] ) ) {
					continue;
				}

				$apis_api = $service_apis->addChild( 'api' );
				$apis_api->addAttribute( 'name', $apiName );
				$apis_api->addAttribute( 'preferred', $atts['preferred'] );
				$apis_api->addAttribute( 'apiLink', $atts['apiLink'] );
				$apis_api->addAttribute( 'blogID', $atts['blogID'] == '' ? '1' : $atts['blogID'] );

				if ( !isset( $atts['settings'] ) || ( count( $atts['settings'] ) < 1 ) ) {
					continue;
				}

				$api_settings = $apis_api->addChild( 'settings' );

				foreach ( $atts['settings'] as $settingName => $settingValue ) {
					switch ( $settingName ) {
						case 'docs':
						case 'notes':
							$settings_setting = $api_settings->addChild( $settingName, $settingValue );
							break;
						case 'setting':
							foreach ( $settingValue as $settingArray ) {
								$settings_setting = $api_settings->addChild( 'setting', $settingArray['value'] );
								$settings_setting->addAttribute( 'name', $settingArray['name'] );
							}
							break;
					}
				}
			}

			Cache::set( 'atom:rsd:xml', $xml->asXML() );
		}

		Plugins::act( 'rsd', $xml, $this->handler_vars );
		$xml = $xml->asXML();

		ob_clean();
		header( 'Content-Type: application/rsd+xml' );
		print $xml;
	}

	/**
	 * Handle incoming requests for the introspection document
	 */
	public function act_introspection()
	{
		Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) );

		$cache_xml = null;

		if ( Cache::has( 'atom:introspection:xml' ) ) {
			$cache_xml = Cache::get( 'atom:introspection:xml' );
			$cache_xml = simplexml_load_string( $cache_xml );
		}

		if ( $cache_xml instanceOf SimpleXMLElement ) {
			$xml = $cache_xml;
		}
		else {
			$xml = new SimpleXMLElement( '<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom"></service>' );

			$service_workspace = $xml->addChild( 'workspace' );

			$workspace_title = $service_workspace->addChild( 'atom:title', Utils::htmlspecialchars( Options::get( 'title' ) ), 'http://www.w3.org/2005/Atom' );

			$workspace_collection = $service_workspace->addChild( 'collection' );
			$workspace_collection->addAttribute( 'href', URL::get( 'atom_feed', 'index=1' ) );

			$collection_title = $workspace_collection->addChild( 'atom:title', 'Blog Entries', 'http://www.w3.org/2005/Atom' );
			$collection_accept = $workspace_collection->addChild( 'accept', 'application/atom+xml;type=entry' );

			Cache::set( 'atom:introspection:xml', $xml->asXML() );
		}

		Plugins::act( 'atom_introspection', $xml, $this->handler_vars );
		$xml = $xml->asXML();

		ob_clean();
		header( 'Content-Type: application/atomsvc+xml' );
		print $xml;
	}

	/**
	 * Handle incoming requests for the Atom entry collection for a specific tag
	 */
	public function act_tag_collection()
	{
		Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) );

		$this->get_collection();
	}

	/**
	 * Handle incoming requests for the Atom entry collection for all comments
	 */
	function act_comments( $params = array() )
	{
		Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) );

		$this->get_comments( $params );
	}

	/**
	 * Handle incoming requests for the Atom entry collection for comments on an entry
	 */
	function act_entry_comments()
	{
		Utils::check_request_method( array( 'GET', 'HEAD', 'POST' ) );

		if ( isset( $this->handler_vars['slug'] ) ) {
			$this->act_comments( array( 'slug' => $this->handler_vars['slug'] ) );
		}
		else {
			$this->act_comments( array( 'id' => $this->handler_vars['id'] ) );
		}
	}

	/**
	 * Output an Atom collection of comments based on the supplied parameters.
	 *
	 * @param array $params An array of parameters passed to Comments::get() to retrieve comments
	 */
	function get_comments( $params = array() )
	{
		$comments = null;
		$comments_count = null;

		// Assign self link.
		$self = '';

		// Assign alternate link.
		$alternate = '';

		$updated = HabariDateTime::date_create();

		// Check if this is a feed for a single post
		if ( isset( $params['slug'] ) || isset( $params['id'] ) ) {
			if ( isset( $params['slug'] ) ) {
				$post = Post::get( array( 'slug' => $params['slug'] ) );
			}
			elseif ( isset( $params['id'] ) ) {
				$post = Post::get( array( 'id' => $params['id'] ) );
			}

			// If the post doesn't exist, send a 404
			if ( !$post instanceOf Post ) {
				header( 'HTTP/1.1 404 Not Found', true, 404 );
				die('The post could not be found');
			}

			$comments = $post->comments->approved;
			$comments_count = count( $comments );
			$content_type = Post::type_name( $post->content_type );
			$self = URL::get( "atom_feed_{$content_type}_comments", $post, false );
			$alternate = URL::get( "display_{$content_type}", $post, false );
			if ( $comments_count ) {
				$updated = $comments[$comments_count - 1]->date;
			}
		}
		else {
			$self = URL::get( 'atom_feed_comments' );
			$alternate = URL::get( 'display_home' );
			$params['status'] = Comment::STATUS_APPROVED;
			$comments = Comments::get( $params );
			$comments_count = Comments::count_total( Comment::status('approved') );
			if ( $comments_count ) {
				$updated = $comments[0]->date;
			}
		}

		$id = isset( $params['slug'] ) ? $params['slug'] : 'atom_comments';

		$xml = $this->create_atom_wrapper( $alternate, $self, $id, $updated );

		$xml = $this->add_pagination_links( $xml, $comments_count );

		$xml = $this->add_comments( $xml, $comments );

		Plugins::act( 'atom_get_comments', $xml, $params, $this->handler_vars );
		$xml = $xml->asXML();

		ob_clean();
		header( 'Content-Type: application/atom+xml' );
		print $xml;
	}

	/**
	 * Output the Atom entry for a specific slug
	 *
	 * @param string $slug The slug to get the entry for
	 */
	public function get_entry( $slug )
	{
		$params['slug'] = $slug;
		$params['status'] = $this->is_auth() ? 'any' : Post::status('published');

		if ( $post = Post::get($params) ) {
			// Assign alternate link.
			$alternate = URL::get( 'display_entry', $post, false );
			$self = URL::get( 'atom_entry', $post, false );
			$id = isset( $params['slug'] ) ? $params['slug'] : 'atom_entry';

			$user = User::get_by_id( $post->user_id );
			$title = ( $this->is_auth() ) ? $post->title : $post->title_atom;
			$content = ( $this->is_auth() ) ? Utils::htmlspecialchars( $post->content ) : Utils::htmlspecialchars( $post->content_atom );

			// Build the namespaces, plugins can alter it to override or insert their own.
			$namespaces = array( 'default' => 'http://www.w3.org/2005/Atom' );
			$namespaces = Plugins::filter( 'atom_get_entry_namespaces', $namespaces );
			$namespaces = array_map( create_function( '$value,$key', 'return ( ( $key == "default" ) ? "xmlns" : "xmlns:" . $key ) . "=\"" . $value ."\"";' ), $namespaces, array_keys($namespaces) );
			$namespaces = implode( ' ', $namespaces );

			$xml = new SimpleXMLElement( '<entry ' . $namespaces . '></entry>' );

			$entry = $xml;
			$entry_title = $entry->addChild( 'title', $title );

			$entry_author = $entry->addChild( 'author' );
			$author_name = $entry_author->addChild( 'name', $user->displayname );

			$entry_link = $xml->addChild( 'link' );
			$entry_link->addAttribute( 'rel', 'alternate' );
			$entry_link->addAttribute( 'href', $post->permalink );

			$entry_link = $entry->addChild( 'link' );
			$entry_link->addAttribute( 'rel', 'edit' );
			$entry_link->addAttribute( 'href', URL::get( 'atom_entry', "slug={$post->slug}" ) );

			$entry_id = $entry->addChild( 'id', $post->guid );
			$entry_updated = $entry->addChild( 'updated', $post->updated->get('c') );
			$entry_edited = $entry->addChild( 'app:edited', $post->modified->get('c'), 'http://www.w3.org/2007/app' );
			$entry_published = $entry->addChild( 'published', $post->pubdate->get('c') );

			foreach ( $post->tags as $tag ) {
				$entry_category = $entry->addChild( 'category' );
				$entry_category->addAttribute( 'term', $tag );
			}

			$entry_content = $entry->addChild( 'content', $content );
			$entry_content->addAttribute( 'type', 'html' );

			Plugins::act( 'atom_get_entry', $xml, $post, $this->handler_vars );
			$xml = $xml->asXML();

			ob_clean();
			header( 'Content-Type: application/atom+xml' );

			print $this->tidy_xml($xml);
		}
	}

	/**
	 * Updates (editing) a post entry that is sent via APP.
	 *
	 * @param string $slug The slug of the entry to save
	 */
	public function put_entry( $slug )
	{
		$params = array();

		$this->is_auth( TRUE );
		$bxml = file_get_contents( 'php://input' );

		$params['slug'] = $slug;
		$params['status'] = 'any';

		if ( $post = Post::get($params) ) {
			$xml = new SimpleXMLElement( $bxml );

			Plugins::act( 'atom_put_entry', $xml, $post, $this->handler_vars );

			if ( (string) $xml->title != '' ) {
				$post->title = $xml->title;
			}

			if ( (string) $xml->id != '' ) {
				$post->guid = $xml->id;
			}

			if ( (string) $xml->content != '' ) {
				$post->content = (string) $xml->content;
			}

			// Save categories as tags
			$atom_ns = $xml->children('http://www.w3.org/2005/Atom');
			$categories = $atom_ns->category;
			if ( !empty($categories) ) {
				$terms = array();
				foreach ( $categories as $category ) {
					$category_attrs = $category->attributes();
					$terms[] = (string) $category_attrs['term'];
				}
				$post->tags = $terms;
			}

			if ( isset( $_SERVER['HTTP_SLUG'] ) ) {
				$post->slug = $_SERVER['HTTP_SLUG'];
			}

			$tags = array();
			if ( isset( $xml->category ) ) {
				foreach ( $xml->category as $cat ) {
					$tags[] = (string)$cat['term'];
				}
				$post->tags = array_merge( $tags, $post->tags );
			}

			// Check if it's a draft (using XPath because Namespaces are easier than with SimpleXML)
			$xml->registerXPathNamespace('app', 'http://www.w3.org/2007/app');
			$draft = $xml->xpath('//app:control/app:draft');
			if ( is_array($draft) && (string) $draft[0] == 'yes' ) {
				$post->status = Post::status('draft');
			}
			else {
				$post->status = Post::status('published');
			}

			$post->user_id = $this->user->id;
			$post->update();
		}
	}

	/**
	 * Delete a post based on the HTTP DELETE request via Atom
	 *
	 * @param string $slug The post slug to delete
	 */
	public function delete_entry( $slug )
	{
		$params = array();

		$this->is_auth(TRUE);

		$params['slug'] = $slug;
		$params['status'] = Post::status('published');
		if ( $post = Post::get($params) ) {
			$post->delete();
		}
	}

	/**
	 * Output a post collection based on the provided parameters.
	 *
	 * @param array $params An array of parameters as passed to Posts::get() to retrieve posts.
	 */
	public function get_collection( $params = array() )
	{
		// Store handler vars since we'll be using them a lot.
		$handler_vars = Controller::get_handler_vars();

		// Retrieve the current matched rule and store its name and argument values.
		$rr = URL::get_matched_rule();
		$rr_name = $rr->name;
		$rr_args = $rr->named_arg_values;

		// Assign alternate links based on the matched rule.
		$alternate_rules = array(
			'atom_feed_tag' => 'display_entries_by_tag',
			'atom_feed' => 'display_home',
			'atom_entry' => 'display_entry',
			'atom_feed_entry_comments' => 'display_entry',
			'atom_feed_page_comments' => 'display_entry',
			'atom_feed_comments' => 'display_home',
			);
		$alternate_rules = Plugins::filter( 'atom_get_collection_alternate_rules', $alternate_rules );
		$alternate = URL::get( $alternate_rules[$rr_name], $handler_vars, false );

		// Assign self link based on the matched rule.
		$self = URL::get( $rr_name, $rr_args, false );

		// Get posts to put in the feed
		$page = ( isset( $rr_args['page'] ) ) ? $rr_args['page'] : 1;
		if ( $page > 1 ) {
			$params['page'] = $page;
		}

		if ( !isset($params['content_type']) ) {
			$params['content_type'] = Post::type('entry');
		}
		$params['content_type'] = Plugins::filter( 'atom_get_collection_content_type', $params['content_type'] );

		$params['status'] = $this->is_auth() ? 'any' : Post::status('published');
		$params['orderby'] = 'updated DESC';
		$params['limit'] = Options::get( 'atom_entries' );

		$params = array_merge( $params, $rr_args );

		if ( array_key_exists( 'tag', $params ) ) {
			$id = urlencode($params['tag']);
			$tags = explode(' ', $params['tag']);
			foreach ( $tags as $tag ) {
				if ( $tag[0] == '-' ) {
					$tag = substr($tag, 1);
					$params['not:tag_slug'][] = Utils::slugify($tag);
				}
				else {
					$params['all:tag_slug'][] = Utils::slugify($tag);
				}
			}
			unset( $params['tag'] );
		}
		else {
			$id = 'atom';
		}
		$posts = Posts::get( $params );

		if ( count( $posts ) ) {
			$updated = $posts[0]->updated;
		}
		else {
			$updated = null;
		}

		$xml = $this->create_atom_wrapper( $alternate, $self, $id, $updated );

		if ( $this->is_auth() ) {
			$xml = $this->add_pagination_links( $xml, Posts::count_total() );
		}
		else {
			$xml = $this->add_pagination_links( $xml, Posts::count_total( Post::status('published') ) );
		}

		$xml = $this->add_posts($xml, $posts );

		Plugins::act( 'atom_get_collection', $xml, $params, $handler_vars );
		$xml = $xml->asXML();

		ob_clean();
		header( 'Content-Type: application/atom+xml' );

		print $this->tidy_xml($xml);
	}

	/**
	 * Use Tidy to tidy XML output for debugging by humans
	 *
	 * @param string $xml The unformatted input XML
	 * @return string Formatted XML
	 */
	protected function tidy_xml($xml)
	{
		if ( !DEBUG ) {
			return $xml;
		}
		// Tidy configuration -- make xml readable
		if ( class_exists('tidy', false) ) {
			$config = array(
				'indent' => true,
				'output-xml' => true,
				'input-xml' => true,
				'wrap' => 200
			);

			$tidy = new tidy();
			$tidy->parseString($xml, $config, 'utf8');
			$tidy->cleanRepair();
			$xml = $tidy;
		}

		return $xml;
	}

	/**
	 * Accepts an Atom entry for insertion as a new post.
	 */
	public function post_collection()
	{
		if ( $this->is_auth( TRUE ) ) {
			$bxml = file_get_contents( 'php://input' );
		}

		$xml = new SimpleXMLElement( $bxml );

		$post = new Post();
		Plugins::act( 'atom_post_collection', $xml, $post, $this->handler_vars );

		if ( (string) $xml->title != '' ) {
			$post->title = $xml->title;
		}

		if ( (string) $xml->id != '' ) {
			$post->guid = $xml->id;
		}

		if ( (string) $xml->content != '' ) {
			$post->content = (string) $xml->content;
		}

		if ( (string) $xml->pubdate != '' ) {
			$post->pubdate = (string) $xml->pubdate;
		}

		// Save categories as tags
		$atom_ns = $xml->children('http://www.w3.org/2005/Atom');
		$categories = $atom_ns->category;
		if ( !empty($categories) ) {
			$terms = array();
			foreach ( $categories as $category ) {
				$category_attrs = $category->attributes();
				$terms[] = (string) $category_attrs['term'];
			}
			$post->tags = $terms;
		}

		if ( isset( $_SERVER['HTTP_SLUG'] ) ) {
			$post->slug = $_SERVER['HTTP_SLUG'];
		}

		// Check if it's a draft (using XPath because Namespaces are easier than with SimpleXML)
		$xml->registerXPathNamespace('app', 'http://www.w3.org/2007/app');
		$draft = $xml->xpath('//app:control/app:draft');
		if ( is_array($draft) && (string) $draft[0] == 'yes' ) {
			$post->status = Post::status('draft');
		}
		else {
			$post->status = Post::status('published');
		}

		$post->user_id = $this->user->id;
		$post->insert();

		header('HTTP/1.1 201 Created', true, 201);
		header('Status: 201 Created');
		header('Location: ' . URL::get( 'atom_entry', array( 'slug' => $post->slug ) ) );

		$this->get_entry( $post->slug );
	}

}
?>


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

/**
 * Class to wrap around bitmap field functionality
 */
class Bitmask
{
	protected $flags = array();  // set of flag bit masks
	protected $full = 0;         // maximum integer value of the bitmask
	protected $value = 0;        // internal integer value of the bitmask

	/**
	 * Constructor.  Takes an optional array parameter
	 * of bit flags to mask on.
	 *
	 * @param array $flags An array of flag names
	 * @param integer $value (optional) a combined bitmask value
	 */
	public function __construct( $flags = null, $value = null )
	{
		if ( ! is_array( $flags ) ) {
			throw new InvalidArgumentException( _t( 'Bitmask constructor expects either no arguments or an array as a first argument' ) );
		}

		if ( count( $flags ) > ( PHP_INT_MAX >> 1 ) ) {
			throw new InvalidArgumentException( _t( 'Bitmask can have max PHP_INT_MAX >> 1 flags' ) );
		}

		$this->flags = $flags;
		$this->full = ( 1 << ( count( $this->flags ) ) ) - 1;
		if ( ! is_null( $value ) ) {
			if ( $value === 'full' ) {
				$this->value = $this->full;
			}
			elseif ( (string) (int) $value === (string) $value && $value >= 0 && $value <= $this->full ) {
				$this->value = $value;
			}
			elseif ( is_string( $value ) && in_array( $value, $flags ) ) {
				// This calls the setter directly to deal with non-public
				// properties to make sure we have the same behaviour as the
				// normal API.
				$this->__set($value, true);
			}
			else {
				throw new InvalidArgumentException( _t( 'Bitmask constructor second argument must either be an integer within the valid range, the name of a flag, or full' ) );
			}
		}

	}

	/**
	 * Magic setter method for flag values.
	 *
	 * @param bit   integer representing the mask bit
	 * @param on    on or off?
	 */
	public function __set( $bit, $on )
	{
		switch( $bit ) {
			case 'full':
				if ( ! is_bool( $on ) ) {
					throw new InvalidArgumentException( _t( 'Bitmask full toggle must be boolean' ) );
				}

				if ( $on ) {
					$this->value = $this->full;
				}
				else {
					$this->value = 0;
				}
				break;
			case 'value':
				if ( is_array( $on ) ) {
					if ( count( $on ) !== count( $this->flags ) ) {
						throw new InvalidArgumentException( _t( 'Setting bitmask value by array must use array with same length as number of flags' ) );
					}
					$this->value = 0;
					foreach ( $on as $flag ) {
						if ( ! is_bool( $flag ) ) {
							throw new InvalidArgumentException( _t( 'Bitmask values must be boolean' ) );
						}
						$this->value <<= 1;
						$this->value |= (int) $flag;
					}
				}
				elseif ( (string) (int) $on === (string) $on && $on >= 0 && $on <= $this->full ) {
					$this->value = $on;
				}
				elseif ( empty($on) ) {
					$this->value = 0;
				}
				else {
					throw new InvalidArgumentException( _t( 'Bitmask value must either be an integer within the valid range or an array of booleans' ) );
				}
				break;
			default:
				if ( ! is_bool( $on ) ) {
					throw new InvalidArgumentException( _t( 'Bitmask values must be boolean' ) );
				}

				$bit = array_search( $bit, $this->flags );

				if ( $bit === false ) {
					throw new InvalidArgumentException( _t( 'Bitmask cannot set non-existent flag' ) );
				}

				if ( $on ) {
					$this->value |= 1 << $bit;
				}
				else {
					$this->value &= ~( 1 << $bit );
				}
				break;
			}
		return $on;
	}

	/**
	 * Magic getter method for flag status
	 *
	 * @param bit integer representing the mask bit to test
	 * @return boolean
	 */
	public function __get( $bit )
	{
		if ( $bit === 'value' ) {
			return $this->value;
		}
		elseif ( $bit === 'full' ) {
			return $this->full;
		}
		elseif ( ( $bit = array_search( $bit, $this->flags ) ) !== false ) {
			return ( $this->value & ( 1 << $bit ) ) !== 0;
		}
		else {
			throw new InvalidArgumentException( _t( 'Bitmask cannot get non-existent flag' ) );
		}
	}

	/**
	 * Magic check-whether-flag-exists method
	 *
	 * @param flag string of flag name
	 * @return boolean
	 */
	public function __isset( $flag )
	{
		return $flag === 'full' || $flag === 'value' || in_array( $flag, $this->flags );
	}

	public function __tostring()
	{
		if ( $this->value === $this->full ) {
			return _t( 'full' );
		}
		$output = array();
		foreach ( $this->flags as $flag ) {
			if ( $this->$flag ) {
				$output[] = $flag;
			}
		}
		if ( count( $output ) === 0 ) {
			return _t( 'none' );
		}
		return implode( ',', $output );
	}

}
?>


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

/**
 * Habari Block class
 * Block class for theme display of pluggable content
 *
 */
class Block extends QueryRecord implements IsContent, FormStorage
{
	private $data_values = array();

	/**
	 * Constructor for the Block class.
	 * @param array $paramarray an associative array of initial block field values.
	 */
	public function __construct( $paramarray = array() )
	{
		// Defaults
		$this->fields = array_merge(
			self::default_fields(),
			$this->fields,
			$this->newfields
		);
		parent::__construct( $paramarray );

		$this->exclude_fields( 'id' );
		$this->unserialize_data();
	}

	/**
	 * 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)
	{
		if ( array_key_exists($name, $this->data_values) ) {
			return $this->data_values[$name];
		}
		else {
			return parent::__get($name);
		}

	}

	/**
	 * Overrides QueryRecord __set to implement custom object properties
	 *
	 * @param string Name of property to return
	 * @return mixed The requested field value
	 */
	public function __set($name, $value)
	{
		switch ( $name ) {
			case 'id':
			case 'title':
			case 'data':
				parent::__set( $name, $value );
				$this->unserialize_data();
				return parent::__get( $name );
				break;
			case 'type':
				return parent::__set( $name, $value );
				break;
			default:
				$this->data_values[ $name ] = $value;
				return $this->data_values[ $name ];
				break;
		}
	}

	/**
	 * Overrides QueryRecord __isset, returns whether this Block has the named
	 * data. Falls back to QueryRecord's __isset.
	 *
	 * @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->data_values[$name]) || parent::__isset($name) );
	}

	/**
	 * Return the defined database columns for a Block.
	 * @return array Array of columns in the Block table
	 */
	public static function default_fields()

	{

		return array(
			'id' => 0,
			'title' => '',
			'data' => '',
			'type' => '',
		);
	}

	/**
	 * Render and return the block content
	 *
	 * @param Theme $theme the theme object with which the block will be rendered
	 * @return string The rendered block content
	 */
	public function fetch($theme)
	{
		Plugins::act('block_content_' . $block->type, $block);
		$output = implode( '', $theme->content_return($block));
		return $output;
	}

	/**
	 * Return the content types that this object represents
	 *
	 * @see IsContent
	 * @return array An array of strings representing the content type of this object
	 */
	public function content_type()
	{
		$types = array(
			'block.' . $this->type,
			'block',
		);
		if(isset($this->title)) {
			array_unshift($types, 'block.' . $this->type . '.' . Utils::slugify($this->title));
		}
		$types = Plugins::filter('block_content_type_' . $this->type, $types, $this);
		return $types;
	}

	/**
	 * Unserialize the stored block data
	 */
	public function unserialize_data()
	{
		if ( trim($this->data) != '' ) {
			$this->data_values = unserialize($this->data);
		}
	}

	/**
	 * Saves form fields that are tied to this block.  Implements FormStorage.
	 *
	 * @param string $key The name of the form field to store.
	 * @param mixed $value The value of the form field
	 */
	public function field_save($key, $value)
	{
		$this->$key = $value;
		$this->update();
	}

	/**
	 * Load the form value from the block
	 *
	 * @param string $key The name of the form field to load
	 * @return mixed The value of the block for the form
	 */
	public function field_load($key)
	{
		return $this->$key;
	}

	/**
	 * Insert this block into the database
	 *
	 * @return boolean True on success
	 */
	public function insert()
	{
		// Let plugins disallow and act before we write to the database
		$allow = true;
		$allow = Plugins::filter( 'block_insert_allow', $allow, $this );
		if ( ! $allow ) {
			return;
		}
		Plugins::act( 'block_insert_before', $this );

		$this->data = serialize($this->data_values);
		$result = parent::insertRecord( DB::table( 'blocks' ) );

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

		// Update the block's fields with anything that changed
		$this->fields = array_merge( $this->fields, $this->newfields );

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

		EventLog::log( _t( 'New block %1$s: %2$s', array( $this->id, $this->title ) ), 'info', 'content', 'habari' );

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

	/**
	 * Update this block in the database
	 *
	 * @return boolean True on success
	 */
	public function update()
	{
		$allow = true;
		$allow = Plugins::filter( 'block_update_allow', $allow, $this );
		if ( ! $allow ) {
			return;
		}
		Plugins::act( 'block_update_before', $this );

		$this->data = serialize($this->data_values);
		$result = parent::updateRecord( DB::table( 'blocks' ), array( 'id' => $this->id ) );

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

		Plugins::act( 'block_update_after', $this );
		return $result;
	}

	/**
	 * Delete this block
	 *
	 */
	public function delete()
	{
		// Let plugins disallow and act before we write to the database
		$allow = true;
		$allow = Plugins::filter( 'block_delete_allow', $allow, $this );
		if ( !$allow ) {
			return false;
		}
		Plugins::act( 'block_delete_before', $this );

		$result = parent::deleteRecord( '{blocks}', array( 'id'=>$this->id ) );

		EventLog::log( sprintf(_t('Block %1$s (%2$s) deleted.'), $this->id, $this->title), 'info', 'content', 'habari' );

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

	/**
	 * Get the form used to update this block
	 *
	 * @return FormUI The altered FormUI element that edits this block
	 */
	public function get_form()
	{
		$form = new FormUI('block-' . $this->id , 'block');
		$form->on_success(array($this, 'save_block'));
		$form->set_option('success_message', '

' . _t('Saved block configuration.') . '

');
		Plugins::act('block_form_' . $this->type, $form, $this);
		return $form;
	}

	/**
	 * Display a standard success message upon saving the form
	 *
	 * @return
	 */
	public function save_block($form)
	{
		$form->save();
		return false;
	}

}

?>


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

/**
 * Habari Cache Class
 *
 * Base abstract class for caching computationally expensive or bandwidth intensive data
 */
abstract class Cache
{
	protected static $default_group = 'default';
	protected static $instance;

	/**
	 * Set up the static cache instance in __autoload()
	 */
	public static function __static()
	{
		if( !defined( 'CACHE_CLASS' ) ) {
			define( 'CACHE_CLASS', 'FileCache' );
		}
		$cache_class = CACHE_CLASS;
		self::$instance = new $cache_class();
	}

	/**
	 * Is record with $name in the cache?
	 *
 	 * @param mixed $name name of the cached item or an array of array( string $group, string $name )
	 * @return boolean TRUE if item is cached, FALSE if not
	 */
	public static function has( $name )
	{
		if ( is_array( $name ) ) {
			$array = $name;
			list( $group, $name ) = $array;
		}
		else {
			$group = self::$default_group;
		}
		return self::$instance->_has( $name, $group );
	}

	/**
	 * A cache instance implements this function to return whether a named cache exists.
	 *
	 * @param string $name The name of the cached item
	 * @return boolean TRUE if the item is cached, FALSE if not
	 */
	abstract protected function _has( $name, $group );

	/**
	 * Is group in the cache?
	 *
 	 * @param string $group name of the cached group
	 * @return boolean TRUE if group is cached, FALSE if not
	 */
	public static function has_group( $group )
	{
		return self::$instance->_has_group( $group );
	}

	/**
	 * A cache instance implements this function to return whether a group exists.
	 *
	 * @param string $name The name of the cached group
	 * @return boolean TRUE if the group is cached, FALSE if not
	 */
	abstract protected function _has_group( $group );

	/**
	 * Returns the named value from the cache.
	 *
	 * @param mixed $name The name of the cached item or an array of array( string $group, string $name )
	 * @return mixed The item value or NULL if it doesn't exist in cache
	 */
	public static function get( $name )
	{
		if ( is_array( $name ) ) {
			$array = $name;
			list( $group, $name ) = $array;
		}
		else {
			$group = self::$default_group;
		}
		return self::$instance->_get( $name, $group );
	}

	/**
	 * A cache instance implements this to return the named value from the cache.
	 *
	 * @param string $name The name of the cached item
	 * @return mixed The item value or NULL if it doesn't exist in cache
	 */
	abstract protected function _get( $name, $group );

	/**
	 * Returns the group from the cache.
	 *
	 * @param string $name The name of the cached group
	 * @return mixed The item value or NULL if it doesn't exist in cache
	 */
	public static function get_group( $group )
	{
		return self::$instance->_get_group( $group );
	}

	/**
	 * A cache instance implements this to return the group from the cache.
	 *
	 * @param string $name The name of the cached group
	 * @return mixed The item value or NULL if it doesn't exist in cache
	 */
	abstract protected function _get_group( $group );

	/**
	 * Set the named value in the cache with an expiration.
	 *
	 * @param mixed $name The name of the cached item or an array of array( string $group, string $name )
	 * @param mixed $value The value to store
	 * @param integer $expiry Number of second after the call that the cache will expire
	 * @param boolean $keep If true, retain the cache value even after expiry but report the cache as expired
	 */
	public static function set( $name, $value, $expiry = 3600, $keep = false )
	{
		if ( is_array( $name ) ) {
			$array = $name;
			list( $group, $name ) = $array;
		}
		else {
			$group = self::$default_group;
		}
		self::$instance->_set( $name, $value, $expiry, $group, $keep );
	}

	/**
	 * A cache instance implements this to set the named value in the cache with an expiration.
	 *
	 * @param string $name The name of the cached item
	 * @param mixed $value The value to store
	 * @param integer $expiry Number of second after the call that the cache will expire
	 */
	abstract protected function _set( $name, $value, $expiry, $group, $keep );

	/**
	 * Expires the named value from the cache.
	 *
	 * @param mixed $name The name of the cached item or an array of array( string $group, string $name )
	 * @param string $match_mode (optional) how to match item names ('strict', 'regex', 'glob') (default 'strict')
	 */
	public static function expire( $name, $match_mode = 'strict' )
	{
		if ( is_array( $name ) ) {
			$array = $name;
			list( $group, $name ) = $array;
		}
		else {
			$group = self::$default_group;
		}
		self::$instance->_expire( $name, $group, $match_mode );
	}

	/**
	 * A cache instance implements this to expire the named value from the cache.
	 *
	 * @param string $name The name of the cached item
	 */
	abstract protected function _expire( $name, $group );

	/**
	 * Check if a named value in the cache has expired.
	 *
	 * @param mixed $name The name of the cached item or an array of array( string $group, string $name )
	 */
	public static function expired( $name )
	{
		if ( is_array( $name ) ) {
			$array = $name;
			list( $group, $name ) = $array;
		}
		else {
			$group = self::$default_group;
		}
		return self::$instance->_expired( $name, $group );
	}

	/**
	 * A cache instance implements this to expire the named value from the cache.
	 *
	 * @param string $name The name of the cached item
	 */
	abstract protected function _expired( $name, $group );

	/**
	 * Extend the expiration of the named cached value.
	 *
	 * @param mixed $name The name of the cached item or an array of array( string $group, string $name )
	 * @param integer $expiry The duration in seconds to extend the cache expiration by
	 */
	public static function extend( $name, $expiry )
	{
		if ( is_array( $name ) ) {
			$array = $name;
			list( $group, $name ) = $array;
		}
		else {
			$group = self::$default_group;
		}
		self::$instance->_extend( $name, $expiry, $group );
	}

	/**
	 * A cache instance implements this to extend the expiration of the named cached value.
	 *
	 * @param string $name The name of the cached item
	 * @param integer $expiry The duration in seconds to extend the cache expiration by
	 */
	abstract protected function _extend( $name, $expiry, $group );

	public static function debug() { return self::$instance->debug_data(); }

	/**
	 * Empty the cache completely
	 *
	 */
	public static function purge()
	{
		return self::$instance->_purge();
	}
}

?>


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

/**
 * Various color utility functions.
 */
class ColorUtils
{
	/**
	 * Convert RGB args to RGB array.
	 */
	public static function rgb_rgbarr( $r, $g, $b )
	{
		return array_map( 'round', array( 'r' => $r, 'g' => $g, 'b' => $b ) );
	}

	/**
	 * Convert HSV args to HSV array.
	 */
	public static function hsv_hsvarr( $h, $s, $v )
	{
		return array_map( 'round', array( 'h' => $h, 's' => $s, 'v' => $v ) );
	}

	/**
	 * Converts a HTML style hex string ('#7f6699') to an RGB array.
	 */
	public static function hex_rgb( $hex_string )
	{
		$hex_string = ltrim( $hex_string, '#' );
		if ( ! preg_match('/^[0-9a-f]+$/i', $hex_string ) ) {
			return Error::raise( _t('Not a valid hex color.') );
		}

		$normalized = '';

		switch ( strlen( $hex_string ) ) {
			case 3:
				// 'fed' = 'ffeedd'
				for ( $i = 0; $i < 3; $i++ ) {
					$normalized.= $hex_string{$i} . $hex_string{$i};
				}
				break;
			case 6:
				// already normal
				$normalized = $hex_string;
				break;
			case 2:
			case 4:
				// who uses this anyway!
				$normalized = $hex_string . str_repeat( '0', 6 - strlen( $hex_string ) );
				break;
			default:
				return Error::raise( _t('Not a valid color format.') );
		}

		return self::rgb_rgbarr(
			hexdec( substr( $normalized, 0, 2 ) ),
			hexdec( substr( $normalized, 2, 2 ) ),
			hexdec( substr( $normalized, 4, 2 ) )
		);
	}

	/**
	 * Convert an RGB array to a HTML style hex color.
	 */
	public static function rgb_hex( $rgb_arr )
	{
		$hex_string = sprintf( '%02x%02x%02x', $rgb_arr['r'], $rgb_arr['g'], $rgb_arr['b'] );

		return $hex_string;
	}

	/**
	 * Convert an RGB array to a HSV array.
	 */
	public static function rgb_hsv( $rgb_arr )
	{
		$min = min( $rgb_arr );
		$max = max( $rgb_arr );

		$d = $max - $min;

		if ( $max == 0 ) {
			// black
			// we use 0/0/0, even though at black, H and V are undefined
			$H = 0;
			$S = 0;
			$V = 0;
		}
		else {
			if ( $max == $rgb_arr['r'] ) {
				// reddish (YM)
				$H = ( $rgb_arr['g'] - $rgb_arr['b'] ) / $d;
			}
			elseif ( $max == $rgb_arr['g'] ) {
				// greenish (CY)
				$H = 2 + ( $rgb_arr['b'] - $rgb_arr['r'] ) / $d;
			}
			elseif ( $max == $rgb_arr['b'] ) {
				// bluish (MC)
				$H = 4 + ( $rgb_arr['r'] - $rgb_arr['g'] ) / $d;
			}
			else {
				Error::raise( _t('Something went terribly wrong here.') );
			}

			$H*= 60; // convert to deg
			$H = ( $H + 360 ) % 360; // map to 0..359

			$S = 100 * $d / $max;
			$V = $max;
		}

		return self::hsv_hsvarr( $H, $S, $V );
	}

	/**
	 * Convert a HSV array to RGB.
	 */
	public static function hsv_rgb( $hsv_arr )
	{
		if ( $hsv_arr['s'] == 0 ) {
			// grey
			$R = $G = $B = $hsv_arr['v'];
		}
		else {
			$H = $hsv_arr['h'] / 60; // degrees to sectors
			$S = $hsv_arr['s'] / 100; // percent to fraction
			$f = $H - floor( $H );

			$p = $hsv_arr['v'] * ( 1 - $S );
			$q = $hsv_arr['v'] * ( 1 - $S * $f );
			$t = $hsv_arr['v'] * ( 1 - $S * ( 1 - $f ) );

			switch ( floor( $H ) ) {
				case 0: // first sector
					$R = $hsv_arr['v'];
					$G = $t;
					$B = $p;
					break;
				case 1: // second sector
					$R = $q;
					$G = $hsv_arr['v'];
					$B = $p;
					break;
				case 2: // third sector
					$R = $p;
					$G = $hsv_arr['v'];
					$B = $t;
					break;
				case 3: // fourth sector
					$R = $p;
					$G = $q;
					$B = $hsv_arr['v'];
					break;
				case 4: // fifth sector
					$R = $t;
					$G = $p;
					$B = $hsv_arr['v'];
					break;
				case 5: // sixth sector
					$R = $hsv_arr['v'];
					$G = $p;
					$B = $q;
					break;
			}
		}

		return self::rgb_rgbarr( $R, $G, $B );
	}
}

?>


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

/**
 * Habari CommentRecord Class
 *
 * Includes an instance of the CommentInfo class; for holding inforecords about the comment
 * If the Comment object describes an existing user; use the internal info object to get, set, unset and test for existence (isset) of
 * info records
 *
 * $this->info = new CommentInfo ( 1 );  // Info records of comment with id = 1
 * $this->info->browser_ua= "Netscape 2.0"; // set info record with name "browser_ua" to value "Netscape 2.0"
 * $info_value= $this->info->browser_ua; // get value of info record with name "browser_ua" into variable $info_value
 * if ( isset ($this->info->browser_ua) )  // test for existence of "browser_ua"
 * unset ( $this->info->browser_ua ); // delete "browser_ua" info record
 *
 *
 */
class Comment extends QueryRecord implements IsContent
{

	// our definitions for comment types and statuses
	const STATUS_UNAPPROVED = 0;
	const STATUS_APPROVED = 1;
	const STATUS_SPAM = 2;
	const STATUS_DELETED = 3;

	const COMMENT = 0;
	const PINGBACK = 1;
	const TRACKBACK = 2;

	private $post_object = null;

	private $inforecords = null;

	// static variables to hold comment status and comment type values
	static $comment_status_list = array();
	static $comment_type_list = array();
	static $comment_status_actions = array();

	/**
	 * static function default_fields
	 * Returns the defined database columns for a comment
	 */
	public static function default_fields()
	{
		return array(
			'id' => 0,
			'post_id' => 0,
			'name' => '',
			'email' => '',
			'url' => '',
			'ip' => 0,
			'content' => '',
			'status' => self::STATUS_UNAPPROVED,
			'date' => HabariDateTime::date_create(),
			'type' => self::COMMENT
		);
	}

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

	}

	/**
	 * static function get
	 * Returns a single comment, by ID
	 *
	 *
	 * $post = Post::get( 10 );
	 *
	 *
	 * @param int An ID
	 * @return array A single Comment object
	 */
	static function get( $ID = 0 )
	{
		if ( ! $ID ) {
			return false;
		}
		return DB::get_row( 'SELECT * FROM {comments} WHERE id = ?', array( $ID ), 'Comment' );
	}

	/**
	 * static function create
	 * Creates a comment and saves it
	 * @param array An associative array of comment fields
	 * $return Comment The comment object that was created
	 */
	static function create($paramarray)
	{
		$comment = new Comment($paramarray);
		$comment->insert();
		return $comment;
	}

	/**
	 * function insert
	 * Saves a new comment to the posts table
	 */
	public function insert()
	{
		$allow = true;
		$allow = Plugins::filter('comment_insert_allow', $allow, $this);
		if ( ! $allow ) {
			return;
		}
		Plugins::act('comment_insert_before', $this);
		// Invoke plugins for all fields, since they're all "chnaged" when inserted
		foreach ( $this->fields as $fieldname => $value ) {
			Plugins::act('comment_update_' . $fieldname, $this, $this->$fieldname, $value );
		}
		$result = parent::insertRecord( DB::table('comments') );
		$this->newfields['id'] = DB::last_insert_id(); // Make sure the id is set in the comment object to match the row id
		$this->fields = array_merge($this->fields, $this->newfields);
		$this->newfields = array();
		$this->info->commit( $this->fields['id'] );
		Plugins::act('comment_insert_after', $this);
		return $result;
	}

	/**
	 * function update
	 * Updates an existing comment in the posts table
	 */
	public function update()
	{
		$allow = true;
		$allow = Plugins::filter('comment_update_allow', $allow, $this);
		if ( ! $allow ) {
			return;
		}
		Plugins::act('comment_update_before', $this);
		// invoke plugins for all fields which have been updated
		foreach ( $this->newfields as $fieldname => $value ) {
			Plugins::act('comment_update_' . $fieldname, $this, $this->fields[$fieldname], $value);
		}
		$result = parent::updateRecord( DB::table('comments'), array('id'=>$this->id) );
		$this->fields = array_merge($this->fields, $this->newfields);
		$this->newfields = array();
		$this->info->commit();
		Plugins::act('comment_update_after', $this);
		return $result;
	}

	/**
	 * function delete
	 * Deletes this comment
	 */
	public function delete()
	{
		$allow = true;
		$allow = Plugins::filter('comment_delete_allow', $allow, $this);
		if ( ! $allow ) {
			return;
		}
		Plugins::act('comment_delete_before', $this);
		// Delete all info records associated with this comment
		if ( isset( $this->info ) ) {
			$this->info->delete_all();
		}
		$result = parent::deleteRecord( DB::table('comments'), array('id'=>$this->id) );
		Plugins::act('comment_delete_after', $this);
		return $result;
	}

	/**
	 * 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 )
	{
		$fieldnames = array_merge( array_keys( $this->fields ), array('post', 'info', 'editlink' ) );
		if ( !in_array( $name, $fieldnames ) && strpos( $name, '_' ) !== false ) {
			preg_match('/^(.*)_([^_]+)$/', $name, $matches);
			list( $junk, $name, $filter ) = $matches;
		}
		else {
			$filter = false;
		}

		if ( $name == 'name' && parent::__get( $name ) == '' ) {
			return _t('Anonymous');
		}
		switch ( $name ) {
			case 'post':
				$out = $this->get_post();
				break;
			case 'info':
				$out = $this->get_info();
				break;
			case 'statusname':
				$out = self::status_name( $this->status );
				break;
			case 'typename':
				$out = self::type_name( $this->type );
				break;
			case 'editlink':
				$out = $this->get_editlink();
				break;
			default:
				$out = parent::__get( $name );
				break;
		}
		//$out = parent::__get( $name );
		$out = Plugins::filter( "comment_{$name}", $out, $this );
		if ( $filter ) {
			$out = Plugins::filter( "comment_{$name}_{$filter}", $out, $this );
		}
		return $out;
	}

	/**
	 * function __set
	 * Overrides QueryRecord __set to implement custom object properties
	 * @param string Name of property to return
	 * @return mixed The requested field value
	 */
	public function __set( $name, $value )
	{
		switch ( $name ) {
			case 'status':
				return $this->setstatus($value);
			case 'date':
				if ( !($value instanceOf HabariDateTime) ) {
					$value = HabariDateTime::date_create($value);
				}
				break;
			case 'post':
				if ( is_int( $value ) ) {
					// a post ID was passed
					$p = Post::get(array('id'=>$value));
					$this->post_id = $p->id;
					$this->post_object = $p;
				}
				elseif ( is_string( $value ) ) {
					// a post Slug was passed
					$p = Post::get(array('slug'=>$value));
					$this->post_id = $p->id;
					$this->post_object = $p;
				}
				elseif ( is_object ( $value ) ) {
					// a Post object was passed, so just use it directly
					$this->post_id = $p->id;
					$this->post_object = $value;
				}
				return $value;
		}
		return parent::__set( $name, $value );
	}

	/**
	 * private function get_post()
	 * returns a Post object for the post of this comment
	 * @param bool Whether to use the cached version or not.  Default to true
	 * @return Post a Post object for the post of the current comment
	 */
	private function get_post( $use_cache = TRUE )
	{
		if ( ! isset( $this->post_object ) || ( ! $use_cache)  ) {
			$this->post_object = Posts::get( array('id' => $this->post_id, 'fetch_fn' => 'get_row') );
		}
		return $this->post_object;
	}

	/**
	 * function get_info
	 * Gets the info object for this comment, which contains data from the commentinfo table
	 * related to this comment.
	 * @return CommentInfo object
	 */
	private function get_info()
	{
		if ( ! $this->inforecords ) {
			if ( 0 == $this->id ) {
				$this->inforecords = new CommentInfo();
			}
			else {
				$this->inforecords = new CommentInfo( $this->id );
			}
		}
		return $this->inforecords;
	}

 	/**
 	 * function setstatus
 	 * @param mixed the status to set it to. String or integer.
 	 * @return integer the status of the comment
 	 * Sets the status for a comment, given a string or integer.
 	 */
 	private function setstatus($value)
 	{
 		if ( is_numeric( $value ) ) {
 			$this->newfields['status'] = $value;
		}
 		else {
 			switch ( strtolower($value) ) {
 				case "approved":
 				case "approve":
 				case "ham":
 					$this->newfields['status'] = self::STATUS_APPROVED;
 					break;
 				case "unapproved":
 				case "unapprove":
 					$this->newfields['status'] = self::STATUS_UNAPPROVED;
 					break;
 				case "spam":
 					$this->newfields['status'] = self::STATUS_SPAM;
 					break;
 				case "deleted":
 					$this->newfields['status'] = self::STATUS_DELETED;
 					break;
 			}
 		}
 		return $this->newfields['status'];
 	}

	/**
	 * returns an associative array of comment types
	 * @param bool whether to force a refresh of the cached values
	 * @return array An array of comment type names => integer values
	 */
	public static function list_comment_types( $refresh = false )
	{
		if ( ( ! $refresh ) && ( ! empty( self::$comment_type_list ) ) ) {
			return self::$comment_type_list;
		}
		self::$comment_type_list = array(
			self::COMMENT => 'comment',
			self::PINGBACK => 'pingback',
			self::TRACKBACK => 'trackback',
		);
		return self::$comment_type_list;
	}

	/**
	 * returns an associative array of comment statuses
	 * @param bool whether to force a refresh of the cached values
	 * @return array An array of comment statuses names => interger values
	 */
	public static function list_comment_statuses( $refresh = false )
	{
		if ( ( ! $refresh ) && ( ! empty( self::$comment_status_list ) ) ) {
			return self::$comment_status_list;
		}
		self::$comment_status_list = array(
			self::STATUS_UNAPPROVED => 'unapproved',
			self::STATUS_APPROVED => 'approved',
			self::STATUS_SPAM => 'spam',
			// 'STATUS_DELETED' => self::STATUS_DELETED, // Not supported
		);
		self::$comment_status_list = Plugins::filter('list_comment_statuses', self::$comment_status_list);
		return self::$comment_status_list;
	}

	/**
	 * returns the action name of the comment status
	 * @param mixed a comment status value, or name
	 * @return string a string of the status action, or null
	 */
	public static function status_action( $status )
	{
		if ( empty( self::$comment_status_actions ) ) {
			self::$comment_status_actions = array(
				self::STATUS_UNAPPROVED => _t('Unapprove'),
				self::STATUS_APPROVED => _t('Approve'),
				self::STATUS_SPAM => _t('Spam'),
			);
			self::$comment_status_actions = Plugins::filter('list_comment_actions', self::$comment_status_actions);
		}
		if ( is_numeric( $status ) && isset( self::$comment_status_actions[$status] ) ) {
			return self::$comment_status_actions[$status];
		}
		$statuses = array_flip( Comment::list_comment_statuses() );
		if ( isset($statuses[$status]) ) {
			return self::$comment_status_actions[$statuses[$status]];
		}

		return '';
	}

	/**
	 * returns the integer value of the specified comment status, or false
	 * @param mixed a comment status name or value
	 * @return mixed an integer or boolean false
	 */
	public static function status( $name )
	{
		$statuses = Comment::list_comment_statuses();
		if ( is_numeric( $name ) && ( isset( $statuses[$name] ) ) ) {
			return $name;
		}
		$statuses = array_flip( $statuses );
		if ( isset($statuses[$name]) ) {
			return $statuses[$name];
		}
		return false;
	}

	/**
	 * returns the friendly name of a comment status, or null
	 * @param mixed a comment status value, or name
	 * @return mixed a string of the status name, or null
	 */
	public static function status_name( $status )
	{
		$statuses = Comment::list_comment_statuses();
		if ( is_numeric( $status ) && isset( $statuses[$status] ) ) {
			return $statuses[$status];
		}
		$statuses = array_flip( $statuses );
		if ( isset( $statuses[$status] ) ) {
			return $status;
		}
		return '';
	}

	/**
	 * returns the integer value of the specified comment type, or false
	 * @param mixed a comment type name or number
	 * @return mixed an integer or boolean false
	 */
	public static function type( $name )
	{
		$types = Comment::list_comment_types();
		if ( is_numeric( $name ) && ( isset( $types[$name] ) ) ) {
			return $name;
		}
		$types = array_flip($types);
		if ( isset( $types[$name] ) ) {
			return $types[$name];
		}
		return false;
	}

	/**
	 * returns the friendly name of a comment type, or null
	 * @param mixed a comment type number, or name
	 * @return mixed a string of the comment type, or null
	 */
	public static function type_name( $type )
	{
		$types = Comment::list_comment_types();
		if ( is_numeric( $type ) && isset( $types[$type] ) ) {
			return $types[$type];
		}
		$types = array_flip($types);
		if ( isset($types[$type]) ) {
			return $type;
		}
		return '';
	}

	/**
	 * Return the content type of this object
	 *
	 * @return string The content type of this object
	 * @see IsContent
	 */
	public function content_type()
	{
		return Comment::type_name($this->type);
	}

	/**
	 * Returns an access Bitmask for the given user on this comment. Read access is determined
	 * by the associated post. Update/delete is determined by the comment management tokens.
	 * @param User $user The user mask to fetch
	 * @return Bitmask
	 */
	public function get_access( $user = null )
	{
		if ( ! $user instanceof User ) {
			$user = User::identify();
		}

		// these tokens automatically grant full access to the comment
		if ( $user->can( 'super_user' ) || $user->can( 'manage_all_comments' ) ||
			( $user->id == $this->post->user_id && $user->can( 'manage_own_post_comments' ) ) ) {
			return ACL::get_bitmask( 'full' );
		}

		/* If we got this far, we can't update or delete a comment. We still need to check if we have
		 * read access to it. Collect a list of applicable tokens
		 */
		$tokens = array(
			'post_any',
			'post_' . Post::type_name( $this->post->content_type ),
		);

		if ( $user->id == $this->post->user_id ) {
			$tokens[] = 'own_posts';
		}

		$tokens = array_merge( $tokens, $this->post->get_tokens() );

		// grab the access masks on these tokens
		foreach ( $tokens as $token ) {
			$access = ACL::get_user_token_access( $user, $token );
			if ( $access instanceof Bitmask ) {
				$token_accesses[] = ACL::get_user_token_access( $user, $token )->value;
			}
		}

		// now that we have all the accesses, loop through them to build the access to the particular post
		if ( in_array( 0, $token_accesses ) ) {
			return ACL::get_bitmask( 0 );
		}

		if ( ACL::get_bitmask( Utils::array_or( $token_accesses ) )->read ) {
			return ACL::get_bitmask( 'read' );
		}

		// if we haven't returned by this point, we can neither manage the comment nor read it
		return ACL::get_bitmask( 0 );
	}

	/**
	 * Returns a URL for the ->editlink property of this class.
	 * @return string A url to edit this comment in the admin.
	 */
	private function get_editlink()
	{
		return URL::get('admin', "page=comment&id={$this->id}");
	}
}

?>


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

/**
 * Comment metadata
 */
class CommentInfo extends InfoRecords
{

	function __construct ( $comment_id = NULL )
	{
		parent::__construct ( DB::table('commentinfo'), 'comment_id', $comment_id ); // call parent with appropriate  parameters
	}

}

?>