diff options
| author | ewinslow <ewinslow@36083f99-b078-4883-b0ff-0f9b5a30f544> | 2010-09-06 02:42:09 +0000 | 
|---|---|---|
| committer | ewinslow <ewinslow@36083f99-b078-4883-b0ff-0f9b5a30f544> | 2010-09-06 02:42:09 +0000 | 
| commit | 76dac45ebaf104b312a8527a05424601ca9d520a (patch) | |
| tree | 7440558a893ebf1d3816829ecbb96c3b0df9b4f0 /engine/classes | |
| parent | 9e8baf614938dfd1687ddce39b409c3c0e5c5753 (diff) | |
| download | elgg-76dac45ebaf104b312a8527a05424601ca9d520a.tar.gz elgg-76dac45ebaf104b312a8527a05424601ca9d520a.tar.bz2 | |
Refs #2220: Pulled most classes / interfaces out of lib files (except query.php and exception.php) into "classes" folder.  Replaced inline classes with "require_once" statements for now.  Ran unit tests to verify functionality before committing.
git-svn-id: http://code.elgg.org/elgg/trunk@6908 36083f99-b078-4883-b0ff-0f9b5a30f544
Diffstat (limited to 'engine/classes')
51 files changed, 5909 insertions, 0 deletions
| diff --git a/engine/classes/CronException.php b/engine/classes/CronException.php new file mode 100644 index 000000000..3720c2c59 --- /dev/null +++ b/engine/classes/CronException.php @@ -0,0 +1,3 @@ +<?php
 +/** The cron exception. */
 +class CronException extends Exception {}
