diff options
Diffstat (limited to 'engine/classes')
| -rw-r--r-- | engine/classes/ElggAttributeLoader.php | 199 | ||||
| -rw-r--r-- | engine/classes/ElggAutoP.php | 309 | ||||
| -rw-r--r-- | engine/classes/ElggDiskFilestore.php | 10 | ||||
| -rw-r--r-- | engine/classes/ElggGroup.php | 37 | ||||
| -rw-r--r-- | engine/classes/ElggGroupItemVisibility.php | 93 | ||||
| -rw-r--r-- | engine/classes/ElggMenuBuilder.php | 10 | ||||
| -rw-r--r-- | engine/classes/ElggMenuItem.php | 2 | ||||
| -rw-r--r-- | engine/classes/ElggObject.php | 39 | ||||
| -rw-r--r-- | engine/classes/ElggPAM.php | 8 | ||||
| -rw-r--r-- | engine/classes/ElggPlugin.php | 68 | ||||
| -rw-r--r-- | engine/classes/ElggPluginManifest.php | 2 | ||||
| -rw-r--r-- | engine/classes/ElggPluginManifestParser.php | 6 | ||||
| -rw-r--r-- | engine/classes/ElggSession.php | 14 | ||||
| -rw-r--r-- | engine/classes/ElggSite.php | 42 | ||||
| -rw-r--r-- | engine/classes/ElggStaticVariableCache.php | 4 | ||||
| -rw-r--r-- | engine/classes/ElggUser.php | 36 | ||||
| -rw-r--r-- | engine/classes/ElggVolatileMetadataCache.php | 3 | ||||
| -rw-r--r-- | engine/classes/ElggXMLElement.php | 115 | ||||
| -rw-r--r-- | engine/classes/IncompleteEntityException.php | 10 | 
19 files changed, 807 insertions, 200 deletions
diff --git a/engine/classes/ElggAttributeLoader.php b/engine/classes/ElggAttributeLoader.php new file mode 100644 index 000000000..602bb8bae --- /dev/null +++ b/engine/classes/ElggAttributeLoader.php @@ -0,0 +1,199 @@ +<?php + +/** + * Loads ElggEntity attributes from DB or validates those passed in via constructor + * + * @access private + */ +class ElggAttributeLoader { + +	/** +	 * @var array names of attributes in all entities +	 */ +	protected static $primary_attr_names = array( +		'guid', +		'type', +		'subtype', +		'owner_guid', +		'container_guid', +		'site_guid', +		'access_id', +		'time_created', +		'time_updated', +		'last_action', +		'enabled' +	); + +	/** +	 * @var array names of secondary attributes required for the entity +	 */ +	protected $secondary_attr_names = array(); + +	/** +	 * @var string entity type (not class) required for fetched primaries +	 */ +	protected $required_type; + +	/** +	 * @var array +	 */ +	protected $initialized_attributes; + +	/** +	 * @var string class of object being loaded +	 */ +	protected $class; + +	/** +	 * @var bool should access control be considered when fetching entity? +	 */ +	public $requires_access_control = true; + +	/** +	 * @var callable function used to load attributes from {prefix}entities table +	 */ +	public $primary_loader = 'get_entity_as_row'; + +	/** +	 * @var callable function used to load attributes from secondary table +	 */ +	public $secondary_loader = ''; + +	/** +	 * @var callable function used to load all necessary attributes +	 */ +	public $full_loader = ''; + +	/** +	 * @param string $class class of object being loaded +	 * @param string $required_type entity type this is being used to populate +	 * @param array $initialized_attrs attributes after initializeAttributes() has been run +	 * @throws InvalidArgumentException +	 */ +	public function __construct($class, $required_type, array $initialized_attrs) { +		if (!is_string($class)) { +			throw new InvalidArgumentException('$class must be a class name.'); +		} +		$this->class = $class; + +		if (!is_string($required_type)) { +			throw new InvalidArgumentException('$requiredType must be a system entity type.'); +		} +		$this->required_type = $required_type; + +		$this->initialized_attributes = $initialized_attrs; +		unset($initialized_attrs['tables_split'], $initialized_attrs['tables_loaded']); +		$all_attr_names = array_keys($initialized_attrs); +		$this->secondary_attr_names = array_diff($all_attr_names, self::$primary_attr_names); +	} + +	protected function isMissingPrimaries($row) { +		return array_diff(self::$primary_attr_names, array_keys($row)) !== array(); +	} + +	protected function isMissingSecondaries($row) { +		return array_diff($this->secondary_attr_names, array_keys($row)) !== array(); +	} + +	protected function checkType($row) { +		if ($row['type'] !== $this->required_type) { +			$msg = elgg_echo('InvalidClassException:NotValidElggStar', array($row['guid'], $this->class)); +			throw new InvalidClassException($msg); +		} +	} + +	/** +	 * Get all required attributes for the entity, validating any that are passed in. Returns empty array +	 * if can't be loaded (Check $failure_reason). +	 * +	 * This function splits loading between "primary" attributes (those in {prefix}entities table) and +	 * "secondary" attributes (e.g. those in {prefix}objects_entity), but can load all at once if a +	 * combined loader is available. +	 * +	 * @param mixed $row a row loaded from DB (array or stdClass) or a GUID +	 * @return array will be empty if failed to load all attributes (access control or entity doesn't exist) +	 * +	 * @throws InvalidArgumentException|LogicException|IncompleteEntityException +	 */ +	public function getRequiredAttributes($row) { +		if (!is_array($row) && !($row instanceof stdClass)) { +			// assume row is the GUID +			$row = array('guid' => $row); +		} +		$row = (array) $row; +		if (empty($row['guid'])) { +			throw new InvalidArgumentException('$row must be or contain a GUID'); +		} + +		// these must be present to support isFullyLoaded() +		foreach (array('tables_split', 'tables_loaded') as $key) { +			if (isset($this->initialized_attributes[$key])) { +				$row[$key] = $this->initialized_attributes[$key]; +			} +		} + +		$was_missing_primaries = $this->isMissingPrimaries($row); +		$was_missing_secondaries = $this->isMissingSecondaries($row); + +		// some types have a function to load all attributes at once, it should be faster +		if (($was_missing_primaries || $was_missing_secondaries) && is_callable($this->full_loader)) { +			$fetched = (array) call_user_func($this->full_loader, $row['guid']); +			if (!$fetched) { +				return array(); +			} +			$row = array_merge($row, $fetched); +			$this->checkType($row); +		} else { +			if ($was_missing_primaries) { +				if (!is_callable($this->primary_loader)) { +					throw new LogicException('Primary attribute loader must be callable'); +				} +				if (!$this->requires_access_control) { +					$ignoring_access = elgg_set_ignore_access(); +				} +				$fetched = (array) call_user_func($this->primary_loader, $row['guid']); +				if (!$this->requires_access_control) { +					elgg_set_ignore_access($ignoring_access); +				} +				if (!$fetched) { +					return array(); +				} +				$row = array_merge($row, $fetched); +			} + +			// We must test type before trying to load the secondaries so that InvalidClassException +			// gets thrown. Otherwise the secondary loader will fail and return false. +			$this->checkType($row); + +			if ($was_missing_secondaries) { +				if (!is_callable($this->secondary_loader)) { +					throw new LogicException('Secondary attribute loader must be callable'); +				} +				$fetched = (array) call_user_func($this->secondary_loader, $row['guid']); +				if (!$fetched) { +					if ($row['type'] === 'site') { +						// A special case is needed for sites: When vanilla ElggEntities are created and +						// saved, these are stored w/ type "site", but with no sites_entity row. These +						// are probably only created in the unit tests. +						// @todo Don't save vanilla ElggEntities with type "site" +						$row['guid'] = (int) $row['guid']; +						return $row; +					} +					throw new IncompleteEntityException("Secondary loader failed to return row for {$row['guid']}"); +				} +				$row = array_merge($row, $fetched); +			} +		} + +		// loading complete: re-check missing and check type +		if (($was_missing_primaries && $this->isMissingPrimaries($row)) +				|| ($was_missing_secondaries && $this->isMissingSecondaries($row))) { +			throw new LogicException('Attribute loaders failed to return proper attributes'); +		} + +		// guid needs to be an int  http://trac.elgg.org/ticket/4111 +		$row['guid'] = (int) $row['guid']; + +		return $row; +	} +} diff --git a/engine/classes/ElggAutoP.php b/engine/classes/ElggAutoP.php new file mode 100644 index 000000000..89d77e583 --- /dev/null +++ b/engine/classes/ElggAutoP.php @@ -0,0 +1,309 @@ +<?php + +/** + * Create wrapper P and BR elements in HTML depending on newlines. Useful when + * users use newlines to signal line and paragraph breaks. In all cases output + * should be well-formed markup. + * + * In DIV elements, Ps are only added when there would be at + * least two of them. + */ +class ElggAutoP { + +	public $encoding = 'UTF-8'; + +	/** +	 * @var DOMDocument +	 */ +	protected $_doc = null; + +	/** +	 * @var DOMXPath +	 */ +	protected $_xpath = null; + +	protected $_blocks = 'address article area aside blockquote caption col colgroup dd  +		details div dl dt fieldset figure figcaption footer form h1 h2 h3 h4 h5 h6 header  +		hr hgroup legend map math menu nav noscript p pre section select style summary +		table tbody td tfoot th thead tr ul ol option li'; + +	/** +	 * @var array +	 */ +	protected $_inlines = 'a abbr audio b button canvas caption cite code command datalist +		del dfn em embed i iframe img input ins kbd keygen label map mark meter object +		output progress q rp rt ruby s samp script select small source span strong style +		sub sup textarea time var video wbr'; + +	/** +	 * Descend into these elements to add Ps +	 * +	 * @var array +	 */ +	protected $_descendList = 'article aside blockquote body details div footer form +		header section'; + +	/** +	 * Add Ps inside these elements +	 * +	 * @var array +	 */ +	protected $_alterList = 'article aside blockquote body details div footer header +		section'; + +	protected $_unique = ''; + +	public function __construct() { +		$this->_blocks = preg_split('@\\s+@', $this->_blocks); +		$this->_descendList = preg_split('@\\s+@', $this->_descendList); +		$this->_alterList = preg_split('@\\s+@', $this->_alterList); +		$this->_inlines = preg_split('@\\s+@', $this->_inlines); +		$this->_unique = md5(__FILE__); +	} + +	/** +	 * Intance of class for singleton pattern. +	 * @var ElggAutoP +	 */ +	private static $instance; +	 +	/** +	 * Singleton pattern. +	 * @return ElggAutoP +	 */ +	public static function getInstance() { +		$className = __CLASS__; +		if (!(self::$instance instanceof $className)) { +			self::$instance = new $className(); +		} +		return self::$instance; +	} +	 +	/** +	 * Create wrapper P and BR elements in HTML depending on newlines. Useful when +	 * users use newlines to signal line and paragraph breaks. In all cases output +	 * should be well-formed markup. +	 * +	 * In DIV, LI, TD, and TH elements, Ps are only added when their would be at +	 * least two of them. +	 * +	 * @param string $html snippet +	 * @return string|false output or false if parse error occurred +	 */ +	public function process($html) { +		// normalize whitespace +		$html = str_replace(array("\r\n", "\r"), "\n", $html); + +		// allows preserving entities untouched +		$html = str_replace('&', $this->_unique . 'AMP', $html); + +		$this->_doc = new DOMDocument(); +	    +		// parse to DOM, suppressing loadHTML warnings +		// http://www.php.net/manual/en/domdocument.loadhtml.php#95463 +		libxml_use_internal_errors(true); + +		if (!$this->_doc->loadHTML("<html><meta http-equiv='content-type' "  +				. "content='text/html; charset={$this->encoding}'><body>{$html}</body>" +				. "</html>")) { +			return false; +		} + +		$this->_xpath = new DOMXPath($this->_doc); +		// start processing recursively at the BODY element +		$nodeList = $this->_xpath->query('//body[1]'); +		$this->_addParagraphs($nodeList->item(0)); + +		// serialize back to HTML +		$html = $this->_doc->saveHTML(); + +		// split AUTOPs into multiples at /\n\n+/ +		$html = preg_replace('/(' . $this->_unique . 'NL){2,}/', '</autop><autop>', $html); +		$html = str_replace(array($this->_unique . 'BR', $this->_unique . 'NL', '<br>'),  +				'<br />', +				$html); +		$html = str_replace('<br /></autop>', '</autop>', $html); + +		// re-parse so we can handle new AUTOP elements + +		if (!$this->_doc->loadHTML($html)) { +			return false; +		} +		// must re-create XPath object after DOM load +		$this->_xpath = new DOMXPath($this->_doc); + +		// strip AUTOPs that only have comments/whitespace +		foreach ($this->_xpath->query('//autop') as $autop) { +			$hasContent = false; +			if (trim($autop->textContent) !== '') { +				$hasContent = true; +			} else { +				foreach ($autop->childNodes as $node) { +					if ($node->nodeType === XML_ELEMENT_NODE) { +						$hasContent = true; +						break; +					} +				} +			} +			if (!$hasContent) { +				// strip w/ preg_replace later (faster than moving nodes out) +				$autop->setAttribute("r", "1"); +			} +		} + +		// remove a single AUTOP inside certain elements +		foreach ($this->_xpath->query('//div') as $el) { +			$autops = $this->_xpath->query('./autop', $el); +			if ($autops->length === 1) { +				// strip w/ preg_replace later (faster than moving nodes out) +				$autops->item(0)->setAttribute("r", "1"); +			} +		} + +		$html = $this->_doc->saveHTML(); + +		// trim to the contents of BODY +		$bodyStart = strpos($html, '<body>'); +		$bodyEnd = strpos($html, '</body>', $bodyStart + 6); +		$html = substr($html, $bodyStart + 6, $bodyEnd - $bodyStart - 6); +		 +		// strip AUTOPs that should be removed +		$html = preg_replace('@<autop r="1">(.*?)</autop>@', '\\1', $html); + +		// commit to converting AUTOPs to Ps +		$html = str_replace('<autop>', "\n<p>", $html); +		$html = str_replace('</autop>', "</p>\n", $html); +		 +		$html = str_replace('<br>', '<br />', $html); +		$html = str_replace($this->_unique . 'AMP', '&', $html); +		return $html; +	} + +	/** +	 * Add P and BR elements as necessary +	 * +	 * @param DOMElement $el +	 */ +	protected function _addParagraphs(DOMElement $el) { +		// no need to recurse, just queue up +		$elsToProcess = array($el); +		$inlinesToProcess = array(); +		while ($el = array_shift($elsToProcess)) { +			// if true, we can alter all child nodes, if not, we'll just call +			// _addParagraphs on each element in the descendInto list +			$alterInline = in_array($el->nodeName, $this->_alterList); + +			// inside affected elements, we want to trim leading whitespace from +			// the first text node +			$ltrimFirstTextNode = true; + +			// should we open a new AUTOP element to move inline elements into? +			$openP = true; +			$autop = null; + +			// after BR, ignore a newline +			$isFollowingBr = false; + +			$node = $el->firstChild; +			while (null !== $node) { +				if ($alterInline) { +					if ($openP) { +						$openP = false; +						// create a P to move inline content into (this may be removed later) +						$autop = $el->insertBefore($this->_doc->createElement('autop'), $node); +					} +				} + +				$isElement = ($node->nodeType === XML_ELEMENT_NODE); +				if ($isElement) { +					$elName = $node->nodeName; +				} +				$isBlock = ($isElement && in_array($elName, $this->_blocks)); + +				if ($alterInline) { +					$isInline = $isElement && ! $isBlock; +					$isText = ($node->nodeType === XML_TEXT_NODE); +					$isLastInline = (! $node->nextSibling +								   || ($node->nextSibling->nodeType === XML_ELEMENT_NODE +									   && in_array($node->nextSibling->nodeName, $this->_blocks))); +					if ($isElement) { +						$isFollowingBr = ($node->nodeName === 'br'); +					} + +					if ($isText) { +						$nodeText = $node->nodeValue; +						if ($ltrimFirstTextNode) { +							$nodeText = ltrim($nodeText); +							$ltrimFirstTextNode = false; +						} +						if ($isFollowingBr && preg_match('@^[ \\t]*\\n[ \\t]*@', $nodeText, $m)) { +							// if a user ends a line with <br>, don't add a second BR +							$nodeText = substr($nodeText, strlen($m[0])); +						} +						if ($isLastInline) { +							$nodeText = rtrim($nodeText); +						} +						$nodeText = str_replace("\n", $this->_unique . 'NL', $nodeText); +						$tmpNode = $node; +						$node = $node->nextSibling; // move loop to next node + +						// alter node in place, then move into AUTOP +						$tmpNode->nodeValue = $nodeText; +						$autop->appendChild($tmpNode); + +						continue; +					} +				} +				if ($isBlock || ! $node->nextSibling) { +					if ($isBlock) { +						if (in_array($node->nodeName, $this->_descendList)) { +							$elsToProcess[] = $node; +							//$this->_addParagraphs($node); +						} +					} +					$openP = true; +					$ltrimFirstTextNode = true; +				} +				if ($alterInline) { +					if (! $isBlock) { +						$tmpNode = $node; +						if ($isElement && false !== strpos($tmpNode->textContent, "\n")) { +							$inlinesToProcess[] = $tmpNode; +						} +						$node = $node->nextSibling; +						$autop->appendChild($tmpNode); +						continue; +					} +				} + +				$node = $node->nextSibling; +			} +		} + +		// handle inline nodes +		// no need to recurse, just queue up +		while ($el = array_shift($inlinesToProcess)) { +			$ignoreLeadingNewline = false; +			foreach ($el->childNodes as $node) { +				if ($node->nodeType === XML_ELEMENT_NODE) { +					if ($node->nodeValue === 'BR') { +						$ignoreLeadingNewline = true; +					} else { +						$ignoreLeadingNewline = false; +						if (false !== strpos($node->textContent, "\n")) { +							$inlinesToProcess[] = $node; +						} +					} +					continue; +				} elseif ($node->nodeType === XML_TEXT_NODE) { +					$text = $node->nodeValue; +					if ($text[0] === "\n" && $ignoreLeadingNewline) { +						$text = substr($text, 1); +						$ignoreLeadingNewline = false; +					} +					$node->nodeValue = str_replace("\n", $this->_unique . 'BR', $text); +				} +			} +		} +	} +} diff --git a/engine/classes/ElggDiskFilestore.php b/engine/classes/ElggDiskFilestore.php index f00376481..7aace43ba 100644 --- a/engine/classes/ElggDiskFilestore.php +++ b/engine/classes/ElggDiskFilestore.php @@ -200,18 +200,18 @@ class ElggDiskFilestore extends ElggFilestore {  	 * @return string The full path of where the file is stored  	 */  	public function getFilenameOnFilestore(ElggFile $file) { -		$owner = $file->getOwnerEntity(); -		if (!$owner) { -			$owner = elgg_get_logged_in_user_entity(); +		$owner_guid = $file->getOwnerGuid(); +		if (!$owner_guid) { +			$owner_guid = elgg_get_logged_in_user_guid();  		} -		if (!$owner) { +		if (!$owner_guid) {  			$msg = elgg_echo('InvalidParameterException:MissingOwner',  				array($file->getFilename(), $file->guid));  			throw new InvalidParameterException($msg);  		} -		return $this->dir_root . $this->makefileMatrix($owner->guid) . $file->getFilename(); +		return $this->dir_root . $this->makefileMatrix($owner_guid) . $file->getFilename();  	}  	/** diff --git a/engine/classes/ElggGroup.php b/engine/classes/ElggGroup.php index 121186196..ea257f368 100644 --- a/engine/classes/ElggGroup.php +++ b/engine/classes/ElggGroup.php @@ -324,37 +324,18 @@ class ElggGroup extends ElggEntity  	 * @return bool  	 */  	protected function load($guid) { -		// Test to see if we have the generic stuff -		if (!parent::load($guid)) { -			return false; -		} +		$attr_loader = new ElggAttributeLoader(get_class(), 'group', $this->attributes); +		$attr_loader->requires_access_control = !($this instanceof ElggPlugin); +		$attr_loader->secondary_loader = 'get_group_entity_as_row'; -		// Only work with GUID from here -		if ($guid instanceof stdClass) { -			$guid = $guid->guid; -		} - -		// Check the type -		if ($this->attributes['type'] != 'group') { -			$msg = elgg_echo('InvalidClassException:NotValidElggStar', array($guid, get_class())); -			throw new InvalidClassException($msg); -		} - -		// 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; +		$attrs = $attr_loader->getRequiredAttributes($guid); +		if (!$attrs) { +			return false;  		} -		// guid needs to be an int  http://trac.elgg.org/ticket/4111 -		$this->attributes['guid'] = (int)$this->attributes['guid']; +		$this->attributes = $attrs; +		$this->attributes['tables_loaded'] = 2; +		cache_entity($this);  		return true;  	} diff --git a/engine/classes/ElggGroupItemVisibility.php b/engine/classes/ElggGroupItemVisibility.php new file mode 100644 index 000000000..2c7e2abb4 --- /dev/null +++ b/engine/classes/ElggGroupItemVisibility.php @@ -0,0 +1,93 @@ +<?php + +/** + * Determines if otherwise visible items should be hidden from a user due to group + * policy or visibility. + * + * @class      ElggGroupItemVisibility + * @package    Elgg.Core + * @subpackage Groups + * + * @access private + */ +class ElggGroupItemVisibility { + +	const REASON_MEMBERSHIP = 'membershiprequired'; +	const REASON_LOGGEDOUT = 'loggedinrequired'; +	const REASON_NOACCESS = 'noaccess'; + +	/** +	 * @var bool +	 */ +	public $shouldHideItems = false; + +	/** +	 * @var string +	 */ +	public $reasonHidden = ''; + +	/** +	 * Determine visibility of items within a container for the current user +	 * +	 * @param int $container_guid GUID of a container (may/may not be a group) +	 * +	 * @return ElggGroupItemVisibility +	 * +	 * @todo Make this faster, considering it must run for every river item. +	 */ +	static public function factory($container_guid) { +		// cache because this may be called repeatedly during river display, and +		// due to need to check group visibility, cache will be disabled for some +		// get_entity() calls +		static $cache = array(); + +		$ret = new ElggGroupItemVisibility(); + +		if (!$container_guid) { +			return $ret; +		} + +		$user = elgg_get_logged_in_user_entity(); +		$user_guid = $user ? $user->guid : 0; + +		$container_guid = (int) $container_guid; + +		$cache_key = "$container_guid|$user_guid"; +		if (empty($cache[$cache_key])) { +			// compute + +			$container = get_entity($container_guid); +			$is_visible = (bool) $container; + +			if (!$is_visible) { +				// see if it *really* exists... +				$prev_access = elgg_set_ignore_access(); +				$container = get_entity($container_guid); +				elgg_set_ignore_access($prev_access); +			} + +			if ($container && $container instanceof ElggGroup) { +				/* @var ElggGroup $container */ + +				if ($is_visible) { +					if (!$container->isPublicMembership()) { +						if ($user) { +							if (!$container->isMember($user) && !$user->isAdmin()) { +								$ret->shouldHideItems = true; +								$ret->reasonHidden = self::REASON_MEMBERSHIP; +							} +						} else { +							$ret->shouldHideItems = true; +							$ret->reasonHidden = self::REASON_LOGGEDOUT; +						} +					} +				} else { +					$ret->shouldHideItems = true; +					$ret->reasonHidden = self::REASON_NOACCESS; +				} +			} +			$cache[$cache_key] = $ret; +		} +		return $cache[$cache_key]; +	} +} diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php index df0f9147f..d9a704dd9 100644 --- a/engine/classes/ElggMenuBuilder.php +++ b/engine/classes/ElggMenuBuilder.php @@ -158,7 +158,7 @@ class ElggMenuBuilder {  		// scan looking for a selected item  		foreach ($this->menu as $menu_item) {  			if ($menu_item->getHref()) { -				if (elgg_http_url_is_identical(full_url(), $menu_item->getHref())) { +				if (elgg_http_url_is_identical(current_page_url(), $menu_item->getHref())) {  					$menu_item->setSelected(true);  					return $menu_item;  				} @@ -205,7 +205,7 @@ class ElggMenuBuilder {  		// sort each section  		foreach ($this->menu as $index => $section) {  			foreach ($section as $key => $node) { -				$section[$key]->original_order = $key; +				$section[$key]->setData('original_order', $key);  			}  			usort($section, $sort_callback);  			$this->menu[$index] = $section; @@ -240,7 +240,7 @@ class ElggMenuBuilder {  		$result = strnatcmp($at, $bt);  		if ($result === 0) { -			return $a->original_order - $b->original_order; +			return $a->getData('original_order') - $b->getData('original_order');  		}  		return $result;  	} @@ -258,7 +258,7 @@ class ElggMenuBuilder {  		$result = strcmp($an, $bn);  		if ($result === 0) { -			return $a->original_order - $b->original_order; +			return $a->getData('original_order') - $b->getData('original_order');  		}  		return $result;  	} @@ -275,7 +275,7 @@ class ElggMenuBuilder {  		$bw = $b->getWeight();  		if ($aw == $bw) { -			return $a->original_order - $b->original_order; +			return $a->getData('original_order') - $b->getData('original_order');  		}  		return $aw - $bw;  	} diff --git a/engine/classes/ElggMenuItem.php b/engine/classes/ElggMenuItem.php index fe25f3ddd..81ce6c099 100644 --- a/engine/classes/ElggMenuItem.php +++ b/engine/classes/ElggMenuItem.php @@ -543,7 +543,7 @@ class ElggMenuItem {  	 */  	public function sortChildren($sortFunction) {  		foreach ($this->data['children'] as $key => $node) { -			$this->data['children'][$key]->original_order = $key; +			$this->data['children'][$key]->data['original_order'] = $key;  		}  		usort($this->data['children'], $sortFunction);  	} diff --git a/engine/classes/ElggObject.php b/engine/classes/ElggObject.php index fa6296c8c..6263f84f6 100644 --- a/engine/classes/ElggObject.php +++ b/engine/classes/ElggObject.php @@ -99,37 +99,18 @@ class ElggObject extends ElggEntity {  	 * @throws InvalidClassException  	 */  	protected function load($guid) { -		// Load data from entity table if needed -		if (!parent::load($guid)) { -			return false; -		} +		$attr_loader = new ElggAttributeLoader(get_class(), 'object', $this->attributes); +		$attr_loader->requires_access_control = !($this instanceof ElggPlugin); +		$attr_loader->secondary_loader = 'get_object_entity_as_row'; -		// Only work with GUID from here -		if ($guid instanceof stdClass) { -			$guid = $guid->guid; -		} - -		// Check the type -		if ($this->attributes['type'] != 'object') { -			$msg = elgg_echo('InvalidClassException:NotValidElggStar', array($guid, get_class())); -			throw new InvalidClassException($msg); -		} - -		// 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; +		$attrs = $attr_loader->getRequiredAttributes($guid); +		if (!$attrs) { +			return false;  		} -		// guid needs to be an int  http://trac.elgg.org/ticket/4111 -		$this->attributes['guid'] = (int)$this->attributes['guid']; +		$this->attributes = $attrs; +		$this->attributes['tables_loaded'] = 2; +		cache_entity($this);  		return true;  	} @@ -149,7 +130,7 @@ class ElggObject extends ElggEntity {  		// Save ElggObject-specific attributes  		return create_object_entity($this->get('guid'), $this->get('title'), -			$this->get('description'), $this->get('container_guid')); +			$this->get('description'));  	}  	/** diff --git a/engine/classes/ElggPAM.php b/engine/classes/ElggPAM.php index 0681a909b..f07095fc1 100644 --- a/engine/classes/ElggPAM.php +++ b/engine/classes/ElggPAM.php @@ -53,11 +53,17 @@ class ElggPAM {  		foreach ($_PAM_HANDLERS[$this->policy] as $k => $v) {  			$handler = $v->handler; +			if (!is_callable($handler)) { +				continue; +			} +			/* @var callable $handler */ +  			$importance = $v->importance;  			try {  				// Execute the handler -				$result = $handler($credentials); +				// @todo don't assume $handler is a global function +				$result = call_user_func($handler, $credentials);  				if ($result) {  					$authenticated = true;  				} elseif ($result === false) { diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index 3e43c8e81..8f71b79a8 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -36,8 +36,9 @@ class ElggPlugin extends ElggObject {  	 * @warning Unlike other ElggEntity objects, you cannot null instantiate  	 *          ElggPlugin. You must point it to an actual plugin GUID or location.  	 * -	 * @param mixed $plugin The GUID of the ElggPlugin object or the path of -	 *                      the plugin to load. +	 * @param mixed $plugin The GUID of the ElggPlugin object or the path of the plugin to load. +	 * +	 * @throws PluginException  	 */  	public function __construct($plugin) {  		if (!$plugin) { @@ -76,64 +77,8 @@ class ElggPlugin extends ElggObject {  			// load the rest of the plugin  			parent::__construct($existing_guid);  		} -	} - -	/** -	 * Overridden from ElggEntity and ElggObject::load(). Core always inits plugins with -	 * a query joined to the objects_entity table, so all the info is there. -	 * -	 * @param mixed $guid GUID of an ElggObject or the stdClass object from entities table -	 * -	 * @return bool -	 * @throws InvalidClassException -	 */ -	protected function load($guid) { - -		$expected_attributes = $this->attributes; -		unset($expected_attributes['tables_split']); -		unset($expected_attributes['tables_loaded']); - -		// this was loaded with a full join -		$needs_loaded = false; - -		if ($guid instanceof stdClass) { -			$row = (array) $guid; -			$missing_attributes = array_diff_key($expected_attributes, $row); -			if ($missing_attributes) { -				$needs_loaded = true; -				$guid = $row['guid']; -			} else { -				$this->attributes = $row; -			} -		} else { -			$needs_loaded = true; -		} -		if ($needs_loaded) { -			$entity = (array) get_entity_as_row($guid); -			$object = (array) get_object_entity_as_row($guid); - -			if (!$entity || !$object) { -				return false; -			} -			 -			$this->attributes = array_merge($this->attributes, $entity, $object); -		} - -		$this->attributes['tables_loaded'] = 2; - -		// Check the type -		if ($this->attributes['type'] != 'object') { -			$msg = elgg_echo('InvalidClassException:NotValidElggStar', array($guid, get_class())); -			throw new InvalidClassException($msg); -		} - -		// guid needs to be an int  http://trac.elgg.org/ticket/4111 -		$this->attributes['guid'] = (int)$this->attributes['guid']; - -		cache_entity($this); - -		return true; +		_elgg_cache_plugin_by_id($this);  	}  	/** @@ -358,10 +303,7 @@ class ElggPlugin extends ElggObject {  			$return = array();  			foreach ($private_settings as $setting) { -				$name = substr($setting->name, $ps_prefix_len); -				$value = $setting->value; - -				$return[$name] = $value; +				$return[$setting->name] = $setting->value;  			}  			return $return; diff --git a/engine/classes/ElggPluginManifest.php b/engine/classes/ElggPluginManifest.php index a4f5bb95d..6912c2b08 100644 --- a/engine/classes/ElggPluginManifest.php +++ b/engine/classes/ElggPluginManifest.php @@ -130,7 +130,7 @@ class ElggPluginManifest {  		}  		// see if we need to construct the xml object. -		if ($manifest instanceof XmlElement) { +		if ($manifest instanceof ElggXMLElement) {  			$manifest_obj = $manifest;  		} else {  			if (substr(trim($manifest), 0, 1) == '<') { diff --git a/engine/classes/ElggPluginManifestParser.php b/engine/classes/ElggPluginManifestParser.php index b0480d4d8..af152b561 100644 --- a/engine/classes/ElggPluginManifestParser.php +++ b/engine/classes/ElggPluginManifestParser.php @@ -53,10 +53,10 @@ abstract class ElggPluginManifestParser {  	/**  	 * Loads the manifest XML to be parsed.  	 * -	 * @param XmlElement $xml    The Manifest XML object to be parsed -	 * @param object     $caller The object calling this parser. +	 * @param ElggXmlElement $xml    The Manifest XML object to be parsed +	 * @param object         $caller The object calling this parser.  	 */ -	public function __construct(XmlElement $xml, $caller) { +	public function __construct(ElggXMLElement $xml, $caller) {  		$this->manifestObject = $xml;  		$this->caller = $caller;  	} diff --git a/engine/classes/ElggSession.php b/engine/classes/ElggSession.php index 13a33736c..9750f063e 100644 --- a/engine/classes/ElggSession.php +++ b/engine/classes/ElggSession.php @@ -54,7 +54,7 @@ class ElggSession implements ArrayAccess {  	 *  	 * @param mixed $key Name  	 * -	 * @return void +	 * @return mixed  	 */  	function offsetGet($key) {  		if (!ElggSession::$__localcache) { @@ -98,7 +98,7 @@ class ElggSession implements ArrayAccess {  	 *  	 * @param int $offset Offset  	 * -	 * @return int +	 * @return bool  	 */  	function offsetExists($offset) {  		if (isset(ElggSession::$__localcache[$offset])) { @@ -112,6 +112,8 @@ class ElggSession implements ArrayAccess {  		if ($this->offsetGet($offset)) {  			return true;  		} + +		return false;  	} @@ -132,10 +134,10 @@ class ElggSession implements ArrayAccess {  	 * @param string $key   Name  	 * @param mixed  $value Value  	 * -	 * @return mixed +	 * @return void  	 */  	function set($key, $value) { -		return $this->offsetSet($key, $value); +		$this->offsetSet($key, $value);  	}  	/** @@ -143,9 +145,9 @@ class ElggSession implements ArrayAccess {  	 *  	 * @param string $key Name  	 * -	 * @return bool +	 * @return void  	 */  	function del($key) { -		return $this->offsetUnset($key); +		$this->offsetUnset($key);  	}  } diff --git a/engine/classes/ElggSite.php b/engine/classes/ElggSite.php index 401939005..1fe49b85c 100644 --- a/engine/classes/ElggSite.php +++ b/engine/classes/ElggSite.php @@ -117,37 +117,18 @@ class ElggSite extends ElggEntity {  	 * @throws InvalidClassException  	 */  	protected function load($guid) { -		// Test to see if we have the generic stuff -		if (!parent::load($guid)) { -			return false; -		} - -		// Only work with GUID from here -		if ($guid instanceof stdClass) { -			$guid = $guid->guid; -		} - -		// Check the type -		if ($this->attributes['type'] != 'site') { -			$msg = elgg_echo('InvalidClassException:NotValidElggStar', array($guid, get_class())); -			throw new InvalidClassException($msg); -		} +		$attr_loader = new ElggAttributeLoader(get_class(), 'site', $this->attributes); +		$attr_loader->requires_access_control = !($this instanceof ElggPlugin); +		$attr_loader->secondary_loader = 'get_site_entity_as_row'; -		// 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; +		$attrs = $attr_loader->getRequiredAttributes($guid); +		if (!$attrs) { +			return false;  		} -		// guid needs to be an int  http://trac.elgg.org/ticket/4111 -		$this->attributes['guid'] = (int)$this->attributes['guid']; +		$this->attributes = $attrs; +		$this->attributes['tables_loaded'] = 2; +		cache_entity($this);  		return true;  	} @@ -381,6 +362,11 @@ class ElggSite extends ElggEntity {  	public function checkWalledGarden() {  		global $CONFIG; +		// command line calls should not invoke the walled garden check +		if (PHP_SAPI === 'cli') { +			return; +		} +  		if ($CONFIG->walled_garden) {  			if ($CONFIG->default_access == ACCESS_PUBLIC) {  				$CONFIG->default_access = ACCESS_LOGGED_IN; diff --git a/engine/classes/ElggStaticVariableCache.php b/engine/classes/ElggStaticVariableCache.php index 787d35a32..17d849400 100644 --- a/engine/classes/ElggStaticVariableCache.php +++ b/engine/classes/ElggStaticVariableCache.php @@ -21,8 +21,8 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache {  	 * 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! +	 * @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); diff --git a/engine/classes/ElggUser.php b/engine/classes/ElggUser.php index d7bb89265..6c1cdc1de 100644 --- a/engine/classes/ElggUser.php +++ b/engine/classes/ElggUser.php @@ -106,37 +106,17 @@ class ElggUser extends ElggEntity  	 * @return bool  	 */  	protected function load($guid) { -		// Test to see if we have the generic stuff -		if (!parent::load($guid)) { -			return false; -		} +		$attr_loader = new ElggAttributeLoader(get_class(), 'user', $this->attributes); +		$attr_loader->secondary_loader = 'get_user_entity_as_row'; -		// Only work with GUID from here -		if ($guid instanceof stdClass) { -			$guid = $guid->guid; -		} - -		// Check the type -		if ($this->attributes['type'] != 'user') { -			$msg = elgg_echo('InvalidClassException:NotValidElggStar', array($guid, get_class())); -			throw new InvalidClassException($msg); -		} - -		// 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; +		$attrs = $attr_loader->getRequiredAttributes($guid); +		if (!$attrs) { +			return false;  		} -		// guid needs to be an int  http://trac.elgg.org/ticket/4111 -		$this->attributes['guid'] = (int)$this->attributes['guid']; +		$this->attributes = $attrs; +		$this->attributes['tables_loaded'] = 2; +		cache_entity($this);  		return true;  	} diff --git a/engine/classes/ElggVolatileMetadataCache.php b/engine/classes/ElggVolatileMetadataCache.php index 24ae58d42..8a33c198d 100644 --- a/engine/classes/ElggVolatileMetadataCache.php +++ b/engine/classes/ElggVolatileMetadataCache.php @@ -279,6 +279,9 @@ class ElggVolatileMetadataCache {  			),  			'selects' => array('n.string AS name', 'v.string AS value'),  			'order_by' => 'n_table.entity_guid, n_table.time_created ASC', + +			// @todo don't know why this is necessary +			'wheres' => array(get_access_sql_suffix('n_table')),  		);  		$data = elgg_get_metadata($options); diff --git a/engine/classes/ElggXMLElement.php b/engine/classes/ElggXMLElement.php new file mode 100644 index 000000000..65a13912c --- /dev/null +++ b/engine/classes/ElggXMLElement.php @@ -0,0 +1,115 @@ +<?php +/** + * A parser for XML that uses SimpleXMLElement + * + * @package    Elgg.Core + * @subpackage XML + */ +class ElggXMLElement { +	/** +	 * @var SimpleXMLElement +	 */ +	private $_element; + +	/** +	 * Creates an ElggXMLParser from a string or existing SimpleXMLElement +	 *  +	 * @param string|SimpleXMLElement $xml The XML to parse +	 */ +	public function __construct($xml) { +		if ($xml instanceof SimpleXMLElement) { +			$this->_element = $xml; +		} else { +			$this->_element = new SimpleXMLElement($xml); +		} +	} + +	/** +	 * @return string The name of the element +	 */ +	public function getName() { +		return $this->_element->getName(); +	} + +	/** +	 * @return array:string The attributes +	 */ +	public function getAttributes() { +		//include namespace declarations as attributes +		$xmlnsRaw = $this->_element->getNamespaces(); +		$xmlns = array(); +		foreach ($xmlnsRaw as $key => $val) { +			$label = 'xmlns' . ($key ? ":$key" : $key); +			$xmlns[$label] = $val; +		} +		//get attributes and merge with namespaces +		$attrRaw = $this->_element->attributes(); +		$attr = array(); +		foreach ($attrRaw as $key => $val) { +			$attr[$key] = $val; +		} +		$attr = array_merge((array) $xmlns, (array) $attr); +		$result = array(); +		foreach ($attr as $key => $val) { +			$result[$key] = (string) $val; +		} +		return $result; +	} + +	/** +	 * @return string CData +	 */ +	public function getContent() { +		return (string) $this->_element; +	} + +	/** +	 * @return array:ElggXMLElement Child elements +	 */ +	public function getChildren() { +		$children = $this->_element->children(); +		$result = array(); +		foreach ($children as $val) { +			$result[] = new ElggXMLElement($val); +		} + +		return $result; +	} + +	function __get($name) { +		switch ($name) { +			case 'name': +				return $this->getName(); +				break; +			case 'attributes': +				return $this->getAttributes(); +				break; +			case 'content': +				return $this->getContent(); +				break; +			case 'children': +				return $this->getChildren(); +				break; +		} +		return null; +	} + +	function __isset($name) { +		switch ($name) { +			case 'name': +				return $this->getName() !== null; +				break; +			case 'attributes': +				return $this->getAttributes() !== null; +				break; +			case 'content': +				return $this->getContent() !== null; +				break; +			case 'children': +				return $this->getChildren() !== null; +				break; +		} +		return false; +	} + +}
\ No newline at end of file diff --git a/engine/classes/IncompleteEntityException.php b/engine/classes/IncompleteEntityException.php new file mode 100644 index 000000000..8c86edcc6 --- /dev/null +++ b/engine/classes/IncompleteEntityException.php @@ -0,0 +1,10 @@ +<?php +/** + * IncompleteEntityException + * Thrown when constructing an entity that is missing its secondary entity table + * + * @package    Elgg.Core + * @subpackage Exception + * @access private + */ +class IncompleteEntityException extends Exception {}  | 
