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 | 8 | ||||
-rw-r--r-- | engine/classes/ElggMenuItem.php | 2 | ||||
-rw-r--r-- | engine/classes/ElggObject.php | 39 | ||||
-rw-r--r-- | engine/classes/ElggPlugin.php | 58 | ||||
-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 | 37 | ||||
-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 |
18 files changed, 789 insertions, 193 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..d7f85685c 100644 --- a/engine/classes/ElggMenuBuilder.php +++ b/engine/classes/ElggMenuBuilder.php @@ -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/ElggPlugin.php b/engine/classes/ElggPlugin.php index c6ce2905f..32b5f952a 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -82,64 +82,6 @@ class ElggPlugin extends ElggObject { } /** - * 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; - } - - /** * Save the plugin object. Make sure required values exist. * * @see ElggObject::save() 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..f7f5b68ea 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; - } + $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'; - // 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); - } - - // 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; } 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 {} |