\ No newline at end of file diff --git a/engine/classes/ElggAccess.php b/engine/classes/ElggAccess.php new file mode 100644 index 000000000..57cceef03 --- /dev/null +++ b/engine/classes/ElggAccess.php @@ -0,0 +1,32 @@ +<?php
 +/**
 + * Temporary class used to determing if access is being ignored
 + */
 +class ElggAccess {
 +	/**
 +	 * Bypass Elgg's access control if true.
 +	 * @var bool
 +	 */
 +	private $ignore_access;
 +
 +	/**
 +	 * Get current ignore access setting.
 +	 * @return bool
 +	 */
 +	public function get_ignore_access() {
 +		return $this->ignore_access;
 +	}
 +
 +	/**
 +	 * Set ignore access.
 +	 *
 +	 * @param $ignore bool true || false to ignore
 +	 * @return bool Previous setting
 +	 */
 +	public function set_ignore_access($ignore = true) {
 +		$prev = $this->ignore_access;
 +		$this->ignore_access = $ignore;
 +
 +		return $prev;
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggAnnotation.php b/engine/classes/ElggAnnotation.php new file mode 100644 index 000000000..fe85ca082 --- /dev/null +++ b/engine/classes/ElggAnnotation.php @@ -0,0 +1,107 @@ +<?php
 +/**
 + * ElggAnnotation
 + *
 + * An annotation is similar to metadata.
 + * Each entity can have more than one of each type of annotation.
 + *
 + * @package Elgg
 + * @subpackage Core
 + * @author Curverider Ltd <info@elgg.com>
 + */
 +class ElggAnnotation extends ElggExtender {
 +
 +	/**
 +	 * Construct a new annotation, optionally from a given id value or db object.
 +	 *
 +	 * @param mixed $id
 +	 */
 +	function __construct($id = null) {
 +		$this->attributes = array();
 +
 +		if (!empty($id)) {
 +			if ($id instanceof stdClass) {
 +				$annotation = $id;
 +			} else {
 +				$annotation = get_annotation($id);
 +			}
 +
 +			if ($annotation) {
 +				$objarray = (array) $annotation;
 +
 +				foreach($objarray as $key => $value) {
 +					$this->attributes[$key] = $value;
 +				}
 +
 +				$this->attributes['type'] = "annotation";
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * Class member get overloading
 +	 *
 +	 * @param string $name
 +	 * @return mixed
 +	 */
 +	function __get($name) {
 +		return $this->get($name);
 +	}
 +
 +	/**
 +	 * Class member set overloading
 +	 *
 +	 * @param string $name
 +	 * @param mixed $value
 +	 * @return void
 +	 */
 +	function __set($name, $value) {
 +		return $this->set($name, $value);
 +	}
 +
 +	/**
 +	 * Save this instance
 +	 *
 +	 * @return int an object id
 +	 */
 +	function save() {
 +		if ($this->id > 0) {
 +			return update_annotation($this->id, $this->name, $this->value, $this->value_type, $this->owner_guid, $this->access_id);
 +		} else {
 +			$this->id = create_annotation($this->entity_guid, $this->name, $this->value,
 +				$this->value_type, $this->owner_guid, $this->access_id);
 +
 +			if (!$this->id) {
 +				throw new IOException(sprintf(elgg_echo('IOException:UnableToSaveNew'), get_class()));
 +			}
 +			return $this->id;
 +		}
 +	}
 +
 +	/**
 +	 * Delete the annotation.
 +	 */
 +	function delete() {
 +		return delete_annotation($this->id);
 +	}
 +
 +	/**
 +	 * Get a url for this annotation.
 +	 *
 +	 * @return string
 +	 */
 +	public function getURL() {
 +		return get_annotation_url($this->id);
 +	}
 +
 +	// SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * For a given ID, return the object associated with it.
 +	 * This is used by the river functionality primarily.
 +	 * This is useful for checking access permissions etc on objects.
 +	 */
 +	public function getObjectFromID($id) {
 +		return get_annotation($id);
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggCache.php b/engine/classes/ElggCache.php new file mode 100644 index 000000000..c59285467 --- /dev/null +++ b/engine/classes/ElggCache.php @@ -0,0 +1,164 @@ +<?php
 +
 +/**
 + * ElggCache The elgg cache superclass.
 + * This defines the interface for a cache (wherever that cache is stored).
 + *
 + * @author Curverider Ltd <info@elgg.com>
 + * @package Elgg
 + * @subpackage API
 + */
 +abstract class ElggCache implements
 +	// Override for array access
 +	ArrayAccess  {
 +	/**
 +	 * Variables for the cache object.
 +	 *
 +	 * @var array
 +	 */
 +	private $variables;
 +
 +	/**
 +	 * Set the constructor.
 +	 */
 +	function __construct() {
 +		$this->variables = array();
 +	}
 +
 +	/**
 +	 * Set a cache variable.
 +	 *
 +	 * @param string $variable
 +	 * @param string $value
 +	 */
 +	public function set_variable($variable, $value) {
 +		if (!is_array($this->variables)) {
 +			$this->variables = array();
 +		}
 +
 +		$this->variables[$variable] = $value;
 +	}
 +
 +	/**
 +	 * Get variables for this cache.
 +	 *
 +	 * @param string $variable
 +	 * @return mixed The variable or null;
 +	 */
 +	public function get_variable($variable) {
 +		if (isset($this->variables[$variable])) {
 +			return $this->variables[$variable];
 +		}
 +
 +		return null;
 +	}
 +
 +	/**
 +	 * Class member get overloading, returning key using $this->load defaults.
 +	 *
 +	 * @param string $key
 +	 * @return mixed
 +	 */
 +	function __get($key) {
 +		return $this->load($key);
 +	}
 +
 +	/**
 +	 * Class member set overloading, setting a key using $this->save defaults.
 +	 *
 +	 * @param string $key
 +	 * @param mixed $value
 +	 * @return mixed
 +	 */
 +	function __set($key, $value) {
 +		return $this->save($key, $value);
 +	}
 +
 +	/**
 +	 * Supporting isset, using $this->load() with default values.
 +	 *
 +	 * @param string $key The name of the attribute or metadata.
 +	 * @return bool
 +	 */
 +	function __isset($key) {
 +		return (bool)$this->load($key);
 +	}
 +
 +	/**
 +	 * Supporting unsetting of magic attributes.
 +	 *
 +	 * @param string $key The name of the attribute or metadata.
 +	 */
 +	function __unset($key) {
 +		return $this->delete($key);
 +	}
 +
 +	/**
 +	 * Save data in a cache.
 +	 *
 +	 * @param string $key
 +	 * @param string $data
 +	 * @return bool
 +	 */
 +	abstract public function save($key, $data);
 +
 +	/**
 +	 * Load data from the cache using a given key.
 +	 *
 +	 * @param string $key
 +	 * @param int $offset
 +	 * @param int $limit
 +	 * @return mixed The stored data or false.
 +	 */
 +	abstract public function load($key, $offset = 0, $limit = null);
 +
 +	/**
 +	 * Invalidate a key
 +	 *
 +	 * @param string $key
 +	 * @return bool
 +	 */
 +	abstract public function delete($key);
 +
 +	/**
 +	 * Clear out all the contents of the cache.
 +	 *
 +	 */
 +	abstract public function clear();
 +
 +	/**
 +	 * Add a key only if it doesn't already exist.
 +	 * Implemented simply here, if you extend this class and your caching engine provides a better way then
 +	 * override this accordingly.
 +	 *
 +	 * @param string $key
 +	 * @param string $data
 +	 * @return bool
 +	 */
 +	public function add($key, $data) {
 +		if (!isset($this[$key])) {
 +			return $this->save($key, $data);
 +		}
 +
 +		return false;
 +	}
 +
 +	// ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
 +	function offsetSet($key, $value) {
 +		$this->save($key, $value);
 +	}
 +
 +	function offsetGet($key) {
 +		return $this->load($key);
 +	}
 +
 +	function offsetUnset($key) {
 +		if ( isset($this->key) ) {
 +			unset($this->key);
 +		}
 +	}
 +
 +	function offsetExists($offset) {
 +		return isset($this->$offset);
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggDiskFilestore.php b/engine/classes/ElggDiskFilestore.php new file mode 100644 index 000000000..6b0fa2554 --- /dev/null +++ b/engine/classes/ElggDiskFilestore.php @@ -0,0 +1,263 @@ +<?php +/** + * @class ElggDiskFilestore + * This class uses disk storage to save data. + * @author Curverider Ltd + */ +class ElggDiskFilestore extends ElggFilestore { +	/** +	 * Directory root. +	 */ +	private $dir_root; + +	/** +	 * Default depth of file directory matrix +	 */ +	private $matrix_depth = 5; + +	/** +	 * Construct a disk filestore using the given directory root. +	 * +	 * @param string $directory_root Root directory, must end in "/" +	 */ +	public function __construct($directory_root = "") { +		global $CONFIG; + +		if ($directory_root) { +			$this->dir_root = $directory_root; +		} else { +			$this->dir_root = $CONFIG->dataroot; +		} +	} + +	public function open(ElggFile $file, $mode) { +		$fullname = $this->getFilenameOnFilestore($file); + +		// Split into path and name +		$ls = strrpos($fullname,"/"); +		if ($ls===false) { +			$ls = 0; +		} + +		$path = substr($fullname, 0, $ls); +		$name = substr($fullname, $ls); + +		// Try and create the directory +		try { +			$this->make_directory_root($path); +		} catch (Exception $e) { + +		} + +		if (($mode!='write') && (!file_exists($fullname))) { +			return false; +		} + +		switch ($mode) { +			case "read" : +				$mode = "rb"; +				break; +			case "write" : +				$mode = "w+b"; +				break; +			case "append" : +				$mode = "a+b"; +				break; +			default: +				throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:UnrecognisedFileMode'), $mode)); +		} + +		return fopen($fullname, $mode); + +	} + +	public function write($f, $data) { +		return fwrite($f, $data); +	} + +	public function read($f, $length, $offset = 0) { +		if ($offset) { +			$this->seek($f, $offset); +		} + +		return fread($f, $length); +	} + +	public function close($f) { +		return fclose($f); +	} + +	public function delete(ElggFile $file) { +		$filename = $this->getFilenameOnFilestore($file); +		if (file_exists($filename)) { +			return unlink($filename); +		} else { +			return true; +		} +	} + +	public function seek($f, $position) { +		return fseek($f, $position); +	} + +	public function tell($f) { +		return ftell($f); +	} + +	public function eof($f) { +		return feof($f); +	} + +	public function getFileSize(ElggFile $file) { +		return filesize($this->getFilenameOnFilestore($file)); +	} + +	public function getFilenameOnFilestore(ElggFile $file) { +		$owner = $file->getOwnerEntity(); +		if (!$owner) { +			$owner = get_loggedin_user(); +		} + +		if ((!$owner) || (!$owner->username)) { +			throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:MissingOwner'), $file->getFilename(), $file->guid)); +		} + +		return $this->dir_root . $this->make_file_matrix($owner->guid) . $file->getFilename(); +	} + +	public function grabFile(ElggFile $file) { +		return file_get_contents($file->getFilenameOnFilestore()); +	} + +	public function exists(ElggFile $file) { +		return file_exists($this->getFilenameOnFilestore($file)); +	} + +	public function getSize($prefix,$container_guid) { +		if ($container_guid) { +			return get_dir_size($this->dir_root.$this->make_file_matrix($container_guid).$prefix); +		} else { +			return false; +		} +	} + +	/** +	 * Make the directory root. +	 * +	 * @param string $dirroot +	 */ +	protected function make_directory_root($dirroot) { +		if (!file_exists($dirroot)) { +			if (!@mkdir($dirroot, 0700, true)) { +				throw new IOException(sprintf(elgg_echo('IOException:CouldNotMake'), $dirroot)); +			} +		} + +		return true; +	} + +	/** +	 * Multibyte string tokeniser. +	 * +	 * Splits a string into an array. Will fail safely if mbstring is not installed (although this may still +	 * not handle . +	 * +	 * @param string $string String +	 * @param string $charset The charset, defaults to UTF8 +	 * @return array +	 */ +	private function mb_str_split($string, $charset = 'UTF8') { +		if (is_callable('mb_substr')) { +			$length = mb_strlen($string); +			$array = array(); + +			while ($length) { +				$array[] = mb_substr($string, 0, 1, $charset); +				$string = mb_substr($string, 1, $length, $charset); + +				$length = mb_strlen($string); +			} + +			return $array; +		} else { +			return str_split($string); +		} + +		return false; +	} + +	/** +	 * Construct the filename matrix. +	 * +	 * @param int | string $identifier +	 * @return str +	 */ +	protected function make_file_matrix($identifier) { +		if (is_numeric($identifier)) { +			return $this->user_file_matrix($identifier); +		} + +		return $this->deprecated_file_matrix($identifier); +	} + +	/** +	 * Construct the filename matrix with user info +	 * +	 * This method will generate a matrix using the entity's creation time and +	 * unique guid. This is intended only to determine a user's data directory. +	 * +	 * @param int $guid +	 * @return str +	 */ +	protected function user_file_matrix($guid) { +		// lookup the entity +		$user = get_entity($guid); +		if ($user->type != 'user') +		{ +			// only to be used for user directories +			return FALSE; +		} + +		if (!$user->time_created) { +			// fall back to deprecated method +			return $this->deprecated_file_matrix($user->username); +		} + +		$time_created = date('Y/m/d', $user->time_created); +		return "$time_created/$user->guid/"; +	} + +	/** +	 * Construct the filename matrix using a string +	 * +	 * Particularly, this is used with a username to generate the file storage +	 * location. +	 * +	 * @deprecated for user directories: use user_file_matrix() instead. +	 * +	 * @param str $filename +	 * @return str +	 */ +	protected function deprecated_file_matrix($filename) { +		// throw a warning for using deprecated method +		$error  = 'Deprecated use of ElggDiskFilestore::make_file_matrix. '; +		$error .= 'Username passed instead of guid.'; +		elgg_log($error, WARNING); + +		$user = new ElggUser($filename); +		return $this->user_file_matrix($user->guid); +	} + +	public function getParameters() { +		return array("dir_root" => $this->dir_root); +	} + +	public function setParameters(array $parameters) { +		if (isset($parameters['dir_root'])) { +			$this->dir_root = $parameters['dir_root']; +			return true; +		} + +		return false; +	} +} diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php new file mode 100644 index 000000000..f0d58d1bd --- /dev/null +++ b/engine/classes/ElggEntity.php @@ -0,0 +1,1210 @@ +<?php
 +/**
 + * ElggEntity The elgg entity superclass
 + * This class holds methods for accessing the main entities table.
 + *
 + * @author Curverider Ltd <info@elgg.com>
 + * @package Elgg
 + * @subpackage Core
 + */
 +abstract class ElggEntity implements
 +	Notable,    // Calendar interface
 +	Locatable,  // Geocoding interface
 +	Exportable, // Allow export of data
 +	Importable, // Allow import of data
 +	Loggable,	// Can events related to this object class be logged
 +	Iterator,	// Override foreach behaviour
 +	ArrayAccess // Override for array access
 +{
 +	/**
 +	 * The main attributes of an entity.
 +	 * Blank entries for all database fields should be created by the constructor.
 +	 * Subclasses should add to this in their constructors.
 +	 * Any field not appearing in this will be viewed as a
 +	 */
 +	protected $attributes;
 +
 +	/**
 +	 * If set, overrides the value of getURL()
 +	 */
 +	protected $url_override;
 +
 +	/**
 +	 * Icon override, overrides the value of getIcon().
 +	 */
 +	protected $icon_override;
 +
 +	/**
 +	 * Temporary cache for metadata, permitting meta data access before a guid has obtained.
 +	 */
 +	protected $temp_metadata;
 +
 +	/**
 +	 * Temporary cache for annotations, permitting meta data access before a guid has obtained.
 +	 */
 +	protected $temp_annotations;
 +
 +
 +	/**
 +	 * Volatile data structure for this object, allows for storage of data
 +	 * in-memory that isn't sync'd back to the metadata table.
 +	 */
 +	protected $volatile;
 +
 +	/**
 +	 * Initialise the attributes array.
 +	 * This is vital to distinguish between metadata and base parameters.
 +	 *
 +	 * Place your base parameters here.
 +	 *
 +	 * @return void
 +	 */
 +	protected function initialise_attributes() {
 +		initialise_entity_cache();
 +
 +		// Create attributes array if not already created
 +		if (!is_array($this->attributes)) {
 +			$this->attributes = array();
 +		}
 +		if (!is_array($this->temp_metadata)) {
 +			$this->temp_metadata = array();
 +		}
 +		if (!is_array($this->temp_annotations)) {
 +			$this->temp_annotations = array();
 +		}
 +		if (!is_array($this->volatile)) {
 +			$this->volatile = array();
 +		}
 +
 +		$this->attributes['guid'] = "";
 +		$this->attributes['type'] = "";
 +		$this->attributes['subtype'] = "";
 +
 +		$this->attributes['owner_guid'] = get_loggedin_userid();
 +		$this->attributes['container_guid'] = get_loggedin_userid();
 +
 +		$this->attributes['site_guid'] = 0;
 +		$this->attributes['access_id'] = ACCESS_PRIVATE;
 +		$this->attributes['time_created'] = "";
 +		$this->attributes['time_updated'] = "";
 +		$this->attributes['last_action'] = '';
 +		$this->attributes['enabled'] = "yes";
 +
 +		// There now follows a bit of a hack
 +		/* Problem: To speed things up, some objects are split over several tables, this means that it requires
 +		 * n number of database reads to fully populate an entity. This causes problems for caching and create events
 +		 * since it is not possible to tell whether a subclassed entity is complete.
 +		 * Solution: We have two counters, one 'tables_split' which tells whatever is interested how many tables
 +		 * are going to need to be searched in order to fully populate this object, and 'tables_loaded' which is how
 +		 * many have been loaded thus far.
 +		 * If the two are the same then this object is complete.
 +		 *
 +		 * Use: isFullyLoaded() to check
 +		 */
 +		$this->attributes['tables_split'] = 1;
 +		$this->attributes['tables_loaded'] = 0;
 +	}
 +
 +	/**
 +	 * Clone an entity
 +	 *
 +	 * Resets the guid so that the entity can be saved as a distinct entity from
 +	 * the original. Creation time will be set when this new entity is saved.
 +	 * The owner and container guids come from the original entity. The clone
 +	 * method copies metadata but does not copy over annotations, or private settings.
 +	 *
 +	 * Note: metadata will have its owner and access id set when the entity is saved
 +	 * and it will be the same as that of the entity.
 +	 */
 +	public function __clone() {
 +
 +		$orig_entity = get_entity($this->guid);
 +		if (!$orig_entity) {
 +			elgg_log("Failed to clone entity with GUID $this->guid", "ERROR");
 +			return;
 +		}
 +
 +		$metadata_array = get_metadata_for_entity($this->guid);
 +
 +		$this->attributes['guid'] = "";
 +
 +		$this->attributes['subtype'] = $orig_entity->getSubtype();
 +
 +		// copy metadata over to new entity - slightly convoluted due to
 +		// handling of metadata arrays
 +		if (is_array($metadata_array)) {
 +			// create list of metadata names
 +			$metadata_names = array();
 +			foreach ($metadata_array as $metadata) {
 +				$metadata_names[] = $metadata['name'];
 +			}
 +			// arrays are stored with multiple enties per name
 +			$metadata_names = array_unique($metadata_names);
 +
 +			// move the metadata over
 +			foreach ($metadata_names as $name) {
 +				$this->set($name, $orig_entity->$name);
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * Return the value of a given key.
 +	 * If $name is a key field (as defined in $this->attributes) that value is returned, otherwise it will
 +	 * then look to see if the value is in this object's metadata.
 +	 *
 +	 * Q: Why are we not using __get overload here?
 +	 * A: Because overload operators cause problems during subclassing, so we put the code here and
 +	 * create overloads in subclasses.
 +	 *
 +	 * subtype is returned as an id rather than the subtype string. Use getSubtype()
 +	 * to get the subtype string.
 +	 *
 +	 * @param string $name
 +	 * @return mixed Returns the value of a given value, or null.
 +	 */
 +	public function get($name) {
 +		// See if its in our base attribute
 +		if (isset($this->attributes[$name])) {
 +			return $this->attributes[$name];
 +		}
 +
 +		// No, so see if its in the meta data for this entity
 +		$meta = $this->getMetaData($name);
 +
 +		// getMetaData returns NULL if $name is not found
 +		return $meta;
 +	}
 +
 +	/**
 +	 * Set the value of a given key, replacing it if necessary.
 +	 * If $name is a base attribute (as defined in $this->attributes) that value is set, otherwise it will
 +	 * set the appropriate item of metadata.
 +	 *
 +	 * Note: It is important that your class populates $this->attributes with keys for all base attributes, anything
 +	 * not in their gets set as METADATA.
 +	 *
 +	 * Q: Why are we not using __set overload here?
 +	 * A: Because overload operators cause problems during subclassing, so we put the code here and
 +	 * create overloads in subclasses.
 +	 *
 +	 * @param string $name
 +	 * @param mixed $value
 +	 */
 +	public function set($name, $value) {
 +		if (array_key_exists($name, $this->attributes)) {
 +			// Certain properties should not be manually changed!
 +			switch ($name) {
 +				case 'guid':
 +				case 'time_created':
 +				case 'time_updated':
 +				case 'last_action':
 +					return FALSE;
 +					break;
 +				default:
 +					$this->attributes[$name] = $value;
 +					break;
 +			}
 +		} else {
 +			return $this->setMetaData($name, $value);
 +		}
 +
 +		return TRUE;
 +	}
 +
 +	/**
 +	 * Get a given piece of metadata.
 +	 *
 +	 * @param string $name
 +	 */
 +	public function getMetaData($name) {
 +		if ((int) ($this->guid) > 0) {
 +			$md = get_metadata_byname($this->getGUID(), $name);
 +		} else {
 +			if (isset($this->temp_metadata[$name])) {
 +				return $this->temp_metadata[$name];
 +			}
 +		}
 +
 +		if ($md && !is_array($md)) {
 +			return $md->value;
 +		} else if ($md && is_array($md)) {
 +			return metadata_array_to_values($md);
 +		}
 +
 +		return null;
 +	}
 +
 +	/**
 +	 * Class member get overloading
 +	 *
 +	 * @param string $name
 +	 * @return mixed
 +	 */
 +	function __get($name) {
 +		return $this->get($name);
 +	}
 +
 +	/**
 +	 * Class member set overloading
 +	 *
 +	 * @param string $name
 +	 * @param mixed $value
 +	 * @return mixed
 +	 */
 +	function __set($name, $value) {
 +		return $this->set($name, $value);
 +	}
 +
 +	/**
 +	 * Supporting isset.
 +	 *
 +	 * @param string $name The name of the attribute or metadata.
 +	 * @return bool
 +	 */
 +	function __isset($name) {
 +		return $this->$name !== NULL;
 +	}
 +
 +	/**
 +	 * Supporting unsetting of magic attributes.
 +	 *
 +	 * @param string $name The name of the attribute or metadata.
 +	 */
 +	function __unset($name) {
 +		if (array_key_exists($name, $this->attributes)) {
 +			$this->attributes[$name] = "";
 +		}
 +		else {
 +			$this->clearMetaData($name);
 +		}
 +	}
 +
 +	/**
 +	 * Set a piece of metadata.
 +	 *
 +	 * @param string $name Name of the metadata
 +	 * @param mixed $value Value of the metadata
 +	 * @param string $value_type Types supported: integer and string. Will auto-identify if not set
 +	 * @param bool $multiple (does not support associative arrays)
 +	 * @return bool
 +	 */
 +	public function setMetaData($name, $value, $value_type = "", $multiple = false) {
 +		if (is_array($value)) {
 +			unset($this->temp_metadata[$name]);
 +			remove_metadata($this->getGUID(), $name);
 +			foreach ($value as $v) {
 +				if ((int) $this->guid > 0) {
 +					$multiple = true;
 +					if (!create_metadata($this->getGUID(), $name, $v, $value_type,
 +					$this->getOwner(), $this->getAccessID(), $multiple)) {
 +						return false;
 +					}
 +				} else {
 +					if (($multiple) && (isset($this->temp_metadata[$name]))) {
 +						if (!is_array($this->temp_metadata[$name])) {
 +							$tmp = $this->temp_metadata[$name];
 +							$this->temp_metadata[$name] = array();
 +							$this->temp_metadata[$name][] = $tmp;
 +						}
 +
 +						$this->temp_metadata[$name][] = $value;
 +					}
 +					else {
 +						$this->temp_metadata[$name] = $value;
 +					}
 +				}
 +			}
 +
 +			return true;
 +		} else {
 +			unset($this->temp_metadata[$name]);
 +			if ((int) $this->guid > 0) {
 +				$result = create_metadata($this->getGUID(), $name, $value, $value_type, $this->getOwner(), $this->getAccessID(), $multiple);
 +				return (bool)$result;
 +			} else {
 +				if (($multiple) && (isset($this->temp_metadata[$name]))) {
 +					if (!is_array($this->temp_metadata[$name])) {
 +						$tmp = $this->temp_metadata[$name];
 +						$this->temp_metadata[$name] = array();
 +						$this->temp_metadata[$name][] = $tmp;
 +					}
 +
 +					$this->temp_metadata[$name][] = $value;
 +				}
 +				else {
 +					$this->temp_metadata[$name] = $value;
 +				}
 +
 +				return true;
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * Clear metadata.
 +	 */
 +	public function clearMetaData($name = "") {
 +		if (empty($name)) {
 +			return clear_metadata($this->getGUID());
 +		} else {
 +			return remove_metadata($this->getGUID(),$name);
 +		}
 +	}
 +
 +
 +	/**
 +	 * Get a piece of volatile (non-persisted) data on this entity
 +	 */
 +	public function getVolatileData($name) {
 +		if (!is_array($this->volatile)) {
 +			$this->volatile = array();
 +		}
 +
 +		if (array_key_exists($name, $this->volatile)) {
 +			return $this->volatile[$name];
 +		} else {
 +			return NULL;
 +		}
 +	}
 +
 +
 +	/**
 +	 * Set a piece of volatile (non-persisted) data on this entity
 +	 */
 +	public function setVolatileData($name, $value) {
 +		if (!is_array($this->volatile)) {
 +			$this->volatile = array();
 +		}
 +
 +		$this->volatile[$name] = $value;
 +	}
 +
 +
 +	/**
 +	 * Remove all entities associated with this entity
 +	 *
 +	 * @return true
 +	 */
 +	public function clearRelationships() {
 +		remove_entity_relationships($this->getGUID());
 +		remove_entity_relationships($this->getGUID(),"",true);
 +		return true;
 +	}
 +
 +	/**
 +	 * Add a relationship.
 +	 *
 +	 * @param int $guid Relationship to link to.
 +	 * @param string $relationship The type of relationship.
 +	 * @return bool
 +	 */
 +	public function addRelationship($guid, $relationship) {
 +		return add_entity_relationship($this->getGUID(), $relationship, $guid);
 +	}
 +
 +	/**
 +	 * Remove a relationship
 +	 *
 +	 * @param int $guid
 +	 * @param str $relationship
 +	 * @return bool
 +	 */
 +	public function removeRelationship($guid, $relationship) {
 +		return remove_entity_relationship($this->getGUID(), $relationship, $guid);
 +	}
 +
 +	/**
 +	 * Adds a private setting to this entity.
 +	 *
 +	 * @param $name
 +	 * @param $value
 +	 * @return unknown_type
 +	 */
 +	function setPrivateSetting($name, $value) {
 +		return set_private_setting($this->getGUID(), $name, $value);
 +	}
 +
 +	/**
 +	 * Gets private setting for this entity
 +	 *
 +	 * @param $name
 +	 * @return unknown_type
 +	 */
 +	function getPrivateSetting($name) {
 +		return get_private_setting($this->getGUID(), $name);
 +	}
 +
 +	/**
 +	 * Removes private setting for this entity.
 +	 *
 +	 * @param $name
 +	 * @return unknown_type
 +	 */
 +	function removePrivateSetting($name) {
 +		return remove_private_setting($this->getGUID(), $name);
 +	}
 +
 +	/**
 +	 * Adds an annotation to an entity. By default, the type is detected automatically; however,
 +	 * it can also be set. Note that by default, annotations are private.
 +	 *
 +	 * @param string $name
 +	 * @param mixed $value
 +	 * @param int $access_id
 +	 * @param int $owner_id
 +	 * @param string $vartype
 +	 */
 +	function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_id = 0, $vartype = "") {
 +		if ((int) $this->guid > 0) {
 +			return create_annotation($this->getGUID(), $name, $value, $vartype, $owner_id, $access_id);
 +		} else {
 +			$this->temp_annotations[$name] = $value;
 +		}
 +		return true;
 +	}
 +
 +	/**
 +	 * Get the annotations for an entity.
 +	 *
 +	 * @param string $name
 +	 * @param int $limit
 +	 * @param int $offset
 +	 * @param string $order
 +	 */
 +	function getAnnotations($name, $limit = 50, $offset = 0, $order="asc") {
 +		if ((int) ($this->guid) > 0) {
 +			return get_annotations($this->getGUID(), "", "", $name, "", 0, $limit, $offset, $order);
 +		} else {
 +			return $this->temp_annotations[$name];
 +		}
 +	}
 +
 +	/**
 +	 * Remove all annotations or all annotations for this entity.
 +	 *
 +	 * @param string $name
 +	 */
 +	function clearAnnotations($name = "") {
 +		return clear_annotations($this->getGUID(), $name);
 +	}
 +
 +	/**
 +	 * Return the annotations for the entity.
 +	 *
 +	 * @param string $name The type of annotation.
 +	 */
 +	function countAnnotations($name = "") {
 +		return count_annotations($this->getGUID(), "", "", $name);
 +	}
 +
 +	/**
 +	 * Get the average of an integer type annotation.
 +	 *
 +	 * @param string $name
 +	 */
 +	function getAnnotationsAvg($name) {
 +		return get_annotations_avg($this->getGUID(), "", "", $name);
 +	}
 +
 +	/**
 +	 * Get the sum of integer type annotations of a given name.
 +	 *
 +	 * @param string $name
 +	 */
 +	function getAnnotationsSum($name) {
 +		return get_annotations_sum($this->getGUID(), "", "", $name);
 +	}
 +
 +	/**
 +	 * Get the minimum of integer type annotations of given name.
 +	 *
 +	 * @param string $name
 +	 */
 +	function getAnnotationsMin($name) {
 +		return get_annotations_min($this->getGUID(), "", "", $name);
 +	}
 +
 +	/**
 +	 * Get the maximum of integer type annotations of a given name.
 +	 *
 +	 * @param string $name
 +	 */
 +	function getAnnotationsMax($name) {
 +		return get_annotations_max($this->getGUID(), "", "", $name);
 +	}
 +
 +	/**
 +	 * Gets an array of entities from a specific relationship type
 +	 *
 +	 * @param string $relationship Relationship type (eg "friends")
 +	 * @param true|false $inverse Is this an inverse relationship?
 +	 * @param int $limit Number of elements to return
 +	 * @param int $offset Indexing offset
 +	 * @return array|false An array of entities or false on failure
 +	 */
 +	function getEntitiesFromRelationship($relationship, $inverse = false, $limit = 50, $offset = 0) {
 +		return elgg_get_entities_from_relationship(array(
 +			'relationship' => $relationship,
 +			'relationship_guid' => $this->getGUID(),
 +			'inverse_relationship' => $inverse,
 +			'limit' => $limit,
 +			'offset' => $offset
 +		));
 +	}
 +
 +	/**
 +	 * Gets the number of of entities from a specific relationship type
 +	 *
 +	 * @param string $relationship Relationship type (eg "friends")
 +	 * @param bool $inverse_relationship
 +	 * @return int|false The number of entities or false on failure
 +	 */
 +	function countEntitiesFromRelationship($relationship, $inverse_relationship = FALSE) {
 +		return elgg_get_entities_from_relationship(array(
 +			'relationship' => $relationship,
 +			'relationship_guid' => $this->getGUID(),
 +			'inverse_relationship' => $inverse_relationship,
 +			'count' => TRUE
 +		));
 +	}
 +
 +	/**
 +	 * Determines whether or not the specified user (by default the current one) can edit the entity
 +	 *
 +	 * @param int $user_guid The user GUID, optionally (defaults to the currently logged in user)
 +	 * @return true|false
 +	 */
 +	function canEdit($user_guid = 0) {
 +		return can_edit_entity($this->getGUID(), $user_guid);
 +	}
 +
 +	/**
 +	 * Determines whether or not the specified user (by default the current one) can edit metadata on the entity
 +	 *
 +	 * @param ElggMetadata $metadata The piece of metadata to specifically check
 +	 * @param int $user_guid The user GUID, optionally (defaults to the currently logged in user)
 +	 * @return true|false
 +	 */
 +	function canEditMetadata($metadata = null, $user_guid = 0) {
 +		return can_edit_entity_metadata($this->getGUID(), $user_guid, $metadata);
 +	}
 +
 +	/**
 +	 * Returns whether the given user (or current user) has the ability to write to this container.
 +	 *
 +	 * @param int $user_guid The user.
 +	 * @return bool
 +	 */
 +	public function canWriteToContainer($user_guid = 0) {
 +		return can_write_to_container($user_guid, $this->getGUID());
 +	}
 +
 +	/**
 +	 * Obtain this entity's access ID
 +	 *
 +	 * @return int The access ID
 +	 */
 +	public function getAccessID() {
 +		return $this->get('access_id');
 +	}
 +
 +	/**
 +	 * Obtain this entity's GUID
 +	 *
 +	 * @return int GUID
 +	 */
 +	public function getGUID() {
 +		return $this->get('guid');
 +	}
 +
 +	/**
 +	 * Get the owner of this entity
 +	 *
 +	 * @return int The owner GUID
 +	 */
 +	public function getOwner() {
 +		return $this->get('owner_guid');
 +	}
 +
 +	/**
 +	 * Returns the actual entity of the user who owns this entity, if any
 +	 *
 +	 * @return ElggEntity The owning user
 +	 */
 +	public function getOwnerEntity() {
 +		return get_entity($this->get('owner_guid'));
 +	}
 +
 +	/**
 +	 * Gets the type of entity this is
 +	 *
 +	 * @return string Entity type
 +	 */
 +	public function getType() {
 +		return $this->get('type');
 +	}
 +
 +	/**
 +	 * Returns the subtype of this entity
 +	 *
 +	 * @return string The entity subtype
 +	 */
 +	public function getSubtype() {
 +		// If this object hasn't been saved, then return the subtype string.
 +		if (!((int) $this->guid > 0)) {
 +			return $this->get('subtype');
 +		}
 +
 +		return get_subtype_from_id($this->get('subtype'));
 +	}
 +
 +	/**
 +	 * Gets the UNIX epoch time that this entity was created
 +	 *
 +	 * @return int UNIX epoch time
 +	 */
 +	public function getTimeCreated() {
 +		return $this->get('time_created');
 +	}
 +
 +	/**
 +	 * Gets the UNIX epoch time that this entity was last updated
 +	 *
 +	 * @return int UNIX epoch time
 +	 */
 +	public function getTimeUpdated() {
 +		return $this->get('time_updated');
 +	}
 +
 +	/**
 +	 * Gets the display URL for this entity
 +	 *
 +	 * @return string The URL
 +	 */
 +	public function getURL() {
 +		if (!empty($this->url_override)) {
 +			return $this->url_override;
 +		}
 +		return get_entity_url($this->getGUID());
 +	}
 +
 +	/**
 +	 * Overrides the URL returned by getURL
 +	 *
 +	 * @param string $url The new item URL
 +	 * @return string The URL
 +	 */
 +	public function setURL($url) {
 +		$this->url_override = $url;
 +		return $url;
 +	}
 +
 +	/**
 +	 * Return a url for the entity's icon, trying multiple alternatives.
 +	 *
 +	 * @param string $size Either 'large','medium','small' or 'tiny'
 +	 * @return string The url or false if no url could be worked out.
 +	 */
 +	public function getIcon($size = 'medium') {
 +		if (isset($this->icon_override[$size])) {
 +			return $this->icon_override[$size];
 +		}
 +		return get_entity_icon_url($this, $size);
 +	}
 +
 +	/**
 +	 * Set an icon override for an icon and size.
 +	 *
 +	 * @param string $url The url of the icon.
 +	 * @param string $size The size its for.
 +	 * @return bool
 +	 */
 +	public function setIcon($url, $size = 'medium') {
 +		$url = sanitise_string($url);
 +		$size = sanitise_string($size);
 +
 +		if (!$this->icon_override) {
 +			$this->icon_override = array();
 +		}
 +		$this->icon_override[$size] = $url;
 +
 +		return true;
 +	}
 +
 +	/**
 +	 * Tests to see whether the object has been fully loaded.
 +	 *
 +	 * @return bool
 +	 */
 +	public function isFullyLoaded() {
 +		return ! ($this->attributes['tables_loaded'] < $this->attributes['tables_split']);
 +	}
 +
 +	/**
 +	 * Save generic attributes to the entities table.
 +	 */
 +	public function save() {
 +		$guid = (int) $this->guid;
 +		if ($guid > 0) {
 +			cache_entity($this);
 +
 +			return update_entity(
 +				$this->get('guid'),
 +				$this->get('owner_guid'),
 +				$this->get('access_id'),
 +				$this->get('container_guid')
 +			);
 +		} else {
 +			// Create a new entity (nb: using attribute array directly 'cos set function does something special!)
 +			$this->attributes['guid'] = create_entity($this->attributes['type'], $this->attributes['subtype'], $this->attributes['owner_guid'], $this->attributes['access_id'], $this->attributes['site_guid'], $this->attributes['container_guid']);
 +			if (!$this->attributes['guid']) {
 +				throw new IOException(elgg_echo('IOException:BaseEntitySaveFailed'));
 +			}
 +
 +			// Save any unsaved metadata
 +			// @todo How to capture extra information (access id etc)
 +			if (sizeof($this->temp_metadata) > 0) {
 +				foreach($this->temp_metadata as $name => $value) {
 +					$this->$name = $value;
 +					unset($this->temp_metadata[$name]);
 +				}
 +			}
 +
 +			// Save any unsaved annotations metadata.
 +			// @todo How to capture extra information (access id etc)
 +			if (sizeof($this->temp_annotations) > 0) {
 +				foreach($this->temp_annotations as $name => $value) {
 +					$this->annotate($name, $value);
 +					unset($this->temp_annotations[$name]);
 +				}
 +			}
 +
 +			// set the subtype to id now rather than a string
 +			$this->attributes['subtype'] = get_subtype_id($this->attributes['type'], $this->attributes['subtype']);
 +
 +			// Cache object handle
 +			if ($this->attributes['guid']) {
 +				cache_entity($this);
 +			}
 +
 +			return $this->attributes['guid'];
 +		}
 +	}
 +
 +	/**
 +	 * Load the basic entity information and populate base attributes array.
 +	 *
 +	 * @param int $guid
 +	 */
 +	protected function load($guid) {
 +		$row = get_entity_as_row($guid);
 +
 +		if ($row) {
 +			// Create the array if necessary - all subclasses should test before creating
 +			if (!is_array($this->attributes)) {
 +				$this->attributes = array();
 +			}
 +
 +			// Now put these into the attributes array as core values
 +			$objarray = (array) $row;
 +			foreach($objarray as $key => $value) {
 +				$this->attributes[$key] = $value;
 +			}
 +
 +			// Increment the portion counter
 +			if (!$this->isFullyLoaded()) {
 +				$this->attributes['tables_loaded']++;
 +			}
 +
 +			// Cache object handle
 +			if ($this->attributes['guid']) {
 +				cache_entity($this);
 +			}
 +
 +			return true;
 +		}
 +
 +		return false;
 +	}
 +
 +	/**
 +	 * Disable this entity.
 +	 *
 +	 * @param string $reason Optional reason
 +	 * @param bool $recursive Recursively disable all contained entities?
 +	 */
 +	public function disable($reason = "", $recursive = true) {
 +		return disable_entity($this->get('guid'), $reason, $recursive);
 +	}
 +
 +	/**
 +	 * Re-enable this entity.
 +	 */
 +	public function enable() {
 +		return enable_entity($this->get('guid'));
 +	}
 +
 +	/**
 +	 * Is this entity enabled?
 +	 *
 +	 * @return boolean
 +	 */
 +	public function isEnabled() {
 +		if ($this->enabled == 'yes') {
 +			return true;
 +		}
 +
 +		return false;
 +	}
 +
 +	/**
 +	 * Delete this entity.
 +	 */
 +	public function delete() {
 +		return delete_entity($this->get('guid'));
 +	}
 +
 +	// LOCATABLE INTERFACE /////////////////////////////////////////////////////////////
 +
 +	/** Interface to set the location */
 +	public function setLocation($location) {
 +		$location = sanitise_string($location);
 +
 +		$this->location = $location;
 +
 +		return true;
 +	}
 +
 +	/**
 +	 * Set latitude and longitude tags for a given entity.
 +	 *
 +	 * @param float $lat
 +	 * @param float $long
 +	 */
 +	public function setLatLong($lat, $long) {
 +		$lat = sanitise_string($lat);
 +		$long = sanitise_string($long);
 +
 +		$this->set('geo:lat', $lat);
 +		$this->set('geo:long', $long);
 +
 +		return true;
 +	}
 +
 +	/**
 +	 * Get the contents of the ->geo:lat field.
 +	 *
 +	 */
 +	public function getLatitude() {
 +		return $this->get('geo:lat');
 +	}
 +
 +	/**
 +	 * Get the contents of the ->geo:lat field.
 +	 *
 +	 */
 +	public function getLongitude() {
 +		return $this->get('geo:long');
 +	}
 +
 +	/**
 +	 * Get the ->location metadata.
 +	 *
 +	 */
 +	public function getLocation() {
 +		return $this->get('location');
 +	}
 +
 +	// NOTABLE INTERFACE ///////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Calendar functionality.
 +	 * This function sets the time of an object on a calendar listing.
 +	 *
 +	 * @param int $hour If ommitted, now is assumed.
 +	 * @param int $minute If ommitted, now is assumed.
 +	 * @param int $second If ommitted, now is assumed.
 +	 * @param int $day If ommitted, now is assumed.
 +	 * @param int $month If ommitted, now is assumed.
 +	 * @param int $year If ommitted, now is assumed.
 +	 * @param int $duration Duration of event, remainder of the day is assumed.
 +	 */
 +	public function setCalendarTimeAndDuration($hour = NULL, $minute = NULL, $second = NULL, $day = NULL, $month = NULL, $year = NULL, $duration = NULL) {
 +		$start = mktime($hour, $minute, $second, $month, $day, $year);
 +		$end = $start + abs($duration);
 +		if (!$duration) {
 +			$end = get_day_end($day,$month,$year);
 +		}
 +
 +		$this->calendar_start = $start;
 +		$this->calendar_end = $end;
 +
 +		return true;
 +	}
 +
 +	/**
 +	 * Return the start timestamp.
 +	 */
 +	public function getCalendarStartTime() {
 +		return (int)$this->calendar_start;
 +	}
 +
 +	/**
 +	 * Return the end timestamp.
 +	 */
 +	public function getCalendarEndTime() {
 +		return (int)$this->calendar_end;
 +	}
 +
 +	// EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Return an array of fields which can be exported.
 +	 */
 +	public function getExportableValues() {
 +		return array(
 +			'guid',
 +			'type',
 +			'subtype',
 +			'time_created',
 +			'time_updated',
 +			'container_guid',
 +			'owner_guid',
 +			'site_guid'
 +		);
 +	}
 +
 +	/**
 +	 * Export this class into an array of ODD Elements containing all necessary fields.
 +	 * Override if you wish to return more information than can be found in $this->attributes (shouldn't happen)
 +	 */
 +	public function export() {
 +		$tmp = array();
 +
 +		// Generate uuid
 +		$uuid = guid_to_uuid($this->getGUID());
 +
 +		// Create entity
 +		$odd = new ODDEntity(
 +			$uuid,
 +			$this->attributes['type'],
 +			get_subtype_from_id($this->attributes['subtype'])
 +		);
 +
 +		$tmp[] = $odd;
 +
 +		$exportable_values = $this->getExportableValues();
 +
 +		// Now add its attributes
 +		foreach ($this->attributes as $k => $v) {
 +			$meta = NULL;
 +
 +			if (in_array( $k, $exportable_values)) {
 +				switch ($k) {
 +					case 'guid' : 			// Dont use guid in OpenDD
 +					case 'type' :			// Type and subtype already taken care of
 +					case 'subtype' :
 +					break;
 +
 +					case 'time_created' :	// Created = published
 +						$odd->setAttribute('published', date("r", $v));
 +					break;
 +
 +					case 'site_guid' : // Container
 +						$k = 'site_uuid';
 +						$v = guid_to_uuid($v);
 +						$meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
 +					break;
 +
 +					case 'container_guid' : // Container
 +						$k = 'container_uuid';
 +						$v = guid_to_uuid($v);
 +						$meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
 +					break;
 +
 +					case 'owner_guid' :			// Convert owner guid to uuid, this will be stored in metadata
 +						$k = 'owner_uuid';
 +						$v = guid_to_uuid($v);
 +						$meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
 +					break;
 +
 +					default :
 +						$meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
 +				}
 +
 +				// set the time of any metadata created
 +				if ($meta) {
 +					$meta->setAttribute('published', date("r",$this->time_created));
 +					$tmp[] = $meta;
 +				}
 +			}
 +		}
 +
 +		// Now we do something a bit special.
 +		/*
 +		 * This provides a rendered view of the entity to foreign sites.
 +		 */
 +
 +		elgg_set_viewtype('default');
 +		$view = elgg_view_entity($this, true);
 +		elgg_set_viewtype();
 +
 +		$tmp[] = new ODDMetaData($uuid . "volatile/renderedentity/", $uuid, 'renderedentity', $view , 'volatile');
 +
 +		return $tmp;
 +	}
 +
 +	// IMPORTABLE INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Import data from an parsed xml data array.
 +	 *
 +	 * @param array $data
 +	 * @param int $version
 +	 */
 +	public function import(ODD $data) {
 +		if (!($data instanceof ODDEntity)) {
 +			throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnexpectedODDClass'));
 +		}
 +
 +		// Set type and subtype
 +		$this->attributes['type'] = $data->getAttribute('class');
 +		$this->attributes['subtype'] = $data->getAttribute('subclass');
 +
 +		// Set owner
 +		$this->attributes['owner_guid'] = get_loggedin_userid(); // Import as belonging to importer.
 +
 +		// Set time
 +		$this->attributes['time_created'] = strtotime($data->getAttribute('published'));
 +		$this->attributes['time_updated'] = time();
 +
 +		return true;
 +	}
 +
 +	// SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Return an identification for the object for storage in the system log.
 +	 * This id must be an integer.
 +	 *
 +	 * @return int
 +	 */
 +	public function getSystemLogID() {
 +		return $this->getGUID();
 +	}
 +
 +	/**
 +	 * Return the class name of the object.
 +	 */
 +	public function getClassName() {
 +		return get_class($this);
 +	}
 +
 +	/**
 +	 * For a given ID, return the object associated with it.
 +	 * This is used by the river functionality primarily.
 +	 * This is useful for checking access permissions etc on objects.
 +	 */
 +	public function getObjectFromID($id) {
 +		return get_entity($id);
 +	}
 +
 +	/**
 +	 * Return the GUID of the owner of this object.
 +	 */
 +	public function getObjectOwnerGUID() {
 +		return $this->owner_guid;
 +	}
 +
 +	/**
 +	 * Returns tags for this entity.
 +	 *
 +	 * @param array $tag_names Optionally restrict by tag metadata names.
 +	 * @return array
 +	 */
 +	public function getTags($tag_names = NULL) {
 +		global $CONFIG;
 +
 +		if ($tag_names && !is_array($tag_names)) {
 +			$tag_names = array($tag_names);
 +		}
 +
 +		$valid_tags = elgg_get_registered_tag_metadata_names();
 +		$entity_tags = array();
 +
 +		foreach ($valid_tags as $tag_name) {
 +			if (is_array($tag_names) && !in_array($tag_name, $tag_names)) {
 +				continue;
 +			}
 +
 +			if ($tags = $this->$tag_name) {
 +				// if a single tag, metadata returns a string.
 +				// if multiple tags, metadata returns an array.
 +				if (is_array($tags)) {
 +					$entity_tags = array_merge($entity_tags, $tags);
 +				} else {
 +					$entity_tags[] = $tags;
 +				}
 +			}
 +		}
 +
 +		return $entity_tags;
 +	}
 +
 +	// ITERATOR INTERFACE //////////////////////////////////////////////////////////////
 +	/*
 +	 * This lets an entity's attributes be displayed using foreach as a normal array.
 +	 * Example: http://www.sitepoint.com/print/php5-standard-library
 +	 */
 +
 +	private $valid = FALSE;
 +
 +	function rewind() {
 +		$this->valid = (FALSE !== reset($this->attributes));
 +	}
 +
 +	function current() {
 +		return current($this->attributes);
 +	}
 +
 +	function key() {
 +		return key($this->attributes);
 +	}
 +
 +	function next() {
 +		$this->valid = (FALSE !== next($this->attributes));
 +	}
 +
 +	function valid() {
 +		return $this->valid;
 +	}
 +
 +	// ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
 +	/*
 +	 * This lets an entity's attributes be accessed like an associative array.
 +	 * Example: http://www.sitepoint.com/print/php5-standard-library
 +	 */
 +
 +	function offsetSet($key, $value) {
 +		if ( array_key_exists($key, $this->attributes) ) {
 +			$this->attributes[$key] = $value;
 +		}
 +	}
 +
 +	function offsetGet($key) {
 +		if ( array_key_exists($key, $this->attributes) ) {
 +			return $this->attributes[$key];
 +		}
 +	}
 +
 +	function offsetUnset($key) {
 +		if ( array_key_exists($key, $this->attributes) ) {
 +			$this->attributes[$key] = ""; // Full unsetting is dangerious for our objects
 +		}
 +	}
 +
 +	function offsetExists($offset) {
 +		return array_key_exists($offset, $this->attributes);
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggExtender.php b/engine/classes/ElggExtender.php new file mode 100644 index 000000000..ec7a21f35 --- /dev/null +++ b/engine/classes/ElggExtender.php @@ -0,0 +1,252 @@ +<?php
 +
 +/**
 + * ElggExtender
 + *
 + * @author Curverider Ltd
 + * @package Elgg
 + * @subpackage Core
 + */
 +abstract class ElggExtender implements
 +	Exportable,
 +	Loggable,	// Can events related to this object class be logged
 +	Iterator,	// Override foreach behaviour
 +	ArrayAccess // Override for array access
 +{
 +	/**
 +	 * This contains the site's main properties (id, etc)
 +	 * @var array
 +	 */
 +	protected $attributes;
 +
 +	/**
 +	 * Get an attribute
 +	 *
 +	 * @param string $name
 +	 * @return mixed
 +	 */
 +	protected function get($name) {
 +		if (isset($this->attributes[$name])) {
 +			// Sanitise value if necessary
 +			if ($name=='value') {
 +				switch ($this->attributes['value_type']) {
 +					case 'integer' :
 +						return (int)$this->attributes['value'];
 +
 +					//case 'tag' :
 +					//case 'file' :
 +					case 'text' :
 +						return ($this->attributes['value']);
 +
 +					default :
 +						throw new InstallationException(sprintf(elgg_echo('InstallationException:TypeNotSupported'), $this->attributes['value_type']));
 +				}
 +			}
 +
 +			return $this->attributes[$name];
 +		}
 +		return null;
 +	}
 +
 +	/**
 +	 * Set an attribute
 +	 *
 +	 * @param string $name
 +	 * @param mixed $value
 +	 * @param string $value_type
 +	 * @return boolean
 +	 */
 +	protected function set($name, $value, $value_type = "") {
 +		$this->attributes[$name] = $value;
 +		if ($name == 'value') {
 +			$this->attributes['value_type'] = detect_extender_valuetype($value, $value_type);
 +		}
 +
 +		return true;
 +	}
 +
 +	/**
 +	 * Return the owner of this annotation.
 +	 *
 +	 * @return mixed
 +	 */
 +	public function getOwner() {
 +		return $this->owner_guid;
 +	}
 +
 +	/**
 +	 * Return the owner entity
 +	 *
 +	 * @return mixed
 +	 * @since 1.7.0
 +	 */
 +	public function getOwnerEntity() {
 +		return get_user($this->owner_guid);
 +	}
 +
 +	/**
 +	 * Returns the entity this is attached to
 +	 *
 +	 * @return ElggEntity The enttiy
 +	 */
 +	public function getEntity() {
 +		return get_entity($this->entity_guid);
 +	}
 +
 +	/**
 +	 * Save this data to the appropriate database table.
 +	 */
 +	abstract public function save();
 +
 +	/**
 +	 * Delete this data.
 +	 */
 +	abstract public function delete();
 +
 +	/**
 +	 * Determines whether or not the specified user can edit this
 +	 *
 +	 * @param int $user_guid The GUID of the user (defaults to currently logged in user)
 +	 * @return true|false
 +	 */
 +	public function canEdit($user_guid = 0) {
 +		return can_edit_extender($this->id,$this->type,$user_guid);
 +	}
 +
 +	/**
 +	 * Return a url for this extender.
 +	 *
 +	 * @return string
 +	 */
 +	public abstract function getURL();
 +
 +	// EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Return an array of fields which can be exported.
 +	 */
 +	public function getExportableValues() {
 +		return array(
 +			'id',
 +			'entity_guid',
 +			'name',
 +			'value',
 +			'value_type',
 +			'owner_guid',
 +			'type',
 +		);
 +	}
 +
 +	/**
 +	 * Export this object
 +	 *
 +	 * @return array
 +	 */
 +	public function export() {
 +		$uuid = get_uuid_from_object($this);
 +
 +		$meta = new ODDMetadata($uuid, guid_to_uuid($this->entity_guid), $this->attributes['name'], $this->attributes['value'], $this->attributes['type'], guid_to_uuid($this->owner_guid));
 +		$meta->setAttribute('published', date("r", $this->time_created));
 +
 +		return $meta;
 +	}
 +
 +	// SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Return an identification for the object for storage in the system log.
 +	 * This id must be an integer.
 +	 *
 +	 * @return int
 +	 */
 +	public function getSystemLogID() {
 +		return $this->id;
 +	}
 +
 +	/**
 +	 * Return the class name of the object.
 +	 */
 +	public function getClassName() {
 +		return get_class($this);
 +	}
 +
 +	/**
 +	 * Return the GUID of the owner of this object.
 +	 */
 +	public function getObjectOwnerGUID() {
 +		return $this->owner_guid;
 +	}
 +
 +	/**
 +	 * Return a type of the object - eg. object, group, user, relationship, metadata, annotation etc
 +	 */
 +	public function getType() {
 +		return $this->type;
 +	}
 +
 +	/**
 +	 * Return a subtype. For metadata & annotations this is the 'name' and
 +	 * for relationship this is the relationship type.
 +	 */
 +	public function getSubtype() {
 +		return $this->name;
 +	}
 +
 +
 +	// ITERATOR INTERFACE //////////////////////////////////////////////////////////////
 +	/*
 +	 * This lets an entity's attributes be displayed using foreach as a normal array.
 +	 * Example: http://www.sitepoint.com/print/php5-standard-library
 +	 */
 +
 +	private $valid = FALSE;
 +
 +	function rewind() {
 +		$this->valid = (FALSE !== reset($this->attributes));
 +	}
 +
 +	function current() {
 +		return current($this->attributes);
 +	}
 +
 +	function key() {
 +		return key($this->attributes);
 +	}
 +
 +	function next() {
 +		$this->valid = (FALSE !== next($this->attributes));
 +	}
 +
 +	function valid() {
 +		return $this->valid;
 +	}
 +
 +	// ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
 +	/*
 +	 * This lets an entity's attributes be accessed like an associative array.
 +	 * Example: http://www.sitepoint.com/print/php5-standard-library
 +	 */
 +
 +	function offsetSet($key, $value) {
 +		if ( array_key_exists($key, $this->attributes) ) {
 +			$this->attributes[$key] = $value;
 +		}
 +	}
 +
 +	function offsetGet($key) {
 +		if ( array_key_exists($key, $this->attributes) ) {
 +			return $this->attributes[$key];
 +		}
 +	}
 +
 +	function offsetUnset($key) {
 +		if ( array_key_exists($key, $this->attributes) ) {
 +			// Full unsetting is dangerious for our objects
 +			$this->attributes[$key] = "";
 +		}
 +	}
 +
 +	function offsetExists($offset) {
 +		return array_key_exists($offset, $this->attributes);
 +	}
 +}
 diff --git a/engine/classes/ElggFile.php b/engine/classes/ElggFile.php new file mode 100644 index 000000000..68ed2f2f2 --- /dev/null +++ b/engine/classes/ElggFile.php @@ -0,0 +1,318 @@ +<?php + +/** + * @class ElggFile + * This class represents a physical file. + * + * Usage: + *		Create a new ElggFile object and specify a filename, and optionally a FileStore (if one isn't specified + *		then the default is assumed. + * + * 		Open the file using the appropriate mode, and you will be able to read and write to the file. + * + * 		Optionally, you can also call the file's save() method, this will turn the file into an entity in the + * 		system and permit you to do things like attach tags to the file etc. This is not done automatically since + * 		there are many occasions where you may want access to file data on datastores using the ElggFile interface + * 		but do not want to create an Entity reference to it in the system (temporary files for example). + * + * @author Curverider Ltd + */ +class ElggFile extends ElggObject { +	/** Filestore */ +	private $filestore; + +	/** File handle used to identify this file in a filestore. Created by open. */ +	private $handle; + +	protected function initialise_attributes() { +		parent::initialise_attributes(); + +		$this->attributes['subtype'] = "file"; +	} + +	public function __construct($guid = null) { +		parent::__construct($guid); + +		// Set default filestore +		$this->filestore = $this->getFilestore(); +	} + +	/** +	 * Set the filename of this file. +	 * +	 * @param string $name The filename. +	 */ +	public function setFilename($name) { +		$this->filename = $name; +	} + +	/** +	 * Return the filename. +	 */ +	public function getFilename() { +		return $this->filename; +	} + +	/** +	 * Return the filename of this file as it is/will be stored on the filestore, which may be different +	 * to the filename. +	 */ +	public function getFilenameOnFilestore() { +		return $this->filestore->getFilenameOnFilestore($this); +	} + +	/* +	 * Return the size of the filestore associated with this file +	 * +	 */ +	public function getFilestoreSize($prefix='',$container_guid=0) { +		if (!$container_guid) { +			$container_guid = $this->container_guid; +		} +		$fs = $this->getFilestore(); +		return $fs->getSize($prefix,$container_guid); +	} + +	/** +	 * Get the mime type of the file. +	 */ +	public function getMimeType() { +		if ($this->mimetype) { +			return $this->mimetype; +		} + +		// @todo Guess mimetype if not here +	} + +	/** +	 * Set the mime type of the file. +	 * +	 * @param $mimetype The mimetype +	 */ +	public function setMimeType($mimetype) { +		return $this->mimetype = $mimetype; +	} + +	/** +	 * Set the optional file description. +	 * +	 * @param string $description The description. +	 */ +	public function setDescription($description) { +		$this->description = $description; +	} + +	/** +	 * Open the file with the given mode +	 * +	 * @param string $mode Either read/write/append +	 */ +	public function open($mode) { +		if (!$this->getFilename()) { +			throw new IOException(elgg_echo('IOException:MissingFileName')); +		} + +		// See if file has already been saved +		// seek on datastore, parameters and name? + +		// Sanity check +		if ( +			($mode!="read") && +			($mode!="write") && +			($mode!="append") +		) { +			throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:UnrecognisedFileMode'), $mode)); +		} + +		// Get the filestore +		$fs = $this->getFilestore(); + +		// Ensure that we save the file details to object store +		//$this->save(); + +		// Open the file handle +		$this->handle = $fs->open($this, $mode); + +		return $this->handle; +	} + +	/** +	 * Write some data. +	 * +	 * @param string $data The data +	 */ +	public function write($data) { +		$fs = $this->getFilestore(); + +		return $fs->write($this->handle, $data); +	} + +	/** +	 * Read some data. +	 * +	 * @param int $length Amount to read. +	 * @param int $offset The offset to start from. +	 */ +	public function read($length, $offset = 0) { +		$fs = $this->getFilestore(); + +		return $fs->read($this->handle, $length, $offset); +	} + +	/** +	 * Gets the full contents of this file. +	 * +	 * @return mixed The file contents. +	 */ +	public function grabFile() { +		$fs = $this->getFilestore(); +		return $fs->grabFile($this); +	} + +	/** +	 * Close the file and commit changes +	 */ +	public function close() { +		$fs = $this->getFilestore(); + +		if ($fs->close($this->handle)) { +			$this->handle = NULL; + +			return true; +		} + +		return false; +	} + +	/** +	 * Delete this file. +	 */ +	public function delete() { +		$fs = $this->getFilestore(); +		if ($fs->delete($this)) { +			return parent::delete(); +		} +	} + +	/** +	 * Seek a position in the file. +	 * +	 * @param int $position +	 */ +	public function seek($position) { +		$fs = $this->getFilestore(); + +		return $fs->seek($this->handle, $position); +	} + +	/** +	 * Return the current position of the file. +	 * +	 * @return int The file position +	 */ +	public function tell() { +		$fs = $this->getFilestore(); + +		return $fs->tell($this->handle); +	} + +	/** +	 * Return the size of the file in bytes. +	 */ +	public function size() { +		return $this->filestore->getFileSize($this); +	} + +	/** +	 * Return a boolean value whether the file handle is at the end of the file +	 */ +	public function eof() { +		$fs = $this->getFilestore(); + +		return $fs->eof($this->handle); +	} + +	public function exists() { +		$fs = $this->getFilestore(); + +		return $fs->exists($this); +	} + +	/** +	 * Set a filestore. +	 * +	 * @param ElggFilestore $filestore The file store. +	 */ +	public function setFilestore(ElggFilestore $filestore) { +		$this->filestore = $filestore; +	} + +	/** +	 * Return a filestore suitable for saving this file. +	 * This filestore is either a pre-registered filestore, a filestore loaded from metatags saved +	 * along side this file, or the system default. +	 */ +	protected function getFilestore() { +		// Short circuit if already set. +		if ($this->filestore) { +			return $this->filestore; +		} + +		// If filestore meta set then retrieve filestore +		// @todo Better way of doing this? +		$metas = get_metadata_for_entity($this->guid); +		$parameters = array(); +		if (is_array($metas)) { +			foreach ($metas as $meta) { +				if (strpos($meta->name, "filestore::")!==false) { +					// Filestore parameter tag +					$comp = explode("::", $meta->name); +					$name = $comp[1]; + +					$parameters[$name] = $meta->value; +				} +			} +		} + +		if (isset($parameters['filestore'])) { +			if (!class_exists($parameters['filestore'])) { +				$msg = sprintf(elgg_echo('ClassNotFoundException:NotFoundNotSavedWithFile'), +								$parameters['filestore'], +								$this->guid); +				throw new ClassNotFoundException($msg); +			} + +			// Create new filestore object +			$this->filestore = new $parameters['filestore'](); + +			$this->filestore->setParameters($parameters); +		} else { +			// @todo - should we log error if filestore not set +		} + + +		// if still nothing then set filestore to default +		if (!$this->filestore) { +			$this->filestore = get_default_filestore(); +		} + +		return $this->filestore; +	} + +	public function save() { +		if (!parent::save()) { +			return false; +		} + +		// Save datastore metadata +		$params = $this->filestore->getParameters(); +		foreach ($params as $k => $v) { +			$this->setMetaData("filestore::$k", $v); +		} + +		// Now make a note of the filestore class +		$this->setMetaData("filestore::filestore", get_class($this->filestore)); + +		return true; +	} +} diff --git a/engine/classes/ElggFileCache.php b/engine/classes/ElggFileCache.php new file mode 100644 index 000000000..b8989a935 --- /dev/null +++ b/engine/classes/ElggFileCache.php @@ -0,0 +1,164 @@ +<?php
 +/**
 + * ElggFileCache
 + * Store cached data in a file store.
 + *
 + * @author Curverider Ltd <info@elgg.com>
 + * @package Elgg
 + * @subpackage API
 + */
 +class ElggFileCache extends ElggCache {
 +	/**
 +	 * Set the Elgg cache.
 +	 *
 +	 * @param string $cache_path The cache path.
 +	 * @param int $max_age Maximum age in seconds, 0 if no limit.
 +	 * @param int $max_size Maximum size of cache in seconds, 0 if no limit.
 +	 */
 +	function __construct($cache_path, $max_age = 0, $max_size = 0) {
 +		$this->set_variable("cache_path", $cache_path);
 +		$this->set_variable("max_age", $max_age);
 +		$this->set_variable("max_size", $max_size);
 +
 +		if ($cache_path=="") {
 +			throw new ConfigurationException(elgg_echo('ConfigurationException:NoCachePath'));
 +		}
 +	}
 +
 +	/**
 +	 * Create and return a handle to a file.
 +	 *
 +	 * @param string $filename
 +	 * @param string $rw
 +	 */
 +	protected function create_file($filename, $rw = "rb") {
 +		// Create a filename matrix
 +		$matrix = "";
 +		$depth = strlen($filename);
 +		if ($depth > 5) {
 +			$depth = 5;
 +		}
 +
 +		// Create full path
 +		$path = $this->get_variable("cache_path") . $matrix;
 +		if (!is_dir($path)) {
 +			mkdir($path, 0700, true);
 +		}
 +
 +		// Open the file
 +		if ((!file_exists($path . $filename)) && ($rw=="rb")) {
 +			return false;
 +		}
 +
 +		return fopen($path . $filename, $rw);
 +	}
 +
 +	/**
 +	 * Create a sanitised filename for the file.
 +	 *
 +	 * @param string $filename
 +	 */
 +	protected function sanitise_filename($filename) {
 +		// @todo : Writeme
 +
 +		return $filename;
 +	}
 +
 +	/**
 +	 * Save a key
 +	 *
 +	 * @param string $key
 +	 * @param string $data
 +	 * @return boolean
 +	 */
 +	public function save($key, $data) {
 +		$f = $this->create_file($this->sanitise_filename($key), "wb");
 +		if ($f) {
 +			$result = fwrite($f, $data);
 +			fclose($f);
 +
 +			return $result;
 +		}
 +
 +		return false;
 +	}
 +
 +	/**
 +	 * Load a key
 +	 *
 +	 * @param string $key
 +	 * @param int $offset
 +	 * @param int $limit
 +	 * @return string
 +	 */
 +	public function load($key, $offset = 0, $limit = null) {
 +		$f = $this->create_file($this->sanitise_filename($key));
 +		if ($f) {
 +			//fseek($f, $offset);
 +			if (!$limit) {
 +				$limit = -1;
 +			}
 +			$data = stream_get_contents($f, $limit, $offset);
 +
 +			fclose($f);
 +
 +			return $data;
 +		}
 +
 +		return false;
 +	}
 +
 +	/**
 +	 * Invalidate a given key.
 +	 *
 +	 * @param string $key
 +	 * @return bool
 +	 */
 +	public function delete($key) {
 +		$dir = $this->get_variable("cache_path");
 +		
 +		if (file_exists($dir.$key)) {
 +			return unlink($dir.$key);
 +		}
 +		return TRUE;
 +	}
 +
 +	public function clear() {
 +		// @todo writeme
 +	}
 +
 +	public function __destruct() {
 +		// @todo Check size and age, clean up accordingly
 +		$size = 0;
 +		$dir = $this->get_variable("cache_path");
 +
 +		// Short circuit if both size and age are unlimited
 +		if (($this->get_variable("max_age")==0) && ($this->get_variable("max_size")==0)) {
 +			return;
 +		}
 +
 +		$exclude = array(".","..");
 +
 +		$files = scandir($dir);
 +		if (!$files) {
 +			throw new IOException(sprintf(elgg_echo('IOException:NotDirectory'), $dir));
 +		}
 +
 +		// Perform cleanup
 +		foreach ($files as $f) {
 +			if (!in_array($f, $exclude)) {
 +				$stat = stat($dir.$f);
 +
 +				// Add size
 +				$size .= $stat['size'];
 +
 +				// Is this older than my maximum date?
 +				if (($this->get_variable("max_age")>0) && (time() - $stat['mtime'] > $this->get_variable("max_age"))) {
 +					unlink($dir.$f);
 +				}
 +
 +				// @todo Size
 +			}
 +		}
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggFilestore.php b/engine/classes/ElggFilestore.php new file mode 100644 index 000000000..3311842a9 --- /dev/null +++ b/engine/classes/ElggFilestore.php @@ -0,0 +1,115 @@ +<?php
 +/**
 + * @class ElggFilestore
 + * This class defines the interface for all elgg data repositories.
 + * @author Curverider Ltd
 + */
 +abstract class ElggFilestore {
 +	/**
 +	 * Attempt to open the file $file for storage or writing.
 +	 *
 +	 * @param ElggFile $file
 +	 * @param string $mode "read", "write", "append"
 +	 * @return mixed A handle to the opened file or false on error.
 +	 */
 +	abstract public function open(ElggFile $file, $mode);
 +
 +	/**
 +	 * Write data to a given file handle.
 +	 *
 +	 * @param mixed $f The file handle - exactly what this is depends on the file system
 +	 * @param string $data The binary string of data to write
 +	 * @return int Number of bytes written.
 +	 */
 +	abstract public function write($f, $data);
 +
 +	/**
 +	 * Read data from a filestore.
 +	 *
 +	 * @param mixed $f The file handle
 +	 * @param int $length Length in bytes to read.
 +	 * @param int $offset The optional offset.
 +	 * @return mixed String of data or false on error.
 +	 */
 +	abstract public function read($f, $length, $offset = 0);
 +
 +	/**
 +	 * Seek a given position within a file handle.
 +	 *
 +	 * @param mixed $f The file handle.
 +	 * @param int $position The position.
 +	 */
 +	abstract public function seek($f, $position);
 +
 +	/**
 +	 * Return a whether the end of a file has been reached.
 +	 *
 +	 * @param mixed $f The file handle.
 +	 * @return boolean
 +	 */
 +	abstract public function eof($f);
 +
 +	/**
 +	 * Return the current position in an open file.
 +	 *
 +	 * @param mixed $f The file handle.
 +	 * @return int
 +	 */
 +	abstract public function tell($f);
 +
 +	/**
 +	 * Close a given file handle.
 +	 *
 +	 * @param mixed $f
 +	 */
 +	abstract public function close($f);
 +
 +	/**
 +	 * Delete the file associated with a given file handle.
 +	 *
 +	 * @param ElggFile $file
 +	 */
 +	abstract public function delete(ElggFile $file);
 +
 +	/**
 +	 * Return the size in bytes for a given file.
 +	 *
 +	 * @param ElggFile $file
 +	 */
 +	abstract public function getFileSize(ElggFile $file);
 +
 +	/**
 +	 * Return the filename of a given file as stored on the filestore.
 +	 *
 +	 * @param ElggFile $file
 +	 */
 +	abstract public function getFilenameOnFilestore(ElggFile $file);
 +
 +	/**
 +	 * Get the filestore's creation parameters as an associative array.
 +	 * Used for serialisation and for storing the creation details along side a file object.
 +	 *
 +	 * @return array
 +	 */
 +	abstract public function getParameters();
 +
 +	/**
 +	 * Set the parameters from the associative array produced by $this->getParameters().
 +	 */
 +	abstract public function setParameters(array $parameters);
 +
 +	/**
 +	 * Get the contents of the whole file.
 +	 *
 +	 * @param mixed $file The file handle.
 +	 * @return mixed The file contents.
 +	 */
 +	abstract public function grabFile(ElggFile $file);
 +
 +	/**
 +	 * Return whether a file physically exists or not.
 +	 *
 +	 * @param ElggFile $file
 +	 */
 +	abstract public function exists(ElggFile $file);
 +}
\ No newline at end of file diff --git a/engine/classes/ElggGroup.php b/engine/classes/ElggGroup.php new file mode 100644 index 000000000..9713ca39e --- /dev/null +++ b/engine/classes/ElggGroup.php @@ -0,0 +1,299 @@ +<?php
 +
 +/**
 + * @class ElggGroup Class representing a container for other elgg entities.
 + * @author Curverider Ltd
 + */
 +class ElggGroup extends ElggEntity
 +	implements Friendable {
 +
 +	protected function initialise_attributes() {
 +		parent::initialise_attributes();
 +
 +		$this->attributes['type'] = "group";
 +		$this->attributes['name'] = "";
 +		$this->attributes['description'] = "";
 +		$this->attributes['tables_split'] = 2;
 +	}
 +
 +	/**
 +	 * Construct a new user entity, optionally from a given id value.
 +	 *
 +	 * @param mixed $guid If an int, load that GUID.
 +	 * 	If a db row then will attempt to load the rest of the data.
 +	 * @throws Exception if there was a problem creating the user.
 +	 */
 +	function __construct($guid = null) {
 +		$this->initialise_attributes();
 +
 +		if (!empty($guid)) {
 +			// Is $guid is a DB row - either a entity row, or a user table row.
 +			if ($guid instanceof stdClass) {
 +				// Load the rest
 +				if (!$this->load($guid->guid)) {
 +					throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid->guid));
 +				}
 +			}
 +			// Is $guid is an ElggGroup? Use a copy constructor
 +			else if ($guid instanceof ElggGroup) {
 +				elgg_deprecated_notice('This type of usage of the ElggGroup constructor was deprecated. Please use the clone method.', 1.7);
 +
 +				foreach ($guid->attributes as $key => $value) {
 +					$this->attributes[$key] = $value;
 +				}
 +			}
 +			// Is this is an ElggEntity but not an ElggGroup = ERROR!
 +			else if ($guid instanceof ElggEntity) {
 +				throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggGroup'));
 +			}
 +			// We assume if we have got this far, $guid is an int
 +			else if (is_numeric($guid)) {
 +				if (!$this->load($guid)) {
 +					throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid));
 +				}
 +			}
 +
 +			else {
 +				throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * Add an ElggObject to this group.
 +	 *
 +	 * @param ElggObject $object The object.
 +	 * @return bool
 +	 */
 +	public function addObjectToGroup(ElggObject $object) {
 +		return add_object_to_group($this->getGUID(), $object->getGUID());
 +	}
 +
 +	/**
 +	 * Remove an object from the containing group.
 +	 *
 +	 * @param int $guid The guid of the object.
 +	 * @return bool
 +	 */
 +	public function removeObjectFromGroup($guid) {
 +		return remove_object_from_group($this->getGUID(), $guid);
 +	}
 +
 +	public function get($name) {
 +		if ($name == 'username') {
 +			return 'group:' . $this->getGUID();
 +		}
 +		return parent::get($name);
 +	}
 +
 +/**
 + * Start friendable compatibility block:
 + *
 + * 	public function addFriend($friend_guid);
 +	public function removeFriend($friend_guid);
 +	public function isFriend();
 +	public function isFriendsWith($user_guid);
 +	public function isFriendOf($user_guid);
 +	public function getFriends($subtype = "", $limit = 10, $offset = 0);
 +	public function getFriendsOf($subtype = "", $limit = 10, $offset = 0);
 +	public function getObjects($subtype="", $limit = 10, $offset = 0);
 +	public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0);
 +	public function countObjects($subtype = "");
 + */
 +
 +	/**
 +	 * For compatibility with Friendable
 +	 */
 +	public function addFriend($friend_guid) {
 +		return $this->join(get_entity($friend_guid));
 +	}
 +
 +	/**
 +	 * For compatibility with Friendable
 +	 */
 +	public function removeFriend($friend_guid) {
 +		return $this->leave(get_entity($friend_guid));
 +	}
 +
 +	/**
 +	 * For compatibility with Friendable
 +	 */
 +	public function isFriend() {
 +		return $this->isMember();
 +	}
 +
 +	/**
 +	 * For compatibility with Friendable
 +	 */
 +	public function isFriendsWith($user_guid) {
 +		return $this->isMember($user_guid);
 +	}
 +
 +	/**
 +	 * For compatibility with Friendable
 +	 */
 +	public function isFriendOf($user_guid) {
 +		return $this->isMember($user_guid);
 +	}
 +
 +	/**
 +	 * For compatibility with Friendable
 +	 */
 +	public function getFriends($subtype = "", $limit = 10, $offset = 0) {
 +		return get_group_members($this->getGUID(), $limit, $offset);
 +	}
 +
 +	/**
 +	 * For compatibility with Friendable
 +	 */
 +	public function getFriendsOf($subtype = "", $limit = 10, $offset = 0) {
 +		return get_group_members($this->getGUID(), $limit, $offset);
 +	}
 +
 +	/**
 +	 * Get objects contained in this group.
 +	 *
 +	 * @param string $subtype
 +	 * @param int $limit
 +	 * @param int $offset
 +	 * @return mixed
 +	 */
 +	public function getObjects($subtype="", $limit = 10, $offset = 0) {
 +		return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);
 +	}
 +
 +	/**
 +	 * For compatibility with Friendable
 +	 */
 +	public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) {
 +		return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);
 +	}
 +
 +	/**
 +	 * For compatibility with Friendable
 +	 */
 +	public function countObjects($subtype = "") {
 +		return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", 10, 0, true);
 +	}
 +
 +/**
 + * End friendable compatibility block
 + */
 +
 +	/**
 +	 * Get a list of group members.
 +	 *
 +	 * @param int $limit
 +	 * @param int $offset
 +	 * @return mixed
 +	 */
 +	public function getMembers($limit = 10, $offset = 0, $count = false) {
 +		return get_group_members($this->getGUID(), $limit, $offset, 0 , $count);
 +	}
 +
 +	/**
 +	 * Returns whether the current group is public membership or not.
 +	 * @return bool
 +	 */
 +	public function isPublicMembership() {
 +		if ($this->membership == ACCESS_PUBLIC) {
 +			return true;
 +		}
 +
 +		return false;
 +	}
 +
 +	/**
 +	 * Return whether a given user is a member of this group or not.
 +	 *
 +	 * @param ElggUser $user The user
 +	 * @return bool
 +	 */
 +	public function isMember($user = 0) {
 +		if (!($user instanceof ElggUser)) {
 +			$user = get_loggedin_user();
 +		}
 +		if (!($user instanceof ElggUser)) {
 +			return false;
 +		}
 +		return is_group_member($this->getGUID(), $user->getGUID());
 +	}
 +
 +	/**
 +	 * Join an elgg user to this group.
 +	 *
 +	 * @param ElggUser $user
 +	 * @return bool
 +	 */
 +	public function join(ElggUser $user) {
 +		return join_group($this->getGUID(), $user->getGUID());
 +	}
 +
 +	/**
 +	 * Remove a user from the group.
 +	 *
 +	 * @param ElggUser $user
 +	 */
 +	public function leave(ElggUser $user) {
 +		return leave_group($this->getGUID(), $user->getGUID());
 +	}
 +
 +	/**
 +	 * Override the load function.
 +	 * This function will ensure that all data is loaded (were possible), so
 +	 * if only part of the ElggGroup is loaded, it'll load the rest.
 +	 *
 +	 * @param int $guid
 +	 */
 +	protected function load($guid) {
 +		// Test to see if we have the generic stuff
 +		if (!parent::load($guid)) {
 +			return false;
 +		}
 +
 +		// Check the type
 +		if ($this->attributes['type']!='group') {
 +			throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, get_class()));
 +		}
 +
 +		// Load missing data
 +		$row = get_group_entity_as_row($guid);
 +		if (($row) && (!$this->isFullyLoaded())) {
 +			// If $row isn't a cached copy then increment the counter
 +			$this->attributes['tables_loaded'] ++;
 +		}
 +
 +		// Now put these into the attributes array as core values
 +		$objarray = (array) $row;
 +		foreach($objarray as $key => $value) {
 +			$this->attributes[$key] = $value;
 +		}
 +
 +		return true;
 +	}
 +
 +	/**
 +	 * Override the save function.
 +	 */
 +	public function save() {
 +		// Save generic stuff
 +		if (!parent::save()) {
 +			return false;
 +		}
 +
 +		// Now save specific stuff
 +		return create_group_entity($this->get('guid'), $this->get('name'), $this->get('description'));
 +	}
 +
 +	// EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Return an array of fields which can be exported.
 +	 */
 +	public function getExportableValues() {
 +		return array_merge(parent::getExportableValues(), array(
 +			'name',
 +			'description',
 +		));
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggHMACCache.php b/engine/classes/ElggHMACCache.php new file mode 100644 index 000000000..8c50d7dfb --- /dev/null +++ b/engine/classes/ElggHMACCache.php @@ -0,0 +1,94 @@ +<?php
 +/**
 + * ElggHMACCache
 + * Store cached data in a temporary database, only used by the HMAC stuff.
 + *
 + * @author Curverider Ltd <info@elgg.com>
 + * @package Elgg
 + * @subpackage API
 + */
 +class ElggHMACCache extends ElggCache {
 +	/**
 +	 * Set the Elgg cache.
 +	 *
 +	 * @param int $max_age Maximum age in seconds, 0 if no limit.
 +	 */
 +	function __construct($max_age = 0) {
 +		$this->set_variable("max_age", $max_age);
 +	}
 +
 +	/**
 +	 * Save a key
 +	 *
 +	 * @param string $key
 +	 * @param string $data
 +	 * @return boolean
 +	 */
 +	public function save($key, $data) {
 +		global $CONFIG;
 +
 +		$key = sanitise_string($key);
 +		$time = time();
 +
 +		return insert_data("INSERT into {$CONFIG->dbprefix}hmac_cache (hmac, ts) VALUES ('$key', '$time')");
 +	}
 +
 +	/**
 +	 * Load a key
 +	 *
 +	 * @param string $key
 +	 * @param int $offset
 +	 * @param int $limit
 +	 * @return string
 +	 */
 +	public function load($key, $offset = 0, $limit = null) {
 +		global $CONFIG;
 +
 +		$key = sanitise_string($key);
 +
 +		$row = get_data_row("SELECT * from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
 +		if ($row) {
 +			return $row->hmac;
 +		}
 +
 +		return false;
 +	}
 +
 +	/**
 +	 * Invalidate a given key.
 +	 *
 +	 * @param string $key
 +	 * @return bool
 +	 */
 +	public function delete($key) {
 +		global $CONFIG;
 +
 +		$key = sanitise_string($key);
 +
 +		return delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
 +	}
 +
 +	/**
 +	 * Clear out all the contents of the cache.
 +	 *
 +	 * Not currently implemented in this cache type.
 +	 */
 +	public function clear() {
 +		return true;
 +	}
 +
 +	/**
 +	 * Clean out old stuff.
 +	 *
 +	 */
 +	public function __destruct() {
 +		global $CONFIG;
 +
 +		$time = time();
 +		$age = (int)$this->get_variable("max_age");
 +
 +		$expires = $time-$age;
 +
 +		delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where ts<$expires");
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggMemcache.php b/engine/classes/ElggMemcache.php new file mode 100644 index 000000000..5e898c26a --- /dev/null +++ b/engine/classes/ElggMemcache.php @@ -0,0 +1,151 @@ +<?php
 +/**
 + * Memcache wrapper class.
 + * @author Curverider Ltd <info@elgg.com>
 + */
 +class ElggMemcache extends ElggSharedMemoryCache {
 +	/**
 +	 * Minimum version of memcached needed to run
 +	 *
 +	 */
 +	private static $MINSERVERVERSION = '1.1.12';
 +
 +	/**
 +	 * Memcache object
 +	 */
 +	private $memcache;
 +
 +	/**
 +	 * Expiry of saved items (default timeout after a day to prevent anything getting too stale)
 +	 */
 +	private $expires = 86400;
 +
 +	/**
 +	 * The version of memcache running
 +	 */
 +	private $version = 0;
 +
 +	/**
 +	 * Connect to memcache.
 +	 *
 +	 * @param string $cache_id The namespace for this cache to write to - note, namespaces of the same name are shared!
 +	 */
 +	function __construct($namespace = 'default') {
 +		global $CONFIG;
 +
 +		$this->setNamespace($namespace);
 +
 +		// Do we have memcache?
 +		if (!class_exists('Memcache')) {
 +			throw new ConfigurationException(elgg_echo('memcache:notinstalled'));
 +		}
 +
 +		// Create memcache object
 +		$this->memcache	= new Memcache;
 +
 +		// Now add servers
 +		if (!$CONFIG->memcache_servers) {
 +			throw new ConfigurationException(elgg_echo('memcache:noservers'));
 +		}
 +
 +		if (is_callable($this->memcache, 'addServer')) {
 +			foreach ($CONFIG->memcache_servers as $server) {
 +				if (is_array($server)) {
 +					$this->memcache->addServer(
 +						$server[0],
 +						isset($server[1]) ? $server[1] : 11211,
 +						isset($server[2]) ? $server[2] : true,
 +						isset($server[3]) ? $server[3] : null,
 +						isset($server[4]) ? $server[4] : 1,
 +						isset($server[5]) ? $server[5] : 15,
 +						isset($server[6]) ? $server[6] : true
 +					);
 +
 +				} else {
 +					$this->memcache->addServer($server, 11211);
 +				}
 +			}
 +		} else {
 +			elgg_log(elgg_echo('memcache:noaddserver'), 'ERROR');
 +
 +			$server = $CONFIG->memcache_servers[0];
 +			if (is_array($server)) {
 +				$this->memcache->connect($server[0], $server[1]);
 +			} else {
 +				$this->memcache->addServer($server, 11211);
 +			}
 +		}
 +
 +		// Get version
 +		$this->version = $this->memcache->getversion();
 +		if (version_compare($this->version, ElggMemcache::$MINSERVERVERSION, '<')) {
 +			throw new ConfigurationException(sprintf(elgg_echo('memcache:versiontoolow'), ElggMemcache::$MINSERVERVERSION, $this->version));
 +		}
 +
 +		// Set some defaults
 +		if (isset($CONFIG->memcache_expires)) {
 +			$this->expires = $CONFIG->memcache_expires;
 +		}
 +	}
 +
 +	/**
 +	 * Set the default expiry.
 +	 *
 +	 * @param int $expires The lifetime as a unix timestamp or time from now. Defaults forever.
 +	 */
 +	public function setDefaultExpiry($expires = 0) {
 +		$this->expires = $expires;
 +	}
 +
 +	/**
 +	 * Combine a key with the namespace.
 +	 * Memcache can only accept <250 char key. If the given key is too long it is shortened.
 +	 *
 +	 * @param string $key The key
 +	 * @return string The new key.
 +	 */
 +	private function make_memcache_key($key) {
 +		$prefix = $this->getNamespace() . ":";
 +
 +		if (strlen($prefix.$key)> 250) {
 +			$key = md5($key);
 +		}
 +
 +		return $prefix.$key;
 +	}
 +
 +	public function save($key, $data) {
 +		$key = $this->make_memcache_key($key);
 +
 +		$result = $this->memcache->set($key, $data, null, $this->expires);
 +		if (!$result) {
 +			elgg_log("MEMCACHE: FAILED TO SAVE $key", 'ERROR');
 +		}
 +
 +		return $result;
 +	}
 +
 +	public function load($key, $offset = 0, $limit = null) {
 +		$key = $this->make_memcache_key($key);
 +
 +		$result = $this->memcache->get($key);
 +		if (!$result) {
 +			elgg_log("MEMCACHE: FAILED TO LOAD $key", 'ERROR');
 +		}
 +
 +		return $result;
 +	}
 +
 +	public function delete($key) {
 +		$key = $this->make_memcache_key($key);
 +
 +		return $this->memcache->delete($key, 0);
 +	}
 +
 +	public function clear() {
 +		// DISABLE clearing for now - you must use delete on a specific key.
 +		return true;
 +
 +		// @todo Namespaces as in #532
 +	}
 +}
 diff --git a/engine/classes/ElggMetadata.php b/engine/classes/ElggMetadata.php new file mode 100644 index 000000000..631b73c8f --- /dev/null +++ b/engine/classes/ElggMetadata.php @@ -0,0 +1,114 @@ +<?php
 +
 +/**
 + * ElggMetadata
 + * This class describes metadata that can be attached to ElggEntities.
 + *
 + * @author Curverider Ltd <info@elgg.com>
 + * @package Elgg
 + * @subpackage Core
 + */
 +class ElggMetadata extends ElggExtender {
 +	/**
 +	 * Construct a new site object, optionally from a given id value or row.
 +	 *
 +	 * @param mixed $id
 +	 */
 +	function __construct($id = null) {
 +		$this->attributes = array();
 +
 +		if (!empty($id)) {
 +			// Create from db row
 +			if ($id instanceof stdClass) {
 +				$metadata = $id;
 +			} else {
 +				$metadata = get_metadata($id);
 +			}
 +
 +			if ($metadata) {
 +				$objarray = (array) $metadata;
 +				foreach($objarray as $key => $value) {
 +					$this->attributes[$key] = $value;
 +				}
 +				$this->attributes['type'] = "metadata";
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * Class member get overloading
 +	 *
 +	 * @param string $name
 +	 * @return mixed
 +	 */
 +	function __get($name) {
 +		return $this->get($name);
 +	}
 +
 +	/**
 +	 * Class member set overloading
 +	 *
 +	 * @param string $name
 +	 * @param mixed $value
 +	 * @return mixed
 +	 */
 +	function __set($name, $value) {
 +		return $this->set($name, $value);
 +	}
 +
 +	/**
 +	 * Determines whether or not the user can edit this piece of metadata
 +	 *
 +	 * @return true|false Depending on permissions
 +	 */
 +	function canEdit() {
 +		if ($entity = get_entity($this->get('entity_guid'))) {
 +			return $entity->canEditMetadata($this);
 +		}
 +		return false;
 +	}
 +
 +	/**
 +	 * Save matadata object
 +	 *
 +	 * @return int the metadata object id
 +	 */
 +	function save() {
 +		if ($this->id > 0) {
 +			return update_metadata($this->id, $this->name, $this->value, $this->value_type, $this->owner_guid, $this->access_id);
 +		} else {
 +			$this->id = create_metadata($this->entity_guid, $this->name, $this->value, $this->value_type, $this->owner_guid, $this->access_id);
 +			if (!$this->id) {
 +				throw new IOException(sprintf(elgg_echo('IOException:UnableToSaveNew'), get_class()));
 +			}
 +			return $this->id;
 +		}
 +	}
 +
 +	/**
 +	 * Delete a given metadata.
 +	 */
 +	function delete() {
 +		return delete_metadata($this->id);
 +	}
 +
 +	/**
 +	 * Get a url for this item of metadata.
 +	 *
 +	 * @return string
 +	 */
 +	public function getURL() {
 +		return get_metadata_url($this->id);
 +	}
 +
 +	// SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * For a given ID, return the object associated with it.
 +	 * This is used by the river functionality primarily.
 +	 * This is useful for checking access permissions etc on objects.
 +	 */
 +	public function getObjectFromID($id) {
 +		return get_metadata($id);
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggObject.php b/engine/classes/ElggObject.php new file mode 100644 index 000000000..af67ef3f6 --- /dev/null +++ b/engine/classes/ElggObject.php @@ -0,0 +1,199 @@ +<?php
 +
 +/**
 + * ElggObject
 + * Representation of an "object" in the system.
 + *
 + * @package Elgg
 + * @subpackage Core
 + */
 +class ElggObject extends ElggEntity {
 +	/**
 +	 * Initialise the attributes array.
 +	 * This is vital to distinguish between metadata and base parameters.
 +	 *
 +	 * Place your base parameters here.
 +	 */
 +	protected function initialise_attributes() {
 +		parent::initialise_attributes();
 +
 +		$this->attributes['type'] = "object";
 +		$this->attributes['title'] = "";
 +		$this->attributes['description'] = "";
 +		$this->attributes['tables_split'] = 2;
 +	}
 +
 +	/**
 +	 * Construct a new object entity, optionally from a given id value.
 +	 *
 +	 * @param mixed $guid If an int, load that GUID.
 +	 * 	If a db row then will attempt to load the rest of the data.
 +	 * @throws Exception if there was a problem creating the object.
 +	 */
 +	function __construct($guid = null) {
 +		$this->initialise_attributes();
 +
 +		if (!empty($guid)) {
 +			// Is $guid is a DB row - either a entity row, or a object table row.
 +			if ($guid instanceof stdClass) {
 +				// Load the rest
 +				if (!$this->load($guid->guid)) {
 +					throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid->guid));
 +				}
 +			}
 +
 +			// Is $guid is an ElggObject? Use a copy constructor
 +			else if ($guid instanceof ElggObject) {
 +				elgg_deprecated_notice('This type of usage of the ElggObject constructor was deprecated. Please use the clone method.', 1.7);
 +
 +				foreach ($guid->attributes as $key => $value) {
 +					$this->attributes[$key] = $value;
 +				}
 +			}
 +
 +			// Is this is an ElggEntity but not an ElggObject = ERROR!
 +			else if ($guid instanceof ElggEntity) {
 +				throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggObject'));
 +			}
 +
 +			// We assume if we have got this far, $guid is an int
 +			else if (is_numeric($guid)) {
 +				if (!$this->load($guid)) {
 +					throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid));
 +				}
 +			}
 +
 +			else {
 +				throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * Override the load function.
 +	 * This function will ensure that all data is loaded (were possible), so
 +	 * if only part of the ElggObject is loaded, it'll load the rest.
 +	 *
 +	 * @param int $guid
 +	 * @return true|false
 +	 */
 +	protected function load($guid) {
 +		// Test to see if we have the generic stuff
 +		if (!parent::load($guid)) {
 +			return false;
 +		}
 +
 +		// Check the type
 +		if ($this->attributes['type']!='object') {
 +			throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, get_class()));
 +		}
 +
 +		// Load missing data
 +		$row = get_object_entity_as_row($guid);
 +		if (($row) && (!$this->isFullyLoaded())) {
 +			// If $row isn't a cached copy then increment the counter
 +			$this->attributes['tables_loaded'] ++;
 +		}
 +
 +		// Now put these into the attributes array as core values
 +		$objarray = (array) $row;
 +		foreach($objarray as $key => $value) {
 +			$this->attributes[$key] = $value;
 +		}
 +
 +		return true;
 +	}
 +
 +	/**
 +	 * Override the save function.
 +	 * @return true|false
 +	 */
 +	public function save() {
 +		// Save generic stuff
 +		if (!parent::save()) {
 +			return false;
 +		}
 +
 +		// Now save specific stuff
 +		return create_object_entity($this->get('guid'), $this->get('title'), $this->get('description'), $this->get('container_guid'));
 +	}
 +
 +	/**
 +	 * Get sites that this object is a member of
 +	 *
 +	 * @param string $subtype Optionally, the subtype of result we want to limit to
 +	 * @param int $limit The number of results to return
 +	 * @param int $offset Any indexing offset
 +	 */
 +	function getSites($subtype="", $limit = 10, $offset = 0) {
 +		return get_site_objects($this->getGUID(), $subtype, $limit, $offset);
 +	}
 +
 +	/**
 +	 * Add this object to a particular site
 +	 *
 +	 * @param int $site_guid The guid of the site to add it to
 +	 * @return true|false
 +	 */
 +	function addToSite($site_guid) {
 +		return add_site_object($this->getGUID(), $site_guid);
 +	}
 +
 +	/**
 +	 * Set the container for this object.
 +	 *
 +	 * @param int $container_guid The ID of the container.
 +	 * @return bool
 +	 */
 +	function setContainer($container_guid) {
 +		$container_guid = (int)$container_guid;
 +
 +		return $this->set('container_guid', $container_guid);
 +	}
 +
 +	/**
 +	 * Return the container GUID of this object.
 +	 *
 +	 * @return int
 +	 */
 +	function getContainer() {
 +		return $this->get('container_guid');
 +	}
 +
 +	/**
 +	 * As getContainer(), but returns the whole entity.
 +	 *
 +	 * @return mixed ElggGroup object or false.
 +	 */
 +	function getContainerEntity() {
 +		$result = get_entity($this->getContainer());
 +
 +		if (($result) && ($result instanceof ElggGroup)) {
 +			return $result;
 +		}
 +
 +		return false;
 +	}
 +
 +	/**
 +	 * Get the collections associated with a object.
 +	 *
 +	 * @param string $subtype Optionally, the subtype of result we want to limit to
 +	 * @param int $limit The number of results to return
 +	 * @param int $offset Any indexing offset
 +	 * @return unknown
 +	 */
 +	//public function getCollections($subtype="", $limit = 10, $offset = 0) { get_object_collections($this->getGUID(), $subtype, $limit, $offset); }
 +
 +	// EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Return an array of fields which can be exported.
 +	 */
 +	public function getExportableValues() {
 +		return array_merge(parent::getExportableValues(), array(
 +			'title',
 +			'description',
 +		));
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php new file mode 100644 index 000000000..921665f4d --- /dev/null +++ b/engine/classes/ElggPlugin.php @@ -0,0 +1,56 @@ +<?php
 +/**
 + * @class ElggPlugin Object representing a plugin's settings for a given site.
 + * This class is currently a stub, allowing a plugin to saving settings in an object's metadata for each site.
 + * @author Curverider Ltd
 + */
 +class ElggPlugin extends ElggObject {
 +	protected function initialise_attributes() {
 +		parent::initialise_attributes();
 +
 +		$this->attributes['subtype'] = "plugin";
 +	}
 +
 +	public function __construct($guid = null) {
 +		parent::__construct($guid);
 +	}
 +
 +	/**
 +	 * Override entity get and sets in order to save data to private data store.
 +	 */
 +	public function get($name) {
 +		// See if its in our base attribute
 +		if (isset($this->attributes[$name])) {
 +			return $this->attributes[$name];
 +		}
 +
 +		// No, so see if its in the private data store.
 +		// get_private_setting() returns false if it doesn't exist
 +		$meta = get_private_setting($this->guid, $name);
 +
 +		if ($meta === false) {
 +			// Can't find it, so return null
 +			return NULL;
 +		}
 +
 +		return $meta;
 +	}
 +
 +	/**
 +	 * Override entity get and sets in order to save data to private data store.
 +	 */
 +	public function set($name, $value) {
 +		if (array_key_exists($name, $this->attributes)) {
 +			// Check that we're not trying to change the guid!
 +			if ((array_key_exists('guid', $this->attributes)) && ($name=='guid')) {
 +				return false;
 +			}
 +
 +			$this->attributes[$name] = $value;
 +		} else {
 +			return set_private_setting($this->guid, $name, $value);
 +		}
 +
 +		return true;
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggRelationship.php b/engine/classes/ElggRelationship.php new file mode 100644 index 000000000..4d14941a9 --- /dev/null +++ b/engine/classes/ElggRelationship.php @@ -0,0 +1,287 @@ +<?php
 +
 +/**
 + * Relationship class.
 + *
 + * @author Curverider Ltd
 + * @package Elgg
 + * @subpackage Core
 + */
 +class ElggRelationship implements
 +	Importable,
 +	Exportable,
 +	Loggable,	// Can events related to this object class be logged
 +	Iterator,	// Override foreach behaviour
 +	ArrayAccess // Override for array access
 +	{
 +	/**
 +	 * This contains the site's main properties (id, etc)
 +	 * @var array
 +	 */
 +	protected $attributes;
 +
 +	/**
 +	 * Construct a new site object, optionally from a given id value or row.
 +	 *
 +	 * @param mixed $id
 +	 */
 +	function __construct($id = null) {
 +		$this->attributes = array();
 +
 +		if (!empty($id)) {
 +			if ($id instanceof stdClass) {
 +				$relationship = $id; // Create from db row
 +			} else {
 +				$relationship = get_relationship($id);
 +			}
 +
 +			if ($relationship) {
 +				$objarray = (array) $relationship;
 +				foreach($objarray as $key => $value) {
 +					$this->attributes[$key] = $value;
 +				}
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * Class member get overloading
 +	 *
 +	 * @param string $name
 +	 * @return mixed
 +	 */
 +	function __get($name) {
 +		if (isset($this->attributes[$name])) {
 +			return $this->attributes[$name];
 +		}
 +
 +		return null;
 +	}
 +
 +	/**
 +	 * Class member set overloading
 +	 *
 +	 * @param string $name
 +	 * @param mixed $value
 +	 * @return mixed
 +	 */
 +	function __set($name, $value) {
 +		$this->attributes[$name] = $value;
 +		return true;
 +	}
 +
 +	/**
 +	 * Save the relationship
 +	 *
 +	 * @return int the relationship id
 +	 */
 +	public function save() {
 +		if ($this->id > 0) {
 +			delete_relationship($this->id);
 +		}
 +
 +		$this->id = add_entity_relationship($this->guid_one, $this->relationship, $this->guid_two);
 +		if (!$this->id) {
 +			throw new IOException(sprintf(elgg_echo('IOException:UnableToSaveNew'), get_class()));
 +		}
 +
 +		return $this->id;
 +	}
 +
 +	/**
 +	 * Delete a given relationship.
 +	 */
 +	public function delete() {
 +		return delete_relationship($this->id);
 +	}
 +
 +	/**
 +	 * Get a URL for this relationship.
 +	 *
 +	 * @return string
 +	 */
 +	public function getURL() {
 +		return get_relationship_url($this->id);
 +	}
 +
 +	// EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Return an array of fields which can be exported.
 +	 */
 +	public function getExportableValues() {
 +		return array(
 +			'id',
 +			'guid_one',
 +			'relationship',
 +			'guid_two'
 +		);
 +	}
 +
 +	/**
 +	 * Export this relationship
 +	 *
 +	 * @return array
 +	 */
 +	public function export() {
 +		$uuid = get_uuid_from_object($this);
 +		$relationship = new ODDRelationship(
 +			guid_to_uuid($this->guid_one),
 +			$this->relationship,
 +			guid_to_uuid($this->guid_two)
 +		);
 +
 +		$relationship->setAttribute('uuid', $uuid);
 +
 +		return $relationship;
 +	}
 +
 +	// IMPORTABLE INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Import a relationship
 +	 *
 +	 * @param array $data
 +	 * @param int $version
 +	 * @return ElggRelationship
 +	 * @throws ImportException
 +	 */
 +	public function import(ODD $data) {
 +		if (!($element instanceof ODDRelationship)) {
 +			throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnexpectedODDClass'));
 +		}
 +
 +		$uuid_one = $data->getAttribute('uuid1');
 +		$uuid_two = $data->getAttribute('uuid2');
 +
 +		// See if this entity has already been imported, if so then we need to link to it
 +		$entity1 = get_entity_from_uuid($uuid_one);
 +		$entity2 = get_entity_from_uuid($uuid_two);
 +		if (($entity1) && ($entity2)) {
 +			// Set the item ID
 +			$this->attributes['guid_one'] = $entity1->getGUID();
 +			$this->attributes['guid_two'] = $entity2->getGUID();
 +
 +			// Map verb to relationship
 +			//$verb = $data->getAttribute('verb');
 +			//$relationship = get_relationship_from_verb($verb);
 +			$relationship = $data->getAttribute('type');
 +
 +			if ($relationship) {
 +				$this->attributes['relationship'] = $relationship;
 +				// save
 +				$result = $this->save();
 +				if (!$result) {
 +					throw new ImportException(sprintf(elgg_echo('ImportException:ProblemSaving'), get_class()));
 +				}
 +
 +				return $this;
 +			}
 +		}
 +	}
 +
 +	// SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Return an identification for the object for storage in the system log.
 +	 * This id must be an integer.
 +	 *
 +	 * @return int
 +	 */
 +	public function getSystemLogID() {
 +		return $this->id;
 +	}
 +
 +	/**
 +	 * Return the class name of the object.
 +	 */
 +	public function getClassName() {
 +		return get_class($this);
 +	}
 +
 +	/**
 +	 * For a given ID, return the object associated with it.
 +	 * This is used by the river functionality primarily.
 +	 * This is useful for checking access permissions etc on objects.
 +	 */
 +	public function getObjectFromID($id) {
 +		return get_relationship($id);
 +	}
 +
 +	/**
 +	 * Return the GUID of the owner of this object.
 +	 */
 +	public function getObjectOwnerGUID() {
 +		return $this->owner_guid;
 +	}
 +
 +	/**
 +	 * Return a type of the object - eg. object, group, user, relationship, metadata, annotation etc
 +	 */
 +	public function getType() {
 +		return 'relationship';
 +	}
 +
 +	/**
 +	 * Return a subtype. For metadata & annotations this is the 'name' and for relationship this is the relationship type.
 +	 */
 +	public function getSubtype() {
 +		return $this->relationship;
 +	}
 +
 +	// ITERATOR INTERFACE //////////////////////////////////////////////////////////////
 +	/*
 +	 * This lets an entity's attributes be displayed using foreach as a normal array.
 +	 * Example: http://www.sitepoint.com/print/php5-standard-library
 +	 */
 +
 +	private $valid = FALSE;
 +
 +	function rewind() {
 +		$this->valid = (FALSE !== reset($this->attributes));
 +	}
 +
 +	function current() {
 +		return current($this->attributes);
 +	}
 +
 +	function key() {
 +		return key($this->attributes);
 +	}
 +
 +	function next() {
 +		$this->valid = (FALSE !== next($this->attributes));
 +	}
 +
 +	function valid() {
 +		return $this->valid;
 +	}
 +
 +	// ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
 +	/*
 +	 * This lets an entity's attributes be accessed like an associative array.
 +	 * Example: http://www.sitepoint.com/print/php5-standard-library
 +	 */
 +
 +	function offsetSet($key, $value) {
 +		if ( array_key_exists($key, $this->attributes) ) {
 +			$this->attributes[$key] = $value;
 +		}
 +	}
 +
 +	function offsetGet($key) {
 +		if ( array_key_exists($key, $this->attributes) ) {
 +			return $this->attributes[$key];
 +		}
 +	}
 +
 +	function offsetUnset($key) {
 +		if ( array_key_exists($key, $this->attributes) ) {
 +			$this->attributes[$key] = ""; // Full unsetting is dangerious for our objects
 +		}
 +	}
 +
 +	function offsetExists($offset) {
 +		return array_key_exists($offset, $this->attributes);
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggSession.php b/engine/classes/ElggSession.php new file mode 100644 index 000000000..874502579 --- /dev/null +++ b/engine/classes/ElggSession.php @@ -0,0 +1,95 @@ +<?php
 +/**
 + * Magic session class.
 + * This class is intended to extend the $_SESSION magic variable by providing an API hook
 + * to plug in other values.
 + *
 + * Primarily this is intended to provide a way of supplying "logged in user" details without touching the session
 + * (which can cause problems when accessed server side).
 + *
 + * If a value is present in the session then that value is returned, otherwise a plugin hook 'session:get', '$var' is called,
 + * where $var is the variable being requested.
 + *
 + * Setting values will store variables in the session in the normal way.
 + *
 + * LIMITATIONS: You can not access multidimensional arrays
 + *
 + * This is EXPERIMENTAL.
 + */
 +class ElggSession implements ArrayAccess {
 +	/** Local cache of trigger retrieved variables */
 +	private static $__localcache;
 +
 +	function __isset($key) {
 +		return $this->offsetExists($key);
 +	}
 +
 +	/** Set a value, go straight to session. */
 +	function offsetSet($key, $value) {
 +		$_SESSION[$key] = $value;
 +	}
 +
 +	/**
 +	 * Get a variable from either the session, or if its not in the session attempt to get it from
 +	 * an api call.
 +	 */
 +	function offsetGet($key) {
 +		if (!ElggSession::$__localcache) {
 +			ElggSession::$__localcache = array();
 +		}
 +
 +		if (isset($_SESSION[$key])) {
 +			return $_SESSION[$key];
 +		}
 +
 +		if (isset(ElggSession::$__localcache[$key])) {
 +			return ElggSession::$__localcache[$key];
 +		}
 +
 +		$value = NULL;
 +		$value = trigger_plugin_hook('session:get', $key, NULL, $value);
 +
 +		ElggSession::$__localcache[$key] = $value;
 +
 +		return ElggSession::$__localcache[$key];
 +	}
 +
 +	/**
 +	* Unset a value from the cache and the session.
 +	*/
 +	function offsetUnset($key) {
 +		unset(ElggSession::$__localcache[$key]);
 +		unset($_SESSION[$key]);
 +	}
 +
 +	/**
 +	* Return whether the value is set in either the session or the cache.
 +	*/
 +	function offsetExists($offset) {
 +		if (isset(ElggSession::$__localcache[$offset])) {
 +			return true;
 +		}
 +
 +		if (isset($_SESSION[$offset])) {
 +			return true;
 +		}
 +
 +		if ($this->offsetGet($offset)){
 +			return true;
 +		}
 +	}
 +
 +
 +	// Alias functions
 +	function get($key) {
 +		return $this->offsetGet($key);
 +	}
 +
 +	function set($key, $value) {
 +		return $this->offsetSet($key, $value);
 +	}
 +
 +	function del($key) {
 +		return $this->offsetUnset($key);
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggSharedMemoryCache.php b/engine/classes/ElggSharedMemoryCache.php new file mode 100644 index 000000000..cae78943e --- /dev/null +++ b/engine/classes/ElggSharedMemoryCache.php @@ -0,0 +1,34 @@ +<?php
 +/**
 + * Shared memory cache description.
 + * Extends ElggCache with functions useful to shared memory style caches (static variables, memcache etc)
 + */
 +abstract class ElggSharedMemoryCache extends ElggCache {
 +	/**
 +	 * Namespace variable used to keep various bits of the cache
 +	 * separate.
 +	 *
 +	 * @var string
 +	 */
 +	private $namespace;
 +
 +	/**
 +	 * Set the namespace of this cache.
 +	 * This is useful for cache types (like memcache or static variables) where there is one large
 +	 * flat area of memory shared across all instances of the cache.
 +	 *
 +	 * @param string $namespace
 +	 */
 +	public function setNamespace($namespace = "default") {
 +		$this->namespace = $namespace;
 +	}
 +
 +	/**
 +	 * Get the namespace currently defined.
 +	 *
 +	 * @return string
 +	 */
 +	public function getNamespace() {
 +		return $this->namespace;
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggSite.php b/engine/classes/ElggSite.php new file mode 100644 index 000000000..77e26372e --- /dev/null +++ b/engine/classes/ElggSite.php @@ -0,0 +1,299 @@ +<?php
 +/**
 + * ElggSite
 + * Representation of a "site" in the system.
 + * @author Curverider Ltd <info@elgg.com>
 + * @package Elgg
 + * @subpackage Core
 + */
 +class ElggSite extends ElggEntity {
 +	/**
 +	 * Initialise the attributes array.
 +	 * This is vital to distinguish between metadata and base parameters.
 +	 *
 +	 * Place your base parameters here.
 +	 */
 +	protected function initialise_attributes() {
 +		parent::initialise_attributes();
 +
 +		$this->attributes['type'] = "site";
 +		$this->attributes['name'] = "";
 +		$this->attributes['description'] = "";
 +		$this->attributes['url'] = "";
 +		$this->attributes['tables_split'] = 2;
 +	}
 +
 +	/**
 +	 * Construct a new site object, optionally from a given id value.
 +	 *
 +	 * @param mixed $guid If an int, load that GUID.
 +	 * 	If a db row then will attempt to load the rest of the data.
 +	 * @throws Exception if there was a problem creating the site.
 +	 */
 +	function __construct($guid = null) {
 +		$this->initialise_attributes();
 +
 +		if (!empty($guid)) {
 +			// Is $guid is a DB row - either a entity row, or a site table row.
 +			if ($guid instanceof stdClass) {
 +				// Load the rest
 +				if (!$this->load($guid->guid)) {
 +					throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid->guid));
 +				}
 +			}
 +
 +			// Is $guid is an ElggSite? Use a copy constructor
 +			else if ($guid instanceof ElggSite) {
 +				elgg_deprecated_notice('This type of usage of the ElggSite constructor was deprecated. Please use the clone method.', 1.7);
 +				
 +				foreach ($guid->attributes as $key => $value) {
 +					$this->attributes[$key] = $value;
 +				}
 +			}
 +
 +			// Is this is an ElggEntity but not an ElggSite = ERROR!
 +			else if ($guid instanceof ElggEntity) {
 +				throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggSite'));
 +			}
 +
 +			// See if this is a URL
 +			else if (strpos($guid, "http") !== false) {
 +				$guid = get_site_by_url($guid);
 +				foreach ($guid->attributes as $key => $value) {
 +					$this->attributes[$key] = $value;
 +				}
 +			}
 +
 +			// We assume if we have got this far, $guid is an int
 +			else if (is_numeric($guid)) {
 +				if (!$this->load($guid)) {
 +					throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid));
 +				}
 +			}
 +
 +			else {
 +				throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * Override the load function.
 +	 * This function will ensure that all data is loaded (were possible), so
 +	 * if only part of the ElggSite is loaded, it'll load the rest.
 +	 *
 +	 * @param int $guid
 +	 */
 +	protected function load($guid) {
 +		// Test to see if we have the generic stuff
 +		if (!parent::load($guid)) {
 +			return false;
 +		}
 +
 +		// Check the type
 +		if ($this->attributes['type']!='site') {
 +			throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, get_class()));
 +		}
 +
 +		// Load missing data
 +		$row = get_site_entity_as_row($guid);
 +		if (($row) && (!$this->isFullyLoaded())) {
 +			// If $row isn't a cached copy then increment the counter
 +			$this->attributes['tables_loaded'] ++;
 +		}
 +
 +		// Now put these into the attributes array as core values
 +		$objarray = (array) $row;
 +		foreach($objarray as $key => $value) {
 +			$this->attributes[$key] = $value;
 +		}
 +
 +		return true;
 +	}
 +
 +	/**
 +	 * Override the save function.
 +	 */
 +	public function save() {
 +		// Save generic stuff
 +		if (!parent::save()) {
 +			return false;
 +		}
 +
 +		// Now save specific stuff
 +		return create_site_entity($this->get('guid'), $this->get('name'), $this->get('description'), $this->get('url'));
 +	}
 +
 +	/**
 +	 * Delete this site.
 +	 */
 +	public function delete() {
 +		global $CONFIG;
 +		if ($CONFIG->site->getGUID() == $this->guid) {
 +			throw new SecurityException('SecurityException:deletedisablecurrentsite');
 +		}
 +
 +		return parent::delete();
 +	}
 +
 +	/**
 +	 * Disable override to add safety rail.
 +	 *
 +	 * @param unknown_type $reason
 +	 */
 +	public function disable($reason = "") {
 +		global $CONFIG;
 +
 +		if ($CONFIG->site->getGUID() == $this->guid) {
 +			throw new SecurityException('SecurityException:deletedisablecurrentsite');
 +		}
 +
 +		return parent::disable($reason);
 +	}
 +
 +	/**
 +	 * Return a list of users using this site.
 +	 *
 +	 * @param int $limit
 +	 * @param int $offset
 +	 * @return array of ElggUsers
 +	 */
 +	public function getMembers($limit = 10, $offset = 0) {
 +		get_site_members($this->getGUID(), $limit, $offset);
 +	}
 +
 +	/**
 +	 * Add a user to the site.
 +	 *
 +	 * @param int $user_guid
 +	 */
 +	public function addUser($user_guid) {
 +		return add_site_user($this->getGUID(), $user_guid);
 +	}
 +
 +	/**
 +	 * Remove a site user.
 +	 *
 +	 * @param int $user_guid
 +	 */
 +	public function removeUser($user_guid) {
 +		return remove_site_user($this->getGUID(), $user_guid);
 +	}
 +
 +	/**
 +	 * Get an array of member ElggObjects.
 +	 *
 +	 * @param string $subtype
 +	 * @param int $limit
 +	 * @param int $offset
 +	 */
 +	public function getObjects($subtype="", $limit = 10, $offset = 0) {
 +		get_site_objects($this->getGUID(), $subtype, $limit, $offset);
 +	}
 +
 +	/**
 +	 * Add an object to the site.
 +	 *
 +	 * @param int $user_id
 +	 */
 +	public function addObject($object_guid) {
 +		return add_site_object($this->getGUID(), $object_guid);
 +	}
 +
 +	/**
 +	 * Remove a site user.
 +	 *
 +	 * @param int $user_id
 +	 */
 +	public function removeObject($object_guid) {
 +		return remove_site_object($this->getGUID(), $object_guid);
 +	}
 +
 +	/**
 +	 * Get the collections associated with a site.
 +	 *
 +	 * @param string $type
 +	 * @param int $limit
 +	 * @param int $offset
 +	 * @return unknown
 +	 */
 +	public function getCollections($subtype="", $limit = 10, $offset = 0) {
 +		get_site_collections($this->getGUID(), $subtype, $limit, $offset);
 +	}
 +
 +	// EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Return an array of fields which can be exported.
 +	 */
 +	public function getExportableValues() {
 +		return array_merge(parent::getExportableValues(), array(
 +			'name',
 +			'description',
 +			'url',
 +		));
 +	}
 +	
 +	public function check_walled_garden() {
 +		global $CONFIG;
 +		
 +		if ($CONFIG->walled_garden && !isloggedin()) {
 +			// hook into the index system call at the highest priority
 +			register_plugin_hook('index', 'system', 'elgg_walled_garden_index', 1);
 +			
 +			if (!$this->is_public_page()) {
 +				register_error(elgg_echo('loggedinrequired'));
 +				forward();
 +			}
 +		}
 +	}
 +	
 +	public function is_public_page($url='') {
 +		global $CONFIG;
 +		
 +		if (empty($url)) {
 +			$url = current_page_url();
 +			
 +			// do not check against URL queries
 +			if ($pos = strpos($url, '?')) {
 +				$url = substr($url, 0, $pos);
 +			}
 +		}
 +		
 +		// always allow index page
 +		if ($url == $CONFIG->url) {
 +			return TRUE;
 +		}
 +		
 +		// default public pages
 +		$defaults = array(
 +			'action/login',
 +			'pg/register',
 +			'action/register',
 +			'account/forgotten_password\.php',
 +			'action/user/requestnewpassword',
 +			'pg/resetpassword',
 +			'upgrade\.php',
 +			'xml-rpc\.php',
 +			'mt/mt-xmlrpc\.cgi',
 +			'_css/css\.css',
 +			'_css/js\.php',
 +		);
 +		
 +		// include a hook for plugin authors to include public pages
 +		$plugins = trigger_plugin_hook('public_pages', 'walled_garden', NULL, array());
 +		
 +		// lookup admin-specific public pages
 +		
 +		// allow public pages
 +		foreach (array_merge($defaults, $plugins) as $public) {
 +			$pattern = "`^{$CONFIG->url}$public/*$`i";
 +			if (preg_match($pattern, $url)) {
 +				return TRUE;
 +			}
 +		}
 +		
 +		// non-public page
 +		return FALSE;
 +	}
 +}
 diff --git a/engine/classes/ElggStaticVariableCache.php b/engine/classes/ElggStaticVariableCache.php new file mode 100644 index 000000000..0038862bd --- /dev/null +++ b/engine/classes/ElggStaticVariableCache.php @@ -0,0 +1,66 @@ +<?php
 +/**
 + * ElggStaticVariableCache
 + * Dummy cache which stores values in a static array. Using this makes future replacements to other caching back
 + * ends (eg memcache) much easier.
 + *
 + * @author Curverider Ltd <info@elgg.com>
 + * @package Elgg
 + * @subpackage API
 + */
 +class ElggStaticVariableCache extends ElggSharedMemoryCache {
 +	/**
 +	 * The cache.
 +	 *
 +	 * @var unknown_type
 +	 */
 +	private static $__cache;
 +
 +	/**
 +	 * Create the variable cache.
 +	 *
 +	 * This function creates a variable cache in a static variable in memory, optionally with a given namespace (to avoid overlap).
 +	 *
 +	 * @param string $namespace The namespace for this cache to write to - note, namespaces of the same name are shared!
 +	 */
 +	function __construct($namespace = 'default') {
 +		$this->setNamespace($namespace);
 +		$this->clear();
 +	}
 +
 +	public function save($key, $data) {
 +		$namespace = $this->getNamespace();
 +
 +		ElggStaticVariableCache::$__cache[$namespace][$key] = $data;
 +
 +		return true;
 +	}
 +
 +	public function load($key, $offset = 0, $limit = null) {
 +		$namespace = $this->getNamespace();
 +
 +		if (isset(ElggStaticVariableCache::$__cache[$namespace][$key])) {
 +			return ElggStaticVariableCache::$__cache[$namespace][$key];
 +		}
 +
 +		return false;
 +	}
 +
 +	public function delete($key) {
 +		$namespace = $this->getNamespace();
 +
 +		unset(ElggStaticVariableCache::$__cache[$namespace][$key]);
 +
 +		return true;
 +	}
 +
 +	public function clear() {
 +		$namespace = $this->getNamespace();
 +
 +		if (!isset(ElggStaticVariableCache::$__cache)) {
 +			ElggStaticVariableCache::$__cache = array();
 +		}
 +
 +		ElggStaticVariableCache::$__cache[$namespace] = array();
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ElggUser.php b/engine/classes/ElggUser.php new file mode 100644 index 000000000..001da19ed --- /dev/null +++ b/engine/classes/ElggUser.php @@ -0,0 +1,427 @@ +<?php
 +/**
 + * ElggUser
 + *
 + * Representation of a "user" in the system.
 + *
 + * @package Elgg
 + * @subpackage Core
 + */
 +class ElggUser extends ElggEntity
 +	implements Friendable {
 +	/**
 +	 * Initialise the attributes array.
 +	 * This is vital to distinguish between metadata and base parameters.
 +	 *
 +	 * Place your base parameters here.
 +	 */
 +	protected function initialise_attributes() {
 +		parent::initialise_attributes();
 +
 +		$this->attributes['type'] = "user";
 +		$this->attributes['name'] = "";
 +		$this->attributes['username'] = "";
 +		$this->attributes['password'] = "";
 +		$this->attributes['salt'] = "";
 +		$this->attributes['email'] = "";
 +		$this->attributes['language'] = "";
 +		$this->attributes['code'] = "";
 +		$this->attributes['banned'] = "no";
 +		$this->attributes['admin'] = 'no';
 +		$this->attributes['tables_split'] = 2;
 +	}
 +
 +	/**
 +	 * Construct a new user entity, optionally from a given id value.
 +	 *
 +	 * @param mixed $guid If an int, load that GUID.
 +	 * 	If a db row then will attempt to load the rest of the data.
 +	 * @throws Exception if there was a problem creating the user.
 +	 */
 +	function __construct($guid = null) {
 +		$this->initialise_attributes();
 +
 +		if (!empty($guid)) {
 +			// Is $guid is a DB row - either a entity row, or a user table row.
 +			if ($guid instanceof stdClass) {
 +				// Load the rest
 +				if (!$this->load($guid->guid)) {
 +					throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid->guid));
 +				}
 +			}
 +
 +			// See if this is a username
 +			else if (is_string($guid)) {
 +				$guid = get_user_by_username($guid);
 +				foreach ($guid->attributes as $key => $value) {
 +					$this->attributes[$key] = $value;
 +				}
 +			}
 +
 +			// Is $guid is an ElggUser? Use a copy constructor
 +			else if ($guid instanceof ElggUser) {
 +				elgg_deprecated_notice('This type of usage of the ElggUser constructor was deprecated. Please use the clone method.', 1.7);
 +
 +				foreach ($guid->attributes as $key => $value) {
 +					$this->attributes[$key] = $value;
 +				}
 +			}
 +
 +			// Is this is an ElggEntity but not an ElggUser = ERROR!
 +			else if ($guid instanceof ElggEntity) {
 +				throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggUser'));
 +			}
 +
 +			// We assume if we have got this far, $guid is an int
 +			else if (is_numeric($guid)) {
 +				if (!$this->load($guid)) {
 +					throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid));
 +				}
 +			}
 +
 +			else {
 +				throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * Override the load function.
 +	 * This function will ensure that all data is loaded (were possible), so
 +	 * if only part of the ElggUser is loaded, it'll load the rest.
 +	 *
 +	 * @param int $guid
 +	 * @return true|false
 +	 */
 +	protected function load($guid) {
 +		// Test to see if we have the generic stuff
 +		if (!parent::load($guid)) {
 +			return false;
 +		}
 +
 +		// Check the type
 +		if ($this->attributes['type']!='user') {
 +			throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, get_class()));
 +		}
 +
 +		// Load missing data
 +		$row = get_user_entity_as_row($guid);
 +		if (($row) && (!$this->isFullyLoaded())) {
 +			// If $row isn't a cached copy then increment the counter
 +			$this->attributes['tables_loaded'] ++;
 +		}
 +
 +		// Now put these into the attributes array as core values
 +		$objarray = (array) $row;
 +		foreach($objarray as $key => $value) {
 +			$this->attributes[$key] = $value;
 +		}
 +
 +		return true;
 +	}
 +
 +	/**
 +	 * Saves this user to the database.
 +	 * @return true|false
 +	 */
 +	public function save() {
 +		// Save generic stuff
 +		if (!parent::save()) {
 +			return false;
 +		}
 +
 +		// Now save specific stuff
 +		return create_user_entity($this->get('guid'), $this->get('name'), $this->get('username'), $this->get('password'), $this->get('salt'), $this->get('email'), $this->get('language'), $this->get('code'));
 +	}
 +
 +	/**
 +	 * User specific override of the entity delete method.
 +	 *
 +	 * @return bool
 +	 */
 +	public function delete() {
 +		global $USERNAME_TO_GUID_MAP_CACHE, $CODE_TO_GUID_MAP_CACHE;
 +
 +		// clear cache
 +		if (isset($USERNAME_TO_GUID_MAP_CACHE[$this->username])) {
 +			unset($USERNAME_TO_GUID_MAP_CACHE[$this->username]);
 +		}
 +		if (isset($CODE_TO_GUID_MAP_CACHE[$this->code])) {
 +			unset($CODE_TO_GUID_MAP_CACHE[$this->code]);
 +		}
 +
 +		// Delete owned data
 +		clear_annotations_by_owner($this->guid);
 +		clear_metadata_by_owner($this->guid);
 +		clear_user_files($this);
 +
 +		// Delete entity
 +		return parent::delete();
 +	}
 +
 +	/**
 +	 * Ban this user.
 +	 *
 +	 * @param string $reason Optional reason
 +	 */
 +	public function ban($reason = "") {
 +		return ban_user($this->guid, $reason);
 +	}
 +
 +	/**
 +	 * Unban this user.
 +	 */
 +	public function unban()	{
 +		return unban_user($this->guid);
 +	}
 +
 +	/**
 +	 * Is this user banned or not?
 +	 *
 +	 * @return bool
 +	 */
 +	public function isBanned() {
 +		return $this->banned == 'yes';
 +	}
 +
 +	/**
 +	 * Is this user admin?
 +	 *
 +	 * @return bool
 +	 */
 +	public function isAdmin() {
 +
 +		// for backward compatibility we need to pull this directly
 +		// from the attributes instead of using the magic methods.
 +		// this can be removed in 1.9
 +		// return $this->admin == 'yes';
 +		return $this->attributes['admin'] == 'yes';
 +	}
 +
 +	/**
 +	 * Make the user an admin
 +	 *
 +	 * @return bool
 +	 */
 +	public function makeAdmin() {
 +		if (make_user_admin($this->guid)) {
 +			$this->attributes['admin'] = 'yes';
 +			return TRUE;
 +		}
 +		return FALSE;
 +	}
 +
 +	/**
 +	 * Remove the admin flag for user
 +	 *
 +	 * @return bool
 +	 */
 +	public function removeAdmin() {
 +		if (remove_user_admin($this->guid)) {
 +			$this->attributes['admin'] = 'no';
 +			return TRUE;
 +		}
 +		return FALSE;
 +	}
 +
 +	/**
 +	 * Get sites that this user is a member of
 +	 *
 +	 * @param string $subtype Optionally, the subtype of result we want to limit to
 +	 * @param int $limit The number of results to return
 +	 * @param int $offset Any indexing offset
 +	 */
 +	function getSites($subtype="", $limit = 10, $offset = 0) {
 +		// return get_site_users($this->getGUID(), $subtype, $limit, $offset);
 +		return get_user_sites($this->getGUID(), $subtype, $limit, $offset);
 +	}
 +
 +	/**
 +	 * Add this user to a particular site
 +	 *
 +	 * @param int $site_guid The guid of the site to add it to
 +	 * @return true|false
 +	 */
 +	function addToSite($site_guid) {
 +		// return add_site_user($this->getGUID(), $site_guid);
 +		return add_site_user($site_guid, $this->getGUID());
 +	}
 +
 +	/**
 +	 * Remove this user from a particular site
 +	 *
 +	 * @param int $site_guid The guid of the site to remove it from
 +	 * @return true|false
 +	 */
 +	function removeFromSite($site_guid) {
 +		//return remove_site_user($this->getGUID(), $site_guid);
 +		return remove_site_user($site_guid, $this->getGUID());
 +	}
 +
 +	/**
 +	 * Adds a user to this user's friends list
 +	 *
 +	 * @param int $friend_guid The GUID of the user to add
 +	 * @return true|false Depending on success
 +	 */
 +	function addFriend($friend_guid) {
 +		return user_add_friend($this->getGUID(), $friend_guid);
 +	}
 +
 +	/**
 +	 * Removes a user from this user's friends list
 +	 *
 +	 * @param int $friend_guid The GUID of the user to remove
 +	 * @return true|false Depending on success
 +	 */
 +	function removeFriend($friend_guid) {
 +		return user_remove_friend($this->getGUID(), $friend_guid);
 +	}
 +
 +	/**
 +	 * Determines whether or not this user is a friend of the currently logged in user
 +	 *
 +	 * @return true|false
 +	 */
 +	function isFriend() {
 +		return user_is_friend(get_loggedin_userid(), $this->getGUID());
 +	}
 +
 +	/**
 +	 * Determines whether this user is friends with another user
 +	 *
 +	 * @param int $user_guid The GUID of the user to check is on this user's friends list
 +	 * @return true|false
 +	 */
 +	function isFriendsWith($user_guid) {
 +		return user_is_friend($this->getGUID(), $user_guid);
 +	}
 +
 +	/**
 +	 * Determines whether or not this user is on another user's friends list
 +	 *
 +	 * @param int $user_guid The GUID of the user to check against
 +	 * @return true|false
 +	 */
 +	function isFriendOf($user_guid) {
 +		return user_is_friend($user_guid, $this->getGUID());
 +	}
 +
 +	/**
 +	 * Retrieves a list of this user's friends
 +	 *
 +	 * @param string $subtype Optionally, the subtype of user to filter to (leave blank for all)
 +	 * @param int $limit The number of users to retrieve
 +	 * @param int $offset Indexing offset, if any
 +	 * @return array|false Array of ElggUsers, or false, depending on success
 +	 */
 +	function getFriends($subtype = "", $limit = 10, $offset = 0) {
 +		return get_user_friends($this->getGUID(), $subtype, $limit, $offset);
 +	}
 +
 +	/**
 +	 * Retrieves a list of people who have made this user a friend
 +	 *
 +	 * @param string $subtype Optionally, the subtype of user to filter to (leave blank for all)
 +	 * @param int $limit The number of users to retrieve
 +	 * @param int $offset Indexing offset, if any
 +	 * @return array|false Array of ElggUsers, or false, depending on success
 +	 */
 +	function getFriendsOf($subtype = "", $limit = 10, $offset = 0) {
 +		return get_user_friends_of($this->getGUID(), $subtype, $limit, $offset);
 +	}
 +
 +	/**
 +	 * Get an array of ElggObjects owned by this user.
 +	 *
 +	 * @param string $subtype The subtype of the objects, if any
 +	 * @param int $limit Number of results to return
 +	 * @param int $offset Any indexing offset
 +	 */
 +	public function getObjects($subtype="", $limit = 10, $offset = 0) {
 +		return get_user_objects($this->getGUID(), $subtype, $limit, $offset);
 +	}
 +
 +	/**
 +	 * Get an array of ElggObjects owned by this user's friends.
 +	 *
 +	 * @param string $subtype The subtype of the objects, if any
 +	 * @param int $limit Number of results to return
 +	 * @param int $offset Any indexing offset
 +	 */
 +	public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) {
 +		return get_user_friends_objects($this->getGUID(), $subtype, $limit, $offset);
 +	}
 +
 +	/**
 +	 * Counts the number of ElggObjects owned by this user
 +	 *
 +	 * @param string $subtype The subtypes of the objects, if any
 +	 * @return int The number of ElggObjects
 +	 */
 +	public function countObjects($subtype = "") {
 +		return count_user_objects($this->getGUID(), $subtype);
 +	}
 +
 +	/**
 +	 * Get the collections associated with a user.
 +	 *
 +	 * @param string $subtype Optionally, the subtype of result we want to limit to
 +	 * @param int $limit The number of results to return
 +	 * @param int $offset Any indexing offset
 +	 * @return unknown
 +	 */
 +	public function getCollections($subtype="", $limit = 10, $offset = 0) {
 +		return get_user_collections($this->getGUID(), $subtype, $limit, $offset);
 +	}
 +
 +	/**
 +	 * If a user's owner is blank, return its own GUID as the owner
 +	 *
 +	 * @return int User GUID
 +	 */
 +	function getOwner() {
 +		if ($this->owner_guid == 0) {
 +			return $this->getGUID();
 +		}
 +
 +		return $this->owner_guid;
 +	}
 +
 +	// EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
 +
 +	/**
 +	 * Return an array of fields which can be exported.
 +	 */
 +	public function getExportableValues() {
 +		return array_merge(parent::getExportableValues(), array(
 +			'name',
 +			'username',
 +			'language',
 +		));
 +	}
 +
 +	// backward compatibility with admin flag
 +	// remove for 1.9
 +	public function __set($name, $value) {
 +		if ($name == 'admin' || $name == 'siteadmin') {
 +			elgg_deprecated_notice('The admin/siteadmin metadata are not longer used.  Use ElggUser->makeAdmin() and ElggUser->removeAdmin().', '1.7.1');
 +
 +			if ($value == 'yes' || $value == '1') {
 +				$this->makeAdmin();
 +			} else {
 +				$this->removeAdmin();
 +			}
 +		}
 +		return parent::__set($name, $value);
 +	}
 +
 +	public function __get($name) {
 +		if ($name == 'admin' || $name == 'siteadmin') {
 +			elgg_deprecated_notice('The admin/siteadmin metadata are not longer used.  Use ElggUser->isAdmin().', '1.7.1');
 +			return $this->isAdmin();
 +		}
 +
 +		return parent::__get($name);
 +	}
 +}
 diff --git a/engine/classes/ElggWidget.php b/engine/classes/ElggWidget.php new file mode 100644 index 000000000..dbca3c369 --- /dev/null +++ b/engine/classes/ElggWidget.php @@ -0,0 +1,53 @@ +<?php
 +
 +/**
 + * Override ElggObject in order to store widget data in ultra-private stores.
 + */
 +class ElggWidget extends ElggObject {
 +	protected function initialise_attributes() {
 +		parent::initialise_attributes();
 +
 +		$this->attributes['subtype'] = "widget";
 +	}
 +
 +	public function __construct($guid = null) {
 +		parent::__construct($guid);
 +	}
 +
 +	/**
 +	 * Override entity get and sets in order to save data to private data store.
 +	 */
 +	public function get($name) {
 +		// See if its in our base attribute
 +		if (isset($this->attributes[$name])) {
 +			return $this->attributes[$name];
 +		}
 +
 +		// No, so see if its in the private data store.
 +		$meta = get_private_setting($this->guid, $name);
 +		if ($meta) {
 +			return $meta;
 +		}
 +
 +		// Can't find it, so return null
 +		return null;
 +	}
 +
 +	/**
 +	 * Override entity get and sets in order to save data to private data store.
 +	 */
 +	public function set($name, $value) {
 +		if (array_key_exists($name, $this->attributes)) {
 +			// Check that we're not trying to change the guid!
 +			if ((array_key_exists('guid', $this->attributes)) && ($name=='guid')) {
 +				return false;
 +			}
 +
 +			$this->attributes[$name] = $value;
 +		} else {
 +			return set_private_setting($this->guid, $name, $value);
 +		}
 +
 +		return true;
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ErrorResult.php b/engine/classes/ErrorResult.php new file mode 100644 index 000000000..5fc6c88b6 --- /dev/null +++ b/engine/classes/ErrorResult.php @@ -0,0 +1,44 @@ +<?php
 +/**
 + * ErrorResult
 + * The error result class.
 + *
 + * @author Curverider Ltd <info@elgg.com>
 + * @package Elgg
 + * @subpackage Core
 + */
 +class ErrorResult extends GenericResult {
 +	// Fail with no specific code
 +	public static $RESULT_FAIL = -1 ;
 +
 +	public static $RESULT_FAIL_APIKEY_DISABLED = -30;
 +	public static $RESULT_FAIL_APIKEY_INACTIVE = -31;
 +	public static $RESULT_FAIL_APIKEY_INVALID = -32;
 +
 +	// Invalid, expired or missing auth token
 +	public static $RESULT_FAIL_AUTHTOKEN = -20;
 +
 +	public function ErrorResult($message, $code = "", Exception $exception = NULL) {
 +		if ($code == "") {
 +			$code = ErrorResult::$RESULT_FAIL;
 +		}
 +
 +		if ($exception!=NULL) {
 +			$this->setResult($exception->__toString());
 +		}
 +
 +		$this->setStatusCode($code, $message);
 +	}
 +
 +	/**
 +	 * Get a new instance of the ErrorResult.
 +	 *
 +	 * @param string $message
 +	 * @param int $code
 +	 * @param Exception $exception Optional exception for generating a stack trace.
 +	 */
 +	public static function getInstance($message, $code = "", Exception $exception = NULL) {
 +		// Return a new error object.
 +		return new ErrorResult($message, $code, $exception);
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ExportException.php b/engine/classes/ExportException.php new file mode 100644 index 000000000..dc5c686b7 --- /dev/null +++ b/engine/classes/ExportException.php @@ -0,0 +1,9 @@ +<?php
 +/**
 + * Export exception
 + *
 + * @package Elgg
 + * @subpackage Exceptions
 + *
 + */
 +class ExportException extends DataFormatException {}
\ No newline at end of file diff --git a/engine/classes/Exportable.php b/engine/classes/Exportable.php new file mode 100644 index 000000000..da5a7cc54 --- /dev/null +++ b/engine/classes/Exportable.php @@ -0,0 +1,21 @@ +<?php
 +/**
 + * Define an interface for all ODD exportable objects.
 + *
 + * @package Elgg
 + * @subpackage Core
 + * @author Curverider Ltd
 + */
 +interface Exportable {
 +	/**
 +	 * This must take the contents of the object and convert it to exportable ODD
 +	 * @return object or array of objects.
 +	 */
 +	public function export();
 +
 +	/**
 +	 * Return a list of all fields that can be exported.
 +	 * This should be used as the basis for the values returned by export()
 +	 */
 +	public function getExportableValues();
 +}
\ No newline at end of file diff --git a/engine/classes/Friendable.php b/engine/classes/Friendable.php new file mode 100644 index 000000000..d400bd092 --- /dev/null +++ b/engine/classes/Friendable.php @@ -0,0 +1,83 @@ +<?php +/** + * An interface for objects that behave as elements within a social network that have a profile. + * + */ +interface Friendable { +	/** +	 * Adds a user as a friend +	 * +	 * @param int $friend_guid The GUID of the user to add +	 */ +	public function addFriend($friend_guid); + +	/** +	 * Removes a user as a friend +	 * +	 * @param int $friend_guid The GUID of the user to remove +	 */ +	public function removeFriend($friend_guid); + +	/** +	 * Determines whether or not the current user is a friend of this entity +	 * +	 */ +	public function isFriend(); + +	/** +	 * Determines whether or not this entity is friends with a particular entity +	 * +	 * @param int $user_guid The GUID of the entity this entity may or may not be friends with +	 */ +	public function isFriendsWith($user_guid); + +	/** +	 * Determines whether or not a foreign entity has made this one a friend +	 * +	 * @param int $user_guid The GUID of the foreign entity +	 */ +	public function isFriendOf($user_guid); + +	/** +	 * Returns this entity's friends +	 * +	 * @param string $subtype The subtype of entity to return +	 * @param int $limit The number of entities to return +	 * @param int $offset Indexing offset +	 */ +	public function getFriends($subtype = "", $limit = 10, $offset = 0); + +	/** +	 * Returns entities that have made this entity a friend +	 * +	 * @param string $subtype The subtype of entity to return +	 * @param int $limit The number of entities to return +	 * @param int $offset Indexing offset +	 */ +	public function getFriendsOf($subtype = "", $limit = 10, $offset = 0); + +	/** +	 * Returns objects in this entity's container +	 * +	 * @param string $subtype The subtype of entity to return +	 * @param int $limit The number of entities to return +	 * @param int $offset Indexing offset +	 */ +	public function getObjects($subtype="", $limit = 10, $offset = 0); + +	/** +	 * Returns objects in the containers of this entity's friends +	 * +	 * @param string $subtype The subtype of entity to return +	 * @param int $limit The number of entities to return +	 * @param int $offset Indexing offset +	 */ +	public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0); + +	/** +	 * Returns the number of object entities in this entity's container +	 * +	 * @param string $subtype The subtype of entity to count +	 */ +	public function countObjects($subtype = ""); +}
\ No newline at end of file diff --git a/engine/classes/GenericResult.php b/engine/classes/GenericResult.php new file mode 100644 index 000000000..8bccd77f2 --- /dev/null +++ b/engine/classes/GenericResult.php @@ -0,0 +1,107 @@ +<?php
 +/**
 + * GenericResult Result superclass.
 + *
 + * @author Curverider Ltd <info@elgg.com>
 + * @package Elgg
 + * @subpackage Core
 + */
 +abstract class GenericResult {
 +	/**
 +	 * The status of the result.
 +	 * @var int
 +	 */
 +	private $status_code;
 +
 +	/**
 +	 * Message returned along with the status which is almost always an error message.
 +	 * This must be human readable, understandable and localised.
 +	 * @var string
 +	 */
 +	private $message;
 +
 +	/**
 +	 * Result store.
 +	 * Attach result specific informaton here.
 +	 *
 +	 * @var mixed. Should probably be an object of some sort.
 +	 */
 +	private $result;
 +
 +	/**
 +	 * Set a status code and optional message.
 +	 *
 +	 * @param int $status The status code.
 +	 * @param string $message The message.
 +	 */
 +	protected function setStatusCode($status, $message = "") {
 +		$this->status_code = $status;
 +		$this->message = $message;
 +	}
 +
 +	/**
 +	 * Set the result.
 +	 *
 +	 * @param mixed $result
 +	 */
 +	protected function setResult($result) {
 +		$this->result = $result;
 +	}
 +
 +	protected function getStatusCode() {
 +		return $this->status_code;
 +	}
 +
 +	protected function getStatusMessage() {
 +		return $this->message;
 +	}
 +
 +	protected function getResult() {
 +		return $this->result;
 +	}
 +
 +	/**
 +	 * Serialise to a standard class.
 +	 *
 +	 * DEVNOTE: The API is only interested in data, we can not easily serialise
 +	 * custom classes without the need for 1) the other side being PHP, 2) you need to have the class
 +	 * definition installed, 3) its the right version!
 +	 *
 +	 * Therefore, I'm not bothering.
 +	 *
 +	 * Override this to include any more specific information, however api results should be attached to the
 +	 * class using setResult().
 +	 *
 +	 * if $CONFIG->debug is set then additional information about the runtime environment and authentication will be
 +	 * returned.
 +	 *
 +	 * @return stdClass Object containing the serialised result.
 +	 */
 +	public function export() {
 +		global $ERRORS, $CONFIG, $_PAM_HANDLERS_MSG;
 +
 +		$result = new stdClass;
 +
 +		$result->status = $this->getStatusCode();
 +		if ($this->getStatusMessage()!="") {
 +			$result->message = $this->getStatusMessage();
 +		}
 +
 +		$resultdata = $this->getResult();
 +		if (isset($resultdata)) {
 +			$result->result = $resultdata;
 +		}
 +
 +		if (isset($CONFIG->debug)) {
 +			if (count($ERRORS)) {
 +				$result->runtime_errors = $ERRORS;
 +			}
 +
 +			if (count($_PAM_HANDLERS_MSG)) {
 +				$result->pam = $_PAM_HANDLERS_MSG;
 +			}
 +		}
 +
 +		return $result;
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/ImportException.php b/engine/classes/ImportException.php new file mode 100644 index 000000000..fd86fc23c --- /dev/null +++ b/engine/classes/ImportException.php @@ -0,0 +1,8 @@ +<?php
 +/**
 + * Import exception
 + *
 + * @package Elgg
 + * @subpackage Exceptions
 + */
 +class ImportException extends DataFormatException {}
\ No newline at end of file diff --git a/engine/classes/Importable.php b/engine/classes/Importable.php new file mode 100644 index 000000000..775319cb7 --- /dev/null +++ b/engine/classes/Importable.php @@ -0,0 +1,16 @@ +<?php
 +/**
 + * Define an interface for all ODD importable objects.
 + * @author Curverider Ltd
 + */
 +interface Importable {
 +	/**
 +	 * Accepts an array of data to import, this data is parsed from the XML produced by export.
 +	 * The function should return the constructed object data, or NULL.
 +	 *
 +	 * @param ODD $data
 +	 * @return bool
 +	 * @throws ImportException if there was a critical error importing data.
 +	 */
 +	public function import(ODD $data);
 +}
 diff --git a/engine/classes/Locatable.php b/engine/classes/Locatable.php new file mode 100644 index 000000000..5f52d8eab --- /dev/null +++ b/engine/classes/Locatable.php @@ -0,0 +1,36 @@ +<?php
 +
 +/**
 + * Define an interface for geo-tagging entities.
 + *
 + */
 +interface Locatable {
 +	/** Set a location text */
 +	public function setLocation($location);
 +
 +	/**
 +	 * Set latitude and longitude tags for a given entity.
 +	 *
 +	 * @param float $lat
 +	 * @param float $long
 +	 */
 +	public function setLatLong($lat, $long);
 +
 +	/**
 +	 * Get the contents of the ->geo:lat field.
 +	 *
 +	 */
 +	public function getLatitude();
 +
 +	/**
 +	 * Get the contents of the ->geo:lat field.
 +	 *
 +	 */
 +	public function getLongitude();
 +
 +	/**
 +	 * Get the ->location metadata.
 +	 *
 +	 */
 +	public function getLocation();
 +}
\ No newline at end of file diff --git a/engine/classes/Loggable.php b/engine/classes/Loggable.php new file mode 100644 index 000000000..e12641410 --- /dev/null +++ b/engine/classes/Loggable.php @@ -0,0 +1,49 @@ +<?php
 +/**
 + * Interface that provides an interface which must be implemented by all objects wishing to be
 + * recorded in the system log (and by extension the river).
 + *
 + * This interface defines a set of methods that permit the system log functions to hook in and retrieve
 + * the necessary information and to identify what events can actually be logged.
 + *
 + * To have events involving your object to be logged simply implement this interface.
 + *
 + * @author Curverider Ltd
 + */
 +interface Loggable {
 +	/**
 +	 * Return an identification for the object for storage in the system log.
 +	 * This id must be an integer.
 +	 *
 +	 * @return int
 +	 */
 +	public function getSystemLogID();
 +
 +	/**
 +	 * Return the class name of the object.
 +	 * Added as a function because get_class causes errors for some reason.
 +	 */
 +	public function getClassName();
 +
 +	/**
 +	 * Return the type of the object - eg. object, group, user, relationship, metadata, annotation etc
 +	 */
 +	public function getType();
 +
 +	/**
 +	 * Return a subtype. For metadata & annotations this is the 'name' and for relationship this is the relationship type.
 +	 */
 +	public function getSubtype();
 +
 +	/**
 +	 * For a given ID, return the object associated with it.
 +	 * This is used by the river functionality primarily.
 +	 * This is useful for checking access permissions etc on objects.
 +	 */
 +	public function getObjectFromID($id);
 +
 +	/**
 +	 * Return the GUID of the owner of this object.
 +	 */
 +	public function getObjectOwnerGUID();
 +}
\ No newline at end of file diff --git a/engine/classes/Notable.php b/engine/classes/Notable.php new file mode 100644 index 000000000..3c133d494 --- /dev/null +++ b/engine/classes/Notable.php @@ -0,0 +1,30 @@ +<?php
 +/**
 + * Calendar interface for events.
 + *
 + */
 +interface Notable {
 +	/**
 +	 * Calendar functionality.
 +	 * This function sets the time of an object on a calendar listing.
 +	 *
 +	 * @param int $hour If ommitted, now is assumed.
 +	 * @param int $minute If ommitted, now is assumed.
 +	 * @param int $second If ommitted, now is assumed.
 +	 * @param int $day If ommitted, now is assumed.
 +	 * @param int $month If ommitted, now is assumed.
 +	 * @param int $year If ommitted, now is assumed.
 +	 * @param int $duration Duration of event, remainder of the day is assumed.
 +	 */
 +	public function setCalendarTimeAndDuration($hour = NULL, $minute = NULL, $second = NULL, $day = NULL, $month = NULL, $year = NULL, $duration = NULL);
 +
 +	/**
 +	 * Return the start timestamp.
 +	 */
 +	public function getCalendarStartTime();
 +
 +	/**
 +	 * Return the end timestamp.
 +	 */
 +	public function getCalendarEndTime();
 +}
\ No newline at end of file diff --git a/engine/classes/ODD.php b/engine/classes/ODD.php new file mode 100644 index 000000000..a4118ee15 --- /dev/null +++ b/engine/classes/ODD.php @@ -0,0 +1,94 @@ +<?php
 +/**
 + * Open Data Definition (ODD) superclass.
 + * @package Elgg
 + * @subpackage Core
 + * @author Curverider Ltd
 + */
 +abstract class ODD {
 +	/**
 +	 * Attributes.
 +	 */
 +	private $attributes = array();
 +
 +	/**
 +	 * Optional body.
 +	 */
 +	private $body;
 +
 +	/**
 +	 * Construct an ODD document with initial values.
 +	 */
 +	public function __construct() {
 +		$this->body = "";
 +	}
 +
 +	public function getAttributes() {
 +		return $this->attributes;
 +	}
 +
 +	public function setAttribute($key, $value) {
 +		$this->attributes[$key] = $value;
 +	}
 +
 +	public function getAttribute($key) {
 +		if (isset($this->attributes[$key])) {
 +			return $this->attributes[$key];
 +		}
 +
 +		return NULL;
 +	}
 +
 +	public function setBody($value) {
 +		$this->body = $value;
 +	}
 +
 +	public function getBody() {
 +		return $this->body;
 +	}
 +
 +	/**
 +	 * Set the published time.
 +	 *
 +	 * @param int $time Unix timestamp
 +	 */
 +	public function setPublished($time) {
 +		$this->attributes['published'] = date("r", $time);
 +	}
 +
 +	/**
 +	 * Return the published time as a unix timestamp.
 +	 *
 +	 * @return int or false on failure.
 +	 */
 +	public function getPublishedAsTime() {
 +		return strtotime($this->attributes['published']);
 +	}
 +
 +	/**
 +	 * For serialisation, implement to return a string name of the tag eg "header" or "metadata".
 +	 * @return string
 +	 */
 +	abstract protected function getTagName();
 +
 +	/**
 +	 * Magic function to generate valid ODD XML for this item.
 +	 */
 +	public function __toString() {
 +		// Construct attributes
 +		$attr = "";
 +		foreach ($this->attributes as $k => $v) {
 +			$attr .= ($v!="") ? "$k=\"$v\" " : "";
 +		}
 +
 +		$body = $this->getBody();
 +		$tag = $this->getTagName();
 +
 +		$end = "/>";
 +		if ($body!="") {
 +			$end = "><![CDATA[$body]]></{$tag}>";
 +		}
 +
 +		return "<{$tag} $attr" . $end . "\n";
 +	}
 +}
 diff --git a/engine/classes/ODDDocument.php b/engine/classes/ODDDocument.php new file mode 100644 index 000000000..0c8731a75 --- /dev/null +++ b/engine/classes/ODDDocument.php @@ -0,0 +1,129 @@ +<?php
 +/**
 + * @class ODDDocument ODD Document container.
 + * This class is used during import and export to construct.
 + * @author Curverider Ltd
 + */
 +class ODDDocument implements Iterator {
 +	/**
 +	 * ODD Version
 +	 *
 +	 * @var string
 +	 */
 +	private $ODDSupportedVersion = "1.0";
 +
 +	/**
 +	 * Elements of the document.
 +	 */
 +	private $elements;
 +
 +	/**
 +	 * Optional wrapper factory.
 +	 */
 +	private $wrapperfactory;
 +
 +	public function __construct(array $elements = NULL) {
 +		if ($elements) {
 +			if (is_array($elements)) {
 +				$this->elements = $elements;
 +			} else {
 +				$this->addElement($elements);
 +			}
 +		} else {
 +			$this->elements = array();
 +		}
 +	}
 +
 +	/**
 +	 * Return the version of ODD being used.
 +	 *
 +	 * @return string
 +	 */
 +	public function getVersion() {
 +		return $this->ODDSupportedVersion;
 +	}
 +
 +	public function getNumElements() {
 +		return count($this->elements);
 +	}
 +
 +	public function addElement(ODD $element) {
 +		if (!is_array($this->elements)) {
 +			$this->elements = array();
 +			$this->elements[] = $element;
 +		}
 +	}
 +
 +	public function addElements(array $elements) {
 +		foreach ($elements as $element) {
 +			$this->addElement($element);
 +		}
 +	}
 +
 +	public function getElements() {
 +		return $this->elements;
 +	}
 +
 +	/**
 +	 * Set an optional wrapper factory to optionally embed the ODD document in another format.
 +	 */
 +	public function setWrapperFactory(ODDWrapperFactory $factory) {
 +		$this->wrapperfactory = $factory;
 +	}
 +
 +	/**
 +	 * Magic function to generate valid ODD XML for this item.
 +	 */
 +	public function __toString() {
 +		$xml = "";
 +
 +		if ($this->wrapperfactory) {
 +			// A wrapper has been provided
 +			$wrapper = $this->wrapperfactory->getElementWrapper($this); // Get the wrapper for this element
 +
 +			$xml = $wrapper->wrap($this); // Wrap this element (and subelements)
 +		} else {
 +			// Output begin tag
 +			$generated = date("r");
 +			$xml .= "<odd version=\"{$this->ODDSupportedVersion}\" generated=\"$generated\">\n";
 +
 +			// Get XML for elements
 +			foreach ($this->elements as $element) {
 +				$xml .= "$element";
 +			}
 +
 +			// Output end tag
 +			$xml .= "</odd>\n";
 +		}
 +
 +		return $xml;
 +	}
 +
 +	// ITERATOR INTERFACE //////////////////////////////////////////////////////////////
 +	/*
 +	 * This lets an entity's attributes be displayed using foreach as a normal array.
 +	 * Example: http://www.sitepoint.com/print/php5-standard-library
 +	 */
 +
 +	private $valid = FALSE;
 +
 +	function rewind() {
 +		$this->valid = (FALSE !== reset($this->elements));
 +	}
 +
 +	function current() {
 +		return current($this->elements);
 +	}
 +
 +	function key() {
 +		return key($this->elements);
 +	}
 +
 +	function next() {
 +		$this->valid = (FALSE !== next($this->elements));
 +	}
 +
 +	function valid() {
 +		return $this->valid;
 +	}
 +}
 diff --git a/engine/classes/ODDEntity.php b/engine/classes/ODDEntity.php new file mode 100644 index 000000000..30da5f37f --- /dev/null +++ b/engine/classes/ODDEntity.php @@ -0,0 +1,60 @@ +<?php
 +
 +/**
 + * ODD Entity class.
 + * @package Elgg
 + * @subpackage Core
 + * @author Curverider Ltd
 + */
 +class ODDEntity extends ODD {
 +	function __construct($uuid, $class, $subclass = "") {
 +		parent::__construct();
 +
 +		$this->setAttribute('uuid', $uuid);
 +		$this->setAttribute('class', $class);
 +		$this->setAttribute('subclass', $subclass);
 +	}
 +
 +	protected function getTagName() { return "entity"; }
 +}
 +
 +/**
 + * ODD Metadata class.
 + * @package Elgg
 + * @subpackage Core
 + * @author Curverider Ltd
 + */
 +class ODDMetaData extends ODD {
 +	function __construct($uuid, $entity_uuid, $name, $value, $type = "", $owner_uuid = "") {
 +		parent::__construct();
 +
 +		$this->setAttribute('uuid', $uuid);
 +		$this->setAttribute('entity_uuid', $entity_uuid);
 +		$this->setAttribute('name', $name);
 +		$this->setAttribute('type', $type);
 +		$this->setAttribute('owner_uuid', $owner_uuid);
 +		$this->setBody($value);
 +	}
 +
 +	protected function getTagName() {
 +		return "metadata";
 +	}
 +}
 +
 +/**
 + * ODD Relationship class.
 + * @package Elgg
 + * @subpackage Core
 + * @author Curverider Ltd
 + */
 +class ODDRelationship extends ODD {
 +	function __construct($uuid1, $type, $uuid2) {
 +		parent::__construct();
 +
 +		$this->setAttribute('uuid1', $uuid1);
 +		$this->setAttribute('type', $type);
 +		$this->setAttribute('uuid2', $uuid2);
 +	}
 +
 +	protected function getTagName() { return "relationship"; }
 +}
\ No newline at end of file diff --git a/engine/classes/SuccessResult.php b/engine/classes/SuccessResult.php new file mode 100644 index 000000000..db5769d58 --- /dev/null +++ b/engine/classes/SuccessResult.php @@ -0,0 +1,22 @@ +<?php
 +/**
 + * SuccessResult
 + * Generic success result class, extend if you want to do something special.
 + *
 + * @author Curverider Ltd <info@elgg.com>
 + * @package Elgg
 + * @subpackage Core
 + */
 +class SuccessResult extends GenericResult {
 +	public static $RESULT_SUCCESS = 0;  // Do not change this from 0
 +
 +	public function SuccessResult($result) {
 +		$this->setResult($result);
 +		$this->setStatusCode(SuccessResult::$RESULT_SUCCESS);
 +	}
 +
 +	public static function getInstance($result) {
 +		// Return a new error object.
 +		return new SuccessResult($result);
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/XMLRPCArrayParameter.php b/engine/classes/XMLRPCArrayParameter.php new file mode 100644 index 000000000..600f5d9a7 --- /dev/null +++ b/engine/classes/XMLRPCArrayParameter.php @@ -0,0 +1,48 @@ +<?php
 +
 +/**
 + * @class XMLRPCArrayParameter An array containing other XMLRPCParameter objects.
 + * @author Curverider Ltd
 + */
 +class XMLRPCArrayParameter extends XMLRPCParameter
 +{
 +	/**
 +	 * Construct an array.
 +	 *
 +	 * @param array $parameters Optional array of parameters, if not provided then addField must be used.
 +	 */
 +	function __construct($parameters = NULL)
 +	{
 +		parent::__construct();
 +		
 +		if (is_array($parameters))
 +		{
 +			foreach ($parameters as $v)
 +				$this->addField($v);
 +		}
 +	}
 +	
 +	/**
 +	 * Add a field to the container.
 +	 *
 +	 * @param XMLRPCParameter $value The value.
 +	 */
 +	public function addField(XMLRPCParameter $value)
 +	{
 +		if (!is_array($this->value))
 +			$this->value = array();
 +			
 +		$this->value[] = $value;
 +	}
 +	
 +	function __toString() 
 +	{
 +		$params = "";
 +		foreach ($this->value as $value)
 +		{
 +			$params .= "$value";
 +		}
 +		
 +		return "<array><data>$params</data></array>";
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/XMLRPCBase64Parameter.php b/engine/classes/XMLRPCBase64Parameter.php new file mode 100644 index 000000000..b32e7f3da --- /dev/null +++ b/engine/classes/XMLRPCBase64Parameter.php @@ -0,0 +1,24 @@ +<?php
 +/**
 + * @class XMLRPCBase64Parameter A base 64 encoded blob of binary.
 + * @author Curverider Ltd
 + */
 +class XMLRPCBase64Parameter extends XMLRPCParameter
 +{
 +	/**
 +	 * Construct a base64 encoded block
 +	 *
 +	 * @param string $blob Unencoded binary blob
 +	 */
 +	function __construct($blob)
 +	{
 +		parent::__construct();
 +		
 +		$this->value = base64_encode($blob);
 +	}
 +	
 +	function __toString() 
 +	{
 +		return "<value><base64>{$value}</base64></value>";
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/XMLRPCBoolParameter.php b/engine/classes/XMLRPCBoolParameter.php new file mode 100644 index 000000000..c2714ceff --- /dev/null +++ b/engine/classes/XMLRPCBoolParameter.php @@ -0,0 +1,20 @@ +<?php
 +/**
 + * @class XMLRPCBoolParameter A boolean.
 + * @author Curverider Ltd
 + */
 +class XMLRPCBoolParameter extends XMLRPCParameter
 +{
 +	function __construct($value)
 +	{
 +		parent::__construct();
 +		
 +		$this->value = (bool)$value; 
 +	}
 +	
 +	function __toString() 
 +	{
 +		$code = ($this->value) ? "1" : "0";
 +		return "<value><boolean>{$code}</boolean></value>";
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/XMLRPCCall.php b/engine/classes/XMLRPCCall.php new file mode 100644 index 000000000..09e8e6d6d --- /dev/null +++ b/engine/classes/XMLRPCCall.php @@ -0,0 +1,60 @@ +<?php
 +/**
 + * @class XMLRPCCall
 + * This class represents 
 + * @author Curverider Ltd
 + */
 +class XMLRPCCall
 +{
 +	/** Method name */
 +	private $methodname;
 +	/** Parameters */
 +	private $params;
 +	
 +	/**
 +	 * Construct a new XML RPC Call
 +	 *
 +	 * @param string $xml
 +	 */
 +	function __construct($xml)
 +	{
 +		$this->parse($xml);
 +	}
 +	
 +	/**
 +	 * Return the method name associated with the call.
 +	 *
 +	 * @return string
 +	 */
 +	public function getMethodName() { return $this->methodname; }
 +	
 +	/**
 +	 * Return the parameters.
 +	 * Returns a nested array of XmlElement.
 +	 * 
 +	 * @see XmlElement 
 +	 * @return array
 +	 */
 +	public function getParameters() { return $this->params; }
 +	
 +	/**
 +	 * Parse the xml into its components according to spec. 
 +	 * This first version is a little primitive. 
 +	 *
 +	 * @param string $xml
 +	 */
 +	private function parse($xml)
 +	{
 +		$xml = xml_to_object($xml);
 +		
 +		// sanity check
 +		if ((isset($xml->name)) && (strcasecmp($xml->name, "methodCall")!=0))
 +			throw new CallException(elgg_echo('CallException:NotRPCCall'));
 +		
 +		// method name
 +		$this->methodname = $xml->children[0]->content;
 +		
 +		// parameters 
 +		$this->params = $xml->children[1]->children;			
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/XMLRPCDateParameter.php b/engine/classes/XMLRPCDateParameter.php new file mode 100644 index 000000000..47ba88c0f --- /dev/null +++ b/engine/classes/XMLRPCDateParameter.php @@ -0,0 +1,27 @@ +<?php
 +/**
 + * @class XMLRPCDateParameter An ISO8601 data and time.
 + * @author Curverider Ltd
 + */
 +class XMLRPCDateParameter extends XMLRPCParameter
 +{
 +	/**
 +	 * Construct a date
 +	 *
 +	 * @param int $timestamp The unix timestamp, or blank for "now".
 +	 */
 +	function __construct($timestamp = 0)
 +	{
 +		parent::__construct();
 +		
 +		$this->value = $timestamp;
 +		if (!$timestamp)
 +			$this->value = time(); 
 +	}
 +	
 +	function __toString() 
 +	{
 +		$value = date('c', $this->value);
 +		return "<value><dateTime.iso8601>{$value}</dateTime.iso8601></value>";
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/XMLRPCDoubleParameter.php b/engine/classes/XMLRPCDoubleParameter.php new file mode 100644 index 000000000..64cbdff91 --- /dev/null +++ b/engine/classes/XMLRPCDoubleParameter.php @@ -0,0 +1,19 @@ +<?php
 +/**
 + * @class XMLRPCDoubleParameter A double precision signed floating point number.
 + * @author Curverider Ltd
 + */
 +class XMLRPCDoubleParameter extends XMLRPCParameter
 +{
 +	function __construct($value)
 +	{
 +		parent::__construct();
 +		
 +		$this->value = (float)$value; 
 +	}
 +	
 +	function __toString() 
 +	{
 +		return "<value><double>{$this->value}</double></value>";
 +	}
 +}
 diff --git a/engine/classes/XMLRPCErrorResponse.php b/engine/classes/XMLRPCErrorResponse.php new file mode 100644 index 000000000..4dfcfafea --- /dev/null +++ b/engine/classes/XMLRPCErrorResponse.php @@ -0,0 +1,34 @@ +<?php
 +
 +/**
 + * @class XMLRPCErrorResponse
 + * @author Curverider Ltd
 + */
 +class XMLRPCErrorResponse extends XMLRPCResponse
 +{		
 +	/**
 +	 * Set the error response and error code.
 +	 *
 +	 * @param string $message The message
 +	 * @param int $code Error code (default = system error as defined by http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
 +	 */
 +	function __construct($message, $code = -32400)
 +	{
 +		$this->addParameter(
 +			new XMLRPCStructParameter(
 +				array (
 +					'faultCode' => new XMLRPCIntParameter($code),
 +					'faultString' => new XMLRPCStringParameter($message)
 +				)
 +			)
 +		);
 +	}
 +	
 +	/**
 +	 * Output to XML.
 +	 */
 +	public function __toString()
 +	{
 +		return "<methodResponse><fault><value>{$this->parameters[0]}</value></fault></methodResponse>";
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/XMLRPCIntParameter.php b/engine/classes/XMLRPCIntParameter.php new file mode 100644 index 000000000..2305a66a7 --- /dev/null +++ b/engine/classes/XMLRPCIntParameter.php @@ -0,0 +1,19 @@ +<?php
 +/**
 + * @class XMLRPCIntParameter An Integer.
 + * @author Curverider Ltd
 + */
 +class XMLRPCIntParameter extends XMLRPCParameter
 +{
 +	function __construct($value)
 +	{
 +		parent::__construct();
 +		
 +		$this->value = (int)$value; 
 +	}
 +	
 +	function __toString() 
 +	{
 +		return "<value><i4>{$this->value}</i4></value>";
 +	}
 +}
 diff --git a/engine/classes/XMLRPCParameter.php b/engine/classes/XMLRPCParameter.php new file mode 100644 index 000000000..f9e04a073 --- /dev/null +++ b/engine/classes/XMLRPCParameter.php @@ -0,0 +1,12 @@ +<?php
 +/**
 + * @class XMLRPCParameter Superclass for all RPC parameters.
 + * @author Curverider Ltd
 + */
 +abstract class XMLRPCParameter
 +{
 +	protected $value;
 +
 +	function __construct() { }
 +		
 +}
\ No newline at end of file diff --git a/engine/classes/XMLRPCResponse.php b/engine/classes/XMLRPCResponse.php new file mode 100644 index 000000000..1dea8d682 --- /dev/null +++ b/engine/classes/XMLRPCResponse.php @@ -0,0 +1,29 @@ +<?php
 +
 +/**
 + * @class XMLRPCResponse XML-RPC Response. 
 + * @author Curverider Ltd
 + */
 +abstract class XMLRPCResponse
 +{
 +	/** An array of parameters */
 +	protected $parameters = array();
 +	
 +	/**
 +	 * Add a parameter here.
 +	 *
 +	 * @param XMLRPCParameter $param The parameter.
 +	 */
 +	public function addParameter(XMLRPCParameter $param)
 +	{
 +		if (!is_array($this->parameters))
 +			$this->parameters = array();
 +			
 +		$this->parameters[] = $param;
 +	}
 +
 +	public function addInt($value) { $this->addParameter(new XMLRPCIntParameter($value)); }
 +	public function addString($value) { $this->addParameter(new XMLRPCStringParameter($value)); }
 +	public function addDouble($value) { $this->addParameter(new XMLRPCDoubleParameter($value)); }
 +	public function addBoolean($value) { $this->addParameter(new XMLRPCBoolParameter($value)); }
 +}
\ No newline at end of file diff --git a/engine/classes/XMLRPCStringParameter.php b/engine/classes/XMLRPCStringParameter.php new file mode 100644 index 000000000..bf9097747 --- /dev/null +++ b/engine/classes/XMLRPCStringParameter.php @@ -0,0 +1,20 @@ +<?php
 +/**
 + * @class XMLRPCStringParameter A string.
 + * @author Curverider Ltd
 + */
 +class XMLRPCStringParameter extends XMLRPCParameter
 +{
 +	function __construct($value)
 +	{
 +		parent::__construct();
 +		
 +		$this->value = $value; 
 +	}
 +	
 +	function __toString() 
 +	{
 +		$value = htmlentities($this->value);
 +		return "<value><string>{$value}</string></value>";
 +	}
 +}
 diff --git a/engine/classes/XMLRPCStructParameter.php b/engine/classes/XMLRPCStructParameter.php new file mode 100644 index 000000000..326a82804 --- /dev/null +++ b/engine/classes/XMLRPCStructParameter.php @@ -0,0 +1,49 @@ +<?php
 +
 +/**
 + * @class XMLRPCStructParameter A structure containing other XMLRPCParameter objects.
 + * @author Curverider Ltd
 + */
 +class XMLRPCStructParameter extends XMLRPCParameter
 +{
 +	/**
 +	 * Construct a struct.
 +	 *
 +	 * @param array $parameters Optional associated array of parameters, if not provided then addField must be used.
 +	 */
 +	function __construct($parameters = NULL)
 +	{
 +		parent::__construct();
 +		
 +		if (is_array($parameters))
 +		{
 +			foreach ($parameters as $k => $v)
 +				$this->addField($k, $v);
 +		}
 +	}
 +	
 +	/**
 +	 * Add a field to the container.
 +	 *
 +	 * @param string $name The name of the field.
 +	 * @param XMLRPCParameter $value The value.
 +	 */
 +	public function addField($name, XMLRPCParameter $value)
 +	{
 +		if (!is_array($this->value))
 +			$this->value = array();
 +			
 +		$this->value[$name] = $value;
 +	}
 +	
 +	function __toString() 
 +	{
 +		$params = "";
 +		foreach ($this->value as $k => $v)
 +		{
 +			$params .= "<member><name>$k</name>$v</member>";
 +		}
 +		
 +		return "<value><struct>$params</struct></value>";
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/XMLRPCSuccessResponse.php b/engine/classes/XMLRPCSuccessResponse.php new file mode 100644 index 000000000..cffa64439 --- /dev/null +++ b/engine/classes/XMLRPCSuccessResponse.php @@ -0,0 +1,19 @@ +<?php
 +/**
 + * @class XMLRPCSuccessResponse
 + * @author Curverider Ltd
 + */
 +class XMLRPCSuccessResponse extends XMLRPCResponse
 +{
 +	/**
 +	 * Output to XML.
 +	 */
 +	public function __toString()
 +	{
 +		$params = "";
 +		foreach ($this->parameters as $param)
 +			$params .= "<param>$param</param>\n";
 +		
 +		return "<methodResponse><params>$params</params></methodResponse>";
 +	}
 +}
\ No newline at end of file diff --git a/engine/classes/XmlElement.php b/engine/classes/XmlElement.php new file mode 100644 index 000000000..17e3151a8 --- /dev/null +++ b/engine/classes/XmlElement.php @@ -0,0 +1,19 @@ +<?php
 +/**
 + * @class XmlElement
 + * A class representing an XML element for import.
 + */
 +class XmlElement 
 +{
 +	/** The name of the element */
 +	public $name;
 +	
 +	/** The attributes */
 +	public $attributes;
 +	
 +	/** CData */
 +	public $content;
 +	
 +	/** Child elements */
 +	public $children;
 +};
\ No newline at end of file | 
