diff options
Diffstat (limited to 'engine')
61 files changed, 2179 insertions, 498 deletions
diff --git a/engine/classes/ElggBatch.php b/engine/classes/ElggBatch.php index 3d01133fa..0cb13eb32 100644 --- a/engine/classes/ElggBatch.php +++ b/engine/classes/ElggBatch.php @@ -3,47 +3,51 @@ * Efficiently run operations on batches of results for any function * that supports an options array. * - * This is usually used with elgg_get_entities() and friends, elgg_get_annotations() - * and elgg_get_metadata(). + * This is usually used with elgg_get_entities() and friends, + * elgg_get_annotations(), and elgg_get_metadata(). * - * If you pass a valid PHP callback, all results will be run through that callback. - * You can still foreach() through the result set after. Valid PHP callbacks - * can be a string, an array, or a closure. + * If you pass a valid PHP callback, all results will be run through that + * callback. You can still foreach() through the result set after. Valid + * PHP callbacks can be a string, an array, or a closure. * {@link http://php.net/manual/en/language.pseudo-types.php} * - * The callback function must accept 3 arguments: an entity, the getter used, and the options used. + * The callback function must accept 3 arguments: an entity, the getter + * used, and the options used. * - * Results from the callback are stored in callbackResult. - * If the callback returns only booleans, callbackResults will be the combined - * result of all calls. + * Results from the callback are stored in callbackResult. If the callback + * returns only booleans, callbackResults will be the combined result of + * all calls. * - * If the callback returns anything else, callbackresult will be an indexed array - * of whatever the callback returns. If returning error handling information, - * you should include enough information to determine which result you're referring - * to. + * If the callback returns anything else, callbackresult will be an indexed + * array of whatever the callback returns. If returning error handling + * information, you should include enough information to determine which + * result you're referring to. * * Don't combine returning bools and returning something else. * * Note that returning false will not stop the foreach. * + * @warning If your callback or foreach loop deletes or disable entities + * you MUST call setIncrementOffset(false) or set that when instantiating. + * This forces the offset to stay what it was in the $options array. + * * @example * <code> + * // using foreach * $batch = new ElggBatch('elgg_get_entities', array()); + * $batch->setIncrementOffset(false); * * foreach ($batch as $entity) { * $entity->disable(); * } * + * // using both a callback * $callback = function($result, $getter, $options) { - * var_dump("Going to delete annotation id: $result->id"); + * var_dump("Looking at annotation id: $result->id"); * return true; * } * * $batch = new ElggBatch('elgg_get_annotations', array('guid' => 2), $callback); - * - * foreach ($batch as $annotation) { - * $annotation->delete(); - * } * </code> * * @package Elgg.Core @@ -139,6 +143,13 @@ class ElggBatch public $callbackResult = null; /** + * If false, offset will not be incremented. This is used for callbacks/loops that delete. + * + * @var bool + */ + private $incrementOffset = true; + + /** * Batches operations on any elgg_get_*() or compatible function that supports * an options array. * @@ -156,12 +167,18 @@ class ElggBatch * @param int $chunk_size The number of entities to pull in before requesting more. * You have to balance this between running out of memory in PHP * and hitting the db server too often. + * @param bool $inc_offset Increment the offset on each fetch. This must be false for + * callbacks that delete rows. You can set this after the + * object is created with {@see ElggBatch::setIncrementOffset()}. */ - public function __construct($getter, $options, $callback = null, $chunk_size = 25) { + public function __construct($getter, $options, $callback = null, $chunk_size = 25, + $inc_offset = true) { + $this->getter = $getter; $this->options = $options; $this->callback = $callback; $this->chunkSize = $chunk_size; + $this->setIncrementOffset($inc_offset); if ($this->chunkSize <= 0) { $this->chunkSize = 25; @@ -174,7 +191,7 @@ class ElggBatch // if passed a callback, create a new ElggBatch with the same options // and pass each to the callback. if ($callback && is_callable($callback)) { - $batch = new ElggBatch($getter, $options, null, $chunk_size); + $batch = new ElggBatch($getter, $options, null, $chunk_size, $inc_offset); $all_results = null; @@ -245,9 +262,15 @@ class ElggBatch } } + if ($this->incrementOffset) { + $offset = $this->offset + $this->retrievedResults; + } else { + $offset = $this->offset; + } + $current_options = array( 'limit' => $limit, - 'offset' => $this->offset + $this->retrievedResults + 'offset' => $offset ); $options = array_merge($this->options, $current_options); @@ -270,6 +293,16 @@ class ElggBatch } /** + * Increment the offset from the original options array? Setting to + * false is required for callbacks that delete rows. + * + * @param bool $increment + */ + public function setIncrementOffset($increment = true) { + $this->incrementOffset = (bool) $increment; + } + + /** * Implements Iterator */ diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php index 2fa8e9939..dc38dafbe 100644 --- a/engine/classes/ElggEntity.php +++ b/engine/classes/ElggEntity.php @@ -1311,12 +1311,16 @@ abstract class ElggEntity extends ElggData implements /** * Loads attributes from the entities table into the object. * - * @param int $guid GUID of Entity + * @param mixed $guid GUID of entity or stdClass object from entities table * * @return bool */ protected function load($guid) { - $row = get_entity_as_row($guid); + if ($guid instanceof stdClass) { + $row = $guid; + } else { + $row = get_entity_as_row($guid); + } if ($row) { // Create the array if necessary - all subclasses should test before creating diff --git a/engine/classes/ElggFileCache.php b/engine/classes/ElggFileCache.php index 8304372dc..34178d452 100644 --- a/engine/classes/ElggFileCache.php +++ b/engine/classes/ElggFileCache.php @@ -161,12 +161,25 @@ class ElggFileCache extends ElggCache { } /** - * This was probably meant to delete everything? + * Delete all files in the directory of this file cache * * @return void */ public function clear() { - // @todo writeme + $dir = $this->getVariable("cache_path"); + + $exclude = array(".", ".."); + + $files = scandir($dir); + if (!$files) { + return; + } + + foreach ($files as $f) { + if (!in_array($f, $exclude)) { + unlink($dir . $f); + } + } } /** @@ -184,7 +197,7 @@ class ElggFileCache extends ElggCache { return; } - $exclude = array(".",".."); + $exclude = array(".", ".."); $files = scandir($dir); if (!$files) { diff --git a/engine/classes/ElggGroup.php b/engine/classes/ElggGroup.php index ab223e1a4..f7f67bf41 100644 --- a/engine/classes/ElggGroup.php +++ b/engine/classes/ElggGroup.php @@ -29,12 +29,12 @@ class ElggGroup extends ElggEntity } /** - * Construct a new user entity, optionally from a given id value. + * Construct a new group entity, optionally from a given guid value. * * @param mixed $guid If an int, load that GUID. - * If a db row then will attempt to load the rest of the data. + * If an entity table db row, then will load the rest of the data. * - * @throws Exception if there was a problem creating the user. + * @throws Exception if there was a problem creating the group. */ function __construct($guid = null) { $this->initializeAttributes(); @@ -43,15 +43,15 @@ class ElggGroup extends ElggEntity $this->initialise_attributes(false); if (!empty($guid)) { - // Is $guid is a DB row - either a entity row, or a user table row. + // Is $guid is a entity table DB row if ($guid instanceof stdClass) { // Load the rest - if (!$this->load($guid->guid)) { + if (!$this->load($guid)) { $msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid)); throw new IOException($msg); } - // Is $guid is an ElggGroup? Use a copy constructor + // 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); @@ -59,11 +59,11 @@ class ElggGroup extends ElggEntity $this->attributes[$key] = $value; } - // Is this is an ElggEntity but not an ElggGroup = ERROR! + // 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 + // Is it a GUID } else if (is_numeric($guid)) { if (!$this->load($guid)) { throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid))); @@ -319,11 +319,9 @@ class ElggGroup extends ElggEntity } /** - * 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. + * Load the ElggGroup data from the database * - * @param int $guid GUID of an ElggGroup entity + * @param mixed $guid GUID of an ElggGroup entity or database row from entity table * * @return bool */ @@ -333,6 +331,11 @@ class ElggGroup extends ElggEntity return false; } + // 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())); diff --git a/engine/classes/ElggObject.php b/engine/classes/ElggObject.php index 649e32490..b4bae6825 100644 --- a/engine/classes/ElggObject.php +++ b/engine/classes/ElggObject.php @@ -41,12 +41,12 @@ class ElggObject extends ElggEntity { * * If no arguments are passed, create a new entity. * - * If an argument is passed attempt to load a full Object entity. Arguments - * can be: + * If an argument is passed, attempt to load a full ElggObject entity. + * Arguments can be: * - The GUID of an object entity. - * - A DB result object with a guid property + * - A DB result object from the entities table with a guid property * - * @param mixed $guid If an int, load that GUID. If a db row then will attempt to + * @param mixed $guid If an int, load that GUID. If a db row, then will attempt to * load the rest of the data. * * @throws IOException If passed an incorrect guid @@ -59,15 +59,15 @@ class ElggObject extends ElggEntity { $this->initialise_attributes(false); if (!empty($guid)) { - // Is $guid is a DB row - either a entity row, or a object table row. + // Is $guid is a DB row from the entity table if ($guid instanceof stdClass) { // Load the rest - if (!$this->load($guid->guid)) { + if (!$this->load($guid)) { $msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid)); throw new IOException($msg); } - // Is $guid is an ElggObject? Use a copy constructor + // 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); @@ -75,11 +75,11 @@ class ElggObject extends ElggEntity { $this->attributes[$key] = $value; } - // Is this is an ElggEntity but not an ElggObject = ERROR! + // 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 + // Is it a GUID } else if (is_numeric($guid)) { if (!$this->load($guid)) { throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid))); @@ -93,17 +93,22 @@ class ElggObject extends ElggEntity { /** * Loads the full ElggObject when given a guid. * - * @param int $guid Guid of an ElggObject + * @param mixed $guid GUID of an ElggObject or the stdClass object from entities table * * @return bool * @throws InvalidClassException */ protected function load($guid) { - // Test to see if we have the generic stuff + // Load data from entity table if needed 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'] != 'object') { $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($guid, get_class())); diff --git a/engine/classes/ElggPluginManifest.php b/engine/classes/ElggPluginManifest.php index 7592eb667..7aa702d47 100644 --- a/engine/classes/ElggPluginManifest.php +++ b/engine/classes/ElggPluginManifest.php @@ -592,4 +592,23 @@ class ElggPluginManifest { return $return; } + + /** + * Returns a category's friendly name. This can be localized by + * defining the string 'admin:plugins:category:<category>'. If no + * localization is found, returns the category with _ and - converted to ' ' + * and then ucwords()'d. + * + * @param str $category The category as defined in the manifest. + * @return str A human-readable category + */ + static public function getFriendlyCategory($category) { + $cat_raw_string = "admin:plugins:category:$category"; + $cat_display_string = elgg_echo($cat_raw_string); + if ($cat_display_string == $cat_raw_string) { + $category = str_replace(array('-', '_'), ' ', $category); + $cat_display_string = ucwords($category); + } + return $cat_display_string; + } } diff --git a/engine/classes/ElggSite.php b/engine/classes/ElggSite.php index ac0c87bf7..6d07778a9 100644 --- a/engine/classes/ElggSite.php +++ b/engine/classes/ElggSite.php @@ -57,8 +57,8 @@ class ElggSite extends ElggEntity { * - A URL as stored in ElggSite->url * - A DB result object with a guid property * - * @param mixed $guid If an int, load that GUID. If a db row then will attempt - * to load the rest of the data. + * @param mixed $guid If an int, load that GUID. If a db row then will + * load the rest of the data. * * @throws IOException If passed an incorrect guid * @throws InvalidParameterException If passed an Elgg* Entity that isn't an ElggSite @@ -70,15 +70,15 @@ class ElggSite extends ElggEntity { $this->initialise_attributes(false); if (!empty($guid)) { - // Is $guid is a DB row - either a entity row, or a site table row. + // Is $guid is a DB entity table row if ($guid instanceof stdClass) { // Load the rest - if (!$this->load($guid->guid)) { + if (!$this->load($guid)) { $msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid)); throw new IOException($msg); } - // Is $guid is an ElggSite? Use a copy constructor + // 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); @@ -86,18 +86,18 @@ class ElggSite extends ElggEntity { $this->attributes[$key] = $value; } - // Is this is an ElggEntity but not an ElggSite = ERROR! + // 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 + // 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 + // Is it a GUID } else if (is_numeric($guid)) { if (!$this->load($guid)) { throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid))); @@ -111,7 +111,7 @@ class ElggSite extends ElggEntity { /** * Loads the full ElggSite when given a guid. * - * @param int $guid Guid of ElggSite entity + * @param mixed $guid GUID of ElggSite entity or database row object * * @return bool * @throws InvalidClassException @@ -122,6 +122,11 @@ class ElggSite extends ElggEntity { 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())); @@ -417,6 +422,7 @@ class ElggSite extends ElggEntity { // default public pages $defaults = array( + 'walled_garden/.*', 'action/login', 'register', 'action/register', @@ -427,10 +433,14 @@ class ElggSite extends ElggEntity { 'action/security/refreshtoken', 'ajax/view/js/languages', 'upgrade\.php', + 'xml-rpc\.php', + 'mt/mt-xmlrpc\.cgi', 'css/.*', 'js/.*', 'cache/css/.*', 'cache/js/.*', + 'cron/.*', + 'services/.*', ); // include a hook for plugin authors to include public pages diff --git a/engine/classes/ElggUser.php b/engine/classes/ElggUser.php index bdf57c2c3..d7bb89265 100644 --- a/engine/classes/ElggUser.php +++ b/engine/classes/ElggUser.php @@ -47,7 +47,7 @@ class ElggUser extends ElggEntity * 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. + * If an entity table db row then will load the rest of the data. * * @throws Exception if there was a problem creating the user. */ @@ -58,15 +58,15 @@ class ElggUser extends ElggEntity $this->initialise_attributes(false); if (!empty($guid)) { - // Is $guid is a DB row - either a entity row, or a user table row. + // Is $guid is a DB entity row if ($guid instanceof stdClass) { // Load the rest - if (!$this->load($guid->guid)) { + if (!$this->load($guid)) { $msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid)); throw new IOException($msg); } - // See if this is a username + // See if this is a username } else if (is_string($guid)) { $user = get_user_by_username($guid); if ($user) { @@ -75,7 +75,7 @@ class ElggUser extends ElggEntity } } - // Is $guid is an ElggUser? Use a copy constructor + // 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); @@ -83,11 +83,11 @@ class ElggUser extends ElggEntity $this->attributes[$key] = $value; } - // Is this is an ElggEntity but not an ElggUser = ERROR! + // 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 + // Is it a GUID } else if (is_numeric($guid)) { if (!$this->load($guid)) { throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid))); @@ -99,11 +99,9 @@ class ElggUser extends ElggEntity } /** - * 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. + * Load the ElggUser data from the database * - * @param int $guid ElggUser GUID + * @param mixed $guid ElggUser GUID or stdClass database row from entity table * * @return bool */ @@ -113,6 +111,11 @@ class ElggUser extends ElggEntity return false; } + // 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())); @@ -384,7 +387,6 @@ class ElggUser extends ElggEntity 'relationship' => 'friend', 'relationship_guid' => $this->guid, 'limit' => $limit, - 'offset' => get_input('offset', 0), 'full_view' => false, ); @@ -458,7 +460,14 @@ class ElggUser extends ElggEntity * @return array|false */ public function getObjects($subtype = "", $limit = 10, $offset = 0) { - return get_user_objects($this->getGUID(), $subtype, $limit, $offset); + $params = array( + 'type' => 'object', + 'subtype' => $subtype, + 'owner_guid' => $this->getGUID(), + 'limit' => $limit, + 'offset' => $offset + ); + return elgg_get_entities($params); } /** diff --git a/engine/classes/ElggWidget.php b/engine/classes/ElggWidget.php index 7914fa140..99708f66a 100644 --- a/engine/classes/ElggWidget.php +++ b/engine/classes/ElggWidget.php @@ -131,11 +131,21 @@ class ElggWidget extends ElggObject { usort($widgets, create_function('$a,$b','return (int)$a->order > (int)$b->order;')); + // remove widgets from inactive plugins + $widget_types = elgg_get_widget_types($this->context); + $inactive_widgets = array(); + foreach ($widgets as $index => $widget) { + if (!array_key_exists($widget->handler, $widget_types)) { + $inactive_widgets[] = $widget; + unset($widgets[$index]); + } + } + if ($rank == 0) { // top of the column - $this->order = $widgets[0]->order - 10; + $this->order = reset($widgets)->order - 10; } elseif ($rank == (count($widgets) - 1)) { - // bottom of the column + // bottom of the column of active widgets $this->order = end($widgets)->order + 10; } else { // reorder widgets @@ -147,7 +157,7 @@ class ElggWidget extends ElggObject { } } - // split the array in two and recombine with the moved array in middle + // split the array in two and recombine with the moved widget in middle $before = array_slice($widgets, 0, $rank); array_push($before, $this); $after = array_slice($widgets, $rank); @@ -159,6 +169,22 @@ class ElggWidget extends ElggObject { $order += 10; } } + + // put inactive widgets at the bottom + if ($inactive_widgets) { + $bottom = 0; + foreach ($widgets as $widget) { + if ($widget->order > $bottom) { + $bottom = $widget->order; + } + } + $bottom += 10; + foreach ($inactive_widgets as $widget) { + $widget->order = $bottom; + $bottom += 10; + } + } + $this->column = $column; } diff --git a/engine/classes/XMLRPCArrayParameter.php b/engine/classes/XMLRPCArrayParameter.php new file mode 100644 index 000000000..a8edccba7 --- /dev/null +++ b/engine/classes/XMLRPCArrayParameter.php @@ -0,0 +1,56 @@ +<?php + +/** + * An array containing other XMLRPCParameter objects. + * + * @package Elgg.Core + * @subpackage XMLRPC + * + */ +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. + * + * @return void + */ + public function addField(XMLRPCParameter $value) { + if (!is_array($this->value)) { + $this->value = array(); + } + + $this->value[] = $value; + } + + /** + * Converts XML array to string + * + * @return string + */ + function __toString() { + $params = ""; + foreach ($this->value as $value) { + $params .= "$value"; + } + + return "<array><data>$params</data></array>"; + } +} diff --git a/engine/classes/XMLRPCBase64Parameter.php b/engine/classes/XMLRPCBase64Parameter.php new file mode 100644 index 000000000..7db0a761c --- /dev/null +++ b/engine/classes/XMLRPCBase64Parameter.php @@ -0,0 +1,28 @@ +<?php +/** + * A base 64 encoded blob of binary. + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +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); + } + + /** + * Convert to string + * + * @return string + */ + function __toString() { + return "<value><base64>{$value}</base64></value>"; + } +} diff --git a/engine/classes/XMLRPCBoolParameter.php b/engine/classes/XMLRPCBoolParameter.php new file mode 100644 index 000000000..607841cb8 --- /dev/null +++ b/engine/classes/XMLRPCBoolParameter.php @@ -0,0 +1,30 @@ +<?php +/** + * A boolean. + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +class XMLRPCBoolParameter extends XMLRPCParameter { + + /** + * New bool parameter + * + * @param bool $value Value + */ + function __construct($value) { + parent::__construct(); + + $this->value = (bool)$value; + } + + /** + * Convert to string + * + * @return string + */ + function __toString() { + $code = ($this->value) ? "1" : "0"; + return "<value><boolean>{$code}</boolean></value>"; + } +} diff --git a/engine/classes/XMLRPCDateParameter.php b/engine/classes/XMLRPCDateParameter.php new file mode 100644 index 000000000..93bbbd8f5 --- /dev/null +++ b/engine/classes/XMLRPCDateParameter.php @@ -0,0 +1,33 @@ +<?php +/** + * An ISO8601 data and time. + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +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(); + } + } + + /** + * Convert to string + * + * @return string + */ + function __toString() { + $value = date('c', $this->value); + return "<value><dateTime.iso8601>{$value}</dateTime.iso8601></value>"; + } +} diff --git a/engine/classes/XMLRPCDoubleParameter.php b/engine/classes/XMLRPCDoubleParameter.php new file mode 100644 index 000000000..b7834650e --- /dev/null +++ b/engine/classes/XMLRPCDoubleParameter.php @@ -0,0 +1,29 @@ +<?php +/** + * A double precision signed floating point number. + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +class XMLRPCDoubleParameter extends XMLRPCParameter { + + /** + * New XML Double + * + * @param int $value Value + */ + function __construct($value) { + parent::__construct(); + + $this->value = (float)$value; + } + + /** + * Convert to string + * + * @return string + */ + 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..425c075cc --- /dev/null +++ b/engine/classes/XMLRPCErrorResponse.php @@ -0,0 +1,36 @@ +<?php + +/** + * XMLRPC Error Response + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +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. + * + * @return string + */ + public function __toString() { + return "<methodResponse><fault><value>{$this->parameters[0]}</value></fault></methodResponse>"; + } +} diff --git a/engine/classes/XMLRPCIntParameter.php b/engine/classes/XMLRPCIntParameter.php new file mode 100644 index 000000000..0fc146165 --- /dev/null +++ b/engine/classes/XMLRPCIntParameter.php @@ -0,0 +1,29 @@ +<?php +/** + * An Integer. + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +class XMLRPCIntParameter extends XMLRPCParameter { + + /** + * A new XML int + * + * @param int $value Value + */ + function __construct($value) { + parent::__construct(); + + $this->value = (int)$value; + } + + /** + * Convert to string + * + * @return string + */ + 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..ffbad8082 --- /dev/null +++ b/engine/classes/XMLRPCParameter.php @@ -0,0 +1,16 @@ +<?php +/** + * Superclass for all RPC parameters. + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +abstract class XMLRPCParameter { + protected $value; + + /** + * Set initial values + */ + function __construct() { } + +} diff --git a/engine/classes/XMLRPCResponse.php b/engine/classes/XMLRPCResponse.php new file mode 100644 index 000000000..a6256d385 --- /dev/null +++ b/engine/classes/XMLRPCResponse.php @@ -0,0 +1,71 @@ +<?php + +/** + * XML-RPC Response. + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +abstract class XMLRPCResponse { + /** An array of parameters */ + protected $parameters = array(); + + /** + * Add a parameter here. + * + * @param XMLRPCParameter $param The parameter. + * + * @return void + */ + public function addParameter(XMLRPCParameter $param) { + if (!is_array($this->parameters)) { + $this->parameters = array(); + } + + $this->parameters[] = $param; + } + + /** + * Add an integer + * + * @param int $value Value + * + * @return void + */ + public function addInt($value) { + $this->addParameter(new XMLRPCIntParameter($value)); + } + + /** + * Add a string + * + * @param string $value Value + * + * @return void + */ + public function addString($value) { + $this->addParameter(new XMLRPCStringParameter($value)); + } + + /** + * Add a double + * + * @param int $value Value + * + * @return void + */ + public function addDouble($value) { + $this->addParameter(new XMLRPCDoubleParameter($value)); + } + + /** + * Add a boolean + * + * @param bool $value Value + * + * @return void + */ + public function addBoolean($value) { + $this->addParameter(new XMLRPCBoolParameter($value)); + } +} diff --git a/engine/classes/XMLRPCStringParameter.php b/engine/classes/XMLRPCStringParameter.php new file mode 100644 index 000000000..35b28214b --- /dev/null +++ b/engine/classes/XMLRPCStringParameter.php @@ -0,0 +1,30 @@ +<?php +/** + * A string. + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +class XMLRPCStringParameter extends XMLRPCParameter { + + /** + * A new XML string + * + * @param string $value Value + */ + function __construct($value) { + parent::__construct(); + + $this->value = $value; + } + + /** + * Convert to XML string + * + * @return string + */ + 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..694ddf5df --- /dev/null +++ b/engine/classes/XMLRPCStructParameter.php @@ -0,0 +1,55 @@ +<?php + +/** + * A structure containing other XMLRPCParameter objects. + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +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. + * + * @return void + */ + public function addField($name, XMLRPCParameter $value) { + if (!is_array($this->value)) { + $this->value = array(); + } + + $this->value[$name] = $value; + } + + /** + * Convert to string + * + * @return string + */ + function __toString() { + $params = ""; + foreach ($this->value as $k => $v) { + $params .= "<member><name>$k</name>$v</member>"; + } + + return "<value><struct>$params</struct></value>"; + } +} diff --git a/engine/classes/XMLRPCSuccessResponse.php b/engine/classes/XMLRPCSuccessResponse.php new file mode 100644 index 000000000..e02e82c5c --- /dev/null +++ b/engine/classes/XMLRPCSuccessResponse.php @@ -0,0 +1,22 @@ +<?php +/** + * Success Response + * + * @package Elgg.Core + * @subpackage XMLRPC + */ +class XMLRPCSuccessResponse extends XMLRPCResponse { + /** + * Output to XML. + * + * @return string + */ + public function __toString() { + $params = ""; + foreach ($this->parameters as $param) { + $params .= "<param>$param</param>\n"; + } + + return "<methodResponse><params>$params</params></methodResponse>"; + } +} diff --git a/engine/handlers/page_handler.php b/engine/handlers/page_handler.php index 7eca37bb1..1ed295b7d 100644 --- a/engine/handlers/page_handler.php +++ b/engine/handlers/page_handler.php @@ -13,16 +13,12 @@ * * cache * * services * * export - * * js - * * css + * * mt + * * xml-rpc.php * * rewrite.php * * tag (deprecated, reserved for backwards compatibility) * * pg (deprecated, reserved for backwards compatibility) * - * These additionally are reserved for the xml-rpc plugin - * * mt - * * xml-rpc.php - * * {@link page_handler()} explodes the pages string by / and sends it to * the page handler function as registered by {@link elgg_register_page_handler()}. * If a valid page handler isn't found, plugins have a chance to provide a 404. diff --git a/engine/handlers/xml-rpc_handler.php b/engine/handlers/xml-rpc_handler.php new file mode 100644 index 000000000..2ee29e5b7 --- /dev/null +++ b/engine/handlers/xml-rpc_handler.php @@ -0,0 +1,44 @@ +<?php +/** + * XML-RPC handler. + * + * @warning This is very old code. Does it work at all? + * + * @package Elgg.Core + * @subpackage XMLRPC + * @link http://docs.elgg.org/Tutorials/XMLRPC + * @todo Does this work? + */ + +require_once(dirname(dirname(__FILE__)) . "/start.php"); + +// Register the error handler +error_reporting(E_ALL); +set_error_handler('_php_xmlrpc_error_handler'); + +// Register a default exception handler +set_exception_handler('_php_xmlrpc_exception_handler'); + +// Set some defaults +$result = null; +set_input('view', 'xml'); // Set default view regardless + +// Get the post data +$input = get_post_data(); + +if ($input) { + // Parse structures from xml + $call = new XMLRPCCall($input); + + // Process call + $result = trigger_xmlrpc_handler($call); +} else { + throw new CallException(elgg_echo('xmlrpc:noinputdata')); +} + +if (!($result instanceof XMLRPCResponse)) { + throw new APIException(elgg_echo('APIException:ApiResultUnknown')); +} + +// Output result +echo elgg_view_page("XML-RPC", elgg_view("xml-rpc/output", array('result' => $result))); diff --git a/engine/lib/access.php b/engine/lib/access.php index dba1e1ec6..6be252c6a 100644 --- a/engine/lib/access.php +++ b/engine/lib/access.php @@ -671,8 +671,10 @@ function add_user_to_access_collection($user_guid, $collection_id) { return false; } + // if someone tries to insert the same data twice, we do a no-op on duplicate key $q = "INSERT INTO {$CONFIG->dbprefix}access_collection_membership - SET access_collection_id = {$collection_id}, user_guid = {$user_guid}"; + SET access_collection_id = $collection_id, user_guid = $user_guid + ON DUPLICATE KEY UPDATE user_guid = user_guid"; $result = insert_data($q); return $result !== false; diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php index 5049d455b..f32dee0f0 100644 --- a/engine/lib/annotations.php +++ b/engine/lib/annotations.php @@ -209,9 +209,11 @@ function elgg_get_annotations(array $options = array()) { * Deletes annotations based on $options. * * @warning Unlike elgg_get_annotations() this will not accept an empty options array! + * This requires at least one constraint: annotation_owner_guid(s), + * annotation_name(s), annotation_value(s), or guid(s) must be set. * * @param array $options An options array. {@See elgg_get_annotations()} - * @return mixed + * @return mixed Null if the metadata name is invalid. Bool on success or fail. * @since 1.8.0 */ function elgg_delete_annotations(array $options) { diff --git a/engine/lib/cache.php b/engine/lib/cache.php index e71ef332d..c117b9ec9 100644 --- a/engine/lib/cache.php +++ b/engine/lib/cache.php @@ -10,15 +10,14 @@ /* Filepath Cache */ /** - * Returns an ElggCache object suitable for caching view - * file load paths to disk under $CONFIG->dataroot. + * Returns an ElggCache object suitable for caching system information * * @todo Can this be done in a cleaner way? * @todo Swap to memcache etc? * - * @return ElggFileCache A cache object suitable for caching file load paths. + * @return ElggFileCache */ -function elgg_get_filepath_cache() { +function elgg_get_system_cache() { global $CONFIG; /** @@ -27,36 +26,34 @@ function elgg_get_filepath_cache() { static $FILE_PATH_CACHE; if (!$FILE_PATH_CACHE) { - $FILE_PATH_CACHE = new ElggFileCache($CONFIG->dataroot); + $FILE_PATH_CACHE = new ElggFileCache($CONFIG->dataroot . 'system_cache/'); } return $FILE_PATH_CACHE; } /** - * Reset the file path cache. + * Reset the system cache by deleting the caches * - * @return bool + * @return void */ -function elgg_filepath_cache_reset() { - $cache = elgg_get_filepath_cache(); - $view_types_result = $cache->delete('view_types'); - $views_result = $cache->delete('views'); - return $view_types_result && $views_result; +function elgg_reset_system_cache() { + $cache = elgg_get_system_cache(); + $cache->clear(); } /** - * Saves a filepath cache. + * Saves a system cache. * * @param string $type The type or identifier of the cache * @param string $data The data to be saved * @return bool */ -function elgg_filepath_cache_save($type, $data) { +function elgg_save_system_cache($type, $data) { global $CONFIG; - if ($CONFIG->viewpath_cache_enabled) { - $cache = elgg_get_filepath_cache(); + if ($CONFIG->system_cache_enabled) { + $cache = elgg_get_system_cache(); return $cache->save($type, $data); } @@ -64,16 +61,16 @@ function elgg_filepath_cache_save($type, $data) { } /** - * Retrieve the contents of the filepath cache. + * Retrieve the contents of a system cache. * * @param string $type The type of cache to load * @return string */ -function elgg_filepath_cache_load($type) { +function elgg_load_system_cache($type) { global $CONFIG; - if ($CONFIG->viewpath_cache_enabled) { - $cache = elgg_get_filepath_cache(); + if ($CONFIG->system_cache_enabled) { + $cache = elgg_get_system_cache(); $cached_data = $cache->load($type); if ($cached_data) { @@ -85,35 +82,74 @@ function elgg_filepath_cache_load($type) { } /** - * Enables the views file paths disk cache. + * Enables the system disk cache. * - * Uses the 'viewpath_cache_enabled' datalist with a boolean value. - * Resets the views paths cache. + * Uses the 'system_cache_enabled' datalist with a boolean value. + * Resets the system cache. * * @return void */ -function elgg_enable_filepath_cache() { +function elgg_enable_system_cache() { global $CONFIG; - datalist_set('viewpath_cache_enabled', 1); - $CONFIG->viewpath_cache_enabled = 1; - elgg_filepath_cache_reset(); + datalist_set('system_cache_enabled', 1); + $CONFIG->system_cache_enabled = 1; + elgg_reset_system_cache(); } /** - * Disables the views file paths disk cache. + * Disables the system disk cache. * - * Uses the 'viewpath_cache_enabled' datalist with a boolean value. - * Resets the views paths cache. + * Uses the 'system_cache_enabled' datalist with a boolean value. + * Resets the system cache. * * @return void */ -function elgg_disable_filepath_cache() { +function elgg_disable_system_cache() { global $CONFIG; - datalist_set('viewpath_cache_enabled', 0); - $CONFIG->viewpath_cache_enabled = 0; - elgg_filepath_cache_reset(); + datalist_set('system_cache_enabled', 0); + $CONFIG->system_cache_enabled = 0; + elgg_reset_system_cache(); +} + +/** @todo deprecate in Elgg 1.9 **/ + +/** + * @access private + */ +function elgg_get_filepath_cache() { + return elgg_get_system_cache(); +} +/** + * @access private + */ +function elgg_filepath_cache_reset() { + return elgg_reset_system_cache(); +} +/** + * @access private + */ +function elgg_filepath_cache_save($type, $data) { + return elgg_save_system_cache($type, $data); +} +/** + * @access private + */ +function elgg_filepath_cache_load($type) { + return elgg_load_system_cache($type); +} +/** + * @access private + */ +function elgg_enable_filepath_cache() { + return elgg_enable_system_cache(); +} +/** + * @access private + */ +function elgg_disable_filepath_cache() { + return elgg_disable_system_cache(); } /* Simplecache */ @@ -333,7 +369,7 @@ function elgg_invalidate_simplecache() { $return = true; while (false !== ($file = readdir($handle))) { if ($file != "." && $file != "..") { - $return = $return && unlink($CONFIG->dataroot . 'views_simplecache/' . $file); + $return &= unlink($CONFIG->dataroot . 'views_simplecache/' . $file); } } closedir($handle); @@ -346,9 +382,70 @@ function elgg_invalidate_simplecache() { } foreach ($viewtypes as $viewtype) { - $return = $return && datalist_set("simplecache_lastupdate_$viewtype", 0); - $return = $return && datalist_set("simplecache_lastcached_$viewtype", 0); + $return &= datalist_set("simplecache_lastupdate_$viewtype", 0); + $return &= datalist_set("simplecache_lastcached_$viewtype", 0); } return $return; } + +/** + * @see elgg_reset_system_cache() + * @access private + */ +function _elgg_load_cache() { + global $CONFIG; + + $CONFIG->system_cache_loaded = false; + + $CONFIG->views = new stdClass(); + $data = elgg_load_system_cache('view_locations'); + if (!is_string($data)) { + return; + } + $CONFIG->views->locations = unserialize($data); + + $data = elgg_load_system_cache('view_types'); + if (!is_string($data)) { + return; + } + $CONFIG->view_types = unserialize($data); + + $CONFIG->system_cache_loaded = true; +} + +/** + * @access private + */ +function _elgg_cache_init() { + global $CONFIG; + + $viewtype = elgg_get_viewtype(); + + // Regenerate the simple cache if expired. + // Don't do it on upgrade because upgrade does it itself. + // @todo - move into function and perhaps run off init system event + if (!defined('UPGRADING')) { + $lastupdate = datalist_get("simplecache_lastupdate_$viewtype"); + $lastcached = datalist_get("simplecache_lastcached_$viewtype"); + if ($lastupdate == 0 || $lastcached < $lastupdate) { + elgg_regenerate_simplecache($viewtype); + $lastcached = datalist_get("simplecache_lastcached_$viewtype"); + } + $CONFIG->lastcache = $lastcached; + } + + // cache system data if enabled and not loaded + if ($CONFIG->system_cache_enabled && !$CONFIG->system_cache_loaded) { + elgg_save_system_cache('view_locations', serialize($CONFIG->views->locations)); + elgg_save_system_cache('view_types', serialize($CONFIG->view_types)); + } + + if ($CONFIG->system_cache_enabled && !$CONFIG->i18n_loaded_from_cache) { + foreach ($CONFIG->translations as $lang => $map) { + elgg_save_system_cache("$lang.php", serialize($map)); + } + } +} + +elgg_register_event_handler('ready', 'system', '_elgg_cache_init'); diff --git a/engine/lib/calendar.php b/engine/lib/calendar.php new file mode 100644 index 000000000..9a06c5292 --- /dev/null +++ b/engine/lib/calendar.php @@ -0,0 +1,571 @@ +<?php +/** + * Elgg calendar / entity / event functions. + * + * @package Elgg.Core + * @subpackage Calendar + * + * @todo Implement or remove + */ + +/** + * Return a timestamp for the start of a given day (defaults today). + * + * @param int $day Day + * @param int $month Month + * @param int $year Year + * + * @return int + * @access private + */ +function get_day_start($day = null, $month = null, $year = null) { + return mktime(0, 0, 0, $month, $day, $year); +} + +/** + * Return a timestamp for the end of a given day (defaults today). + * + * @param int $day Day + * @param int $month Month + * @param int $year Year + * + * @return int + * @access private + */ +function get_day_end($day = null, $month = null, $year = null) { + return mktime(23, 59, 59, $month, $day, $year); +} + +/** + * Return the notable entities for a given time period. + * + * @param int $start_time The start time as a unix timestamp. + * @param int $end_time The end time as a unix timestamp. + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param string $order_by The field to order by; by default, time_created desc + * @param int $limit The number of entities to return; 10 by default + * @param int $offset The indexing offset, 0 by default + * @param boolean $count Set to true to get a count instead of entities. Defaults to false. + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any. + * @param mixed $container_guid Container or containers to get entities from (default: any). + * + * @return array|false + * @access private + */ +function get_notable_entities($start_time, $end_time, $type = "", $subtype = "", $owner_guid = 0, +$order_by = "asc", $limit = 10, $offset = 0, $count = false, $site_guid = 0, +$container_guid = null) { + global $CONFIG; + + if ($subtype === false || $subtype === null || $subtype === 0) { + return false; + } + + $start_time = (int)$start_time; + $end_time = (int)$end_time; + $order_by = sanitise_string($order_by); + $limit = (int)$limit; + $offset = (int)$offset; + $site_guid = (int) $site_guid; + if ($site_guid == 0) { + $site_guid = $CONFIG->site_guid; + } + + $where = array(); + + if (is_array($type)) { + $tempwhere = ""; + if (sizeof($type)) { + foreach ($type as $typekey => $subtypearray) { + foreach ($subtypearray as $subtypeval) { + $typekey = sanitise_string($typekey); + if (!empty($subtypeval)) { + $subtypeval = (int) get_subtype_id($typekey, $subtypeval); + } else { + $subtypeval = 0; + } + if (!empty($tempwhere)) { + $tempwhere .= " or "; + } + $tempwhere .= "(e.type = '{$typekey}' and e.subtype = {$subtypeval})"; + } + } + } + if (!empty($tempwhere)) { + $where[] = "({$tempwhere})"; + } + } else { + $type = sanitise_string($type); + $subtype = get_subtype_id($type, $subtype); + + if ($type != "") { + $where[] = "e.type='$type'"; + } + + if ($subtype !== "") { + $where[] = "e.subtype=$subtype"; + } + } + + if ($owner_guid != "") { + if (!is_array($owner_guid)) { + $owner_array = array($owner_guid); + $owner_guid = (int) $owner_guid; + $where[] = "e.owner_guid = '$owner_guid'"; + } else if (sizeof($owner_guid) > 0) { + $owner_array = array_map('sanitise_int', $owner_guid); + // Cast every element to the owner_guid array to int + $owner_guid = implode(",", $owner_guid); + $where[] = "e.owner_guid in ({$owner_guid})"; + } + if (is_null($container_guid)) { + $container_guid = $owner_array; + } + } + + if ($site_guid > 0) { + $where[] = "e.site_guid = {$site_guid}"; + } + + if (!is_null($container_guid)) { + if (is_array($container_guid)) { + foreach ($container_guid as $key => $val) { + $container_guid[$key] = (int) $val; + } + $where[] = "e.container_guid in (" . implode(",", $container_guid) . ")"; + } else { + $container_guid = (int) $container_guid; + $where[] = "e.container_guid = {$container_guid}"; + } + } + + // Add the calendar stuff + $cal_join = " + JOIN {$CONFIG->dbprefix}metadata cal_start on e.guid=cal_start.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_start_name on cal_start.name_id=cal_start_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_start_value on cal_start.value_id=cal_start_value.id + + JOIN {$CONFIG->dbprefix}metadata cal_end on e.guid=cal_end.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_end_name on cal_end.name_id=cal_end_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_end_value on cal_end.value_id=cal_end_value.id + "; + $where[] = "cal_start_name.string='calendar_start'"; + $where[] = "cal_start_value.string>=$start_time"; + $where[] = "cal_end_name.string='calendar_end'"; + $where[] = "cal_end_value.string <= $end_time"; + + + if (!$count) { + $query = "SELECT e.* from {$CONFIG->dbprefix}entities e $cal_join where "; + } else { + $query = "SELECT count(e.guid) as total from {$CONFIG->dbprefix}entities e $cal_join where "; + } + foreach ($where as $w) { + $query .= " $w and "; + } + + $query .= get_access_sql_suffix('e'); // Add access controls + + if (!$count) { + $query .= " order by n.calendar_start $order_by"; + // Add order and limit + if ($limit) { + $query .= " limit $offset, $limit"; + } + $dt = get_data($query, "entity_row_to_elggstar"); + + return $dt; + } else { + $total = get_data_row($query); + return $total->total; + } +} + +/** + * Return the notable entities for a given time period based on an item of metadata. + * + * @param int $start_time The start time as a unix timestamp. + * @param int $end_time The end time as a unix timestamp. + * @param mixed $meta_name Metadata name + * @param mixed $meta_value Metadata value + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity. + * @param int $owner_guid Owner GUID + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by Optional ordering. + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any. + * @param bool $count If true, returns count instead of entities. (Default: false) + * + * @return int|array A list of entities, or a count if $count is set to true + * @access private + */ +function get_notable_entities_from_metadata($start_time, $end_time, $meta_name, $meta_value = "", +$entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", +$site_guid = 0, $count = false) { + + global $CONFIG; + + $meta_n = get_metastring_id($meta_name); + $meta_v = get_metastring_id($meta_value); + + $start_time = (int)$start_time; + $end_time = (int)$end_time; + $entity_type = sanitise_string($entity_type); + $entity_subtype = get_subtype_id($entity_type, $entity_subtype); + $limit = (int)$limit; + $offset = (int)$offset; + if ($order_by == "") { + $order_by = "e.time_created desc"; + } + $order_by = sanitise_string($order_by); + $site_guid = (int) $site_guid; + if ((is_array($owner_guid) && (count($owner_guid)))) { + foreach ($owner_guid as $key => $guid) { + $owner_guid[$key] = (int) $guid; + } + } else { + $owner_guid = (int) $owner_guid; + } + + if ($site_guid == 0) { + $site_guid = $CONFIG->site_guid; + } + + //$access = get_access_list(); + + $where = array(); + + if ($entity_type != "") { + $where[] = "e.type='$entity_type'"; + } + + if ($entity_subtype) { + $where[] = "e.subtype=$entity_subtype"; + } + + if ($meta_name != "") { + $where[] = "m.name_id='$meta_n'"; + } + + if ($meta_value != "") { + $where[] = "m.value_id='$meta_v'"; + } + + if ($site_guid > 0) { + $where[] = "e.site_guid = {$site_guid}"; + } + + if (is_array($owner_guid)) { + $where[] = "e.container_guid in (" . implode(",", $owner_guid) . ")"; + } else if ($owner_guid > 0) { + $where[] = "e.container_guid = {$owner_guid}"; + } + + // Add the calendar stuff + $cal_join = " + JOIN {$CONFIG->dbprefix}metadata cal_start on e.guid=cal_start.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_start_name on cal_start.name_id=cal_start_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_start_value on cal_start.value_id=cal_start_value.id + + JOIN {$CONFIG->dbprefix}metadata cal_end on e.guid=cal_end.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_end_name on cal_end.name_id=cal_end_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_end_value on cal_end.value_id=cal_end_value.id + "; + + $where[] = "cal_start_name.string='calendar_start'"; + $where[] = "cal_start_value.string>=$start_time"; + $where[] = "cal_end_name.string='calendar_end'"; + $where[] = "cal_end_value.string <= $end_time"; + + if (!$count) { + $query = "SELECT distinct e.* "; + } else { + $query = "SELECT count(distinct e.guid) as total "; + } + + $query .= "from {$CONFIG->dbprefix}entities e" + . " JOIN {$CONFIG->dbprefix}metadata m on e.guid = m.entity_guid $cal_join where"; + + foreach ($where as $w) { + $query .= " $w and "; + } + + // Add access controls + $query .= get_access_sql_suffix("e"); + $query .= ' and ' . get_access_sql_suffix("m"); + + if (!$count) { + // Add order and limit + $query .= " order by $order_by limit $offset, $limit"; + return get_data($query, "entity_row_to_elggstar"); + } else { + if ($row = get_data_row($query)) { + return $row->total; + } + } + + return false; +} + +/** + * Return the notable entities for a given time period based on their relationship. + * + * @param int $start_time The start time as a unix timestamp. + * @param int $end_time The end time as a unix timestamp. + * @param string $relationship The relationship eg "friends_of" + * @param int $relationship_guid The guid of the entity to use query + * @param bool $inverse_relationship Reverse the normal function of the query to say + * "give me all entities for whom $relationship_guid is a + * $relationship of" + * @param string $type Entity type + * @param string $subtype Entity subtype + * @param int $owner_guid Owner GUID + * @param string $order_by Optional Order by + * @param int $limit Limit + * @param int $offset Offset + * @param boolean $count If true returns a count of entities (default false) + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any + * + * @return array|int|false An array of entities, or the number of entities, or false on failure + * @access private + */ +function get_noteable_entities_from_relationship($start_time, $end_time, $relationship, +$relationship_guid, $inverse_relationship = false, $type = "", $subtype = "", $owner_guid = 0, +$order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0) { + + global $CONFIG; + + $start_time = (int)$start_time; + $end_time = (int)$end_time; + $relationship = sanitise_string($relationship); + $relationship_guid = (int)$relationship_guid; + $inverse_relationship = (bool)$inverse_relationship; + $type = sanitise_string($type); + $subtype = get_subtype_id($type, $subtype); + $owner_guid = (int)$owner_guid; + if ($order_by == "") { + $order_by = "time_created desc"; + } + $order_by = sanitise_string($order_by); + $limit = (int)$limit; + $offset = (int)$offset; + $site_guid = (int) $site_guid; + if ($site_guid == 0) { + $site_guid = $CONFIG->site_guid; + } + + //$access = get_access_list(); + + $where = array(); + + if ($relationship != "") { + $where[] = "r.relationship='$relationship'"; + } + if ($relationship_guid) { + $where[] = $inverse_relationship ? + "r.guid_two='$relationship_guid'" : "r.guid_one='$relationship_guid'"; + } + if ($type != "") { + $where[] = "e.type='$type'"; + } + if ($subtype) { + $where[] = "e.subtype=$subtype"; + } + if ($owner_guid != "") { + $where[] = "e.container_guid='$owner_guid'"; + } + if ($site_guid > 0) { + $where[] = "e.site_guid = {$site_guid}"; + } + + // Add the calendar stuff + $cal_join = " + JOIN {$CONFIG->dbprefix}metadata cal_start on e.guid=cal_start.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_start_name on cal_start.name_id=cal_start_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_start_value on cal_start.value_id=cal_start_value.id + + JOIN {$CONFIG->dbprefix}metadata cal_end on e.guid=cal_end.entity_guid + JOIN {$CONFIG->dbprefix}metastrings cal_end_name on cal_end.name_id=cal_end_name.id + JOIN {$CONFIG->dbprefix}metastrings cal_end_value on cal_end.value_id=cal_end_value.id + "; + $where[] = "cal_start_name.string='calendar_start'"; + $where[] = "cal_start_value.string>=$start_time"; + $where[] = "cal_end_name.string='calendar_end'"; + $where[] = "cal_end_value.string <= $end_time"; + + // Select what we're joining based on the options + $joinon = "e.guid = r.guid_one"; + if (!$inverse_relationship) { + $joinon = "e.guid = r.guid_two"; + } + + if ($count) { + $query = "SELECT count(distinct e.guid) as total "; + } else { + $query = "SELECT distinct e.* "; + } + $query .= " from {$CONFIG->dbprefix}entity_relationships r" + . " JOIN {$CONFIG->dbprefix}entities e on $joinon $cal_join where "; + + foreach ($where as $w) { + $query .= " $w and "; + } + // Add access controls + $query .= get_access_sql_suffix("e"); + if (!$count) { + $query .= " order by $order_by limit $offset, $limit"; // Add order and limit + return get_data($query, "entity_row_to_elggstar"); + } else { + if ($count = get_data_row($query)) { + return $count->total; + } + } + return false; +} + +/** + * Get all entities for today. + * + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param string $order_by The field to order by; by default, time_created desc + * @param int $limit The number of entities to return; 10 by default + * @param int $offset The indexing offset, 0 by default + * @param boolean $count If true returns a count of entities (default false) + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any + * @param mixed $container_guid Container(s) to get entities from (default: any). + * + * @return array|false + * @access private + */ +function get_todays_entities($type = "", $subtype = "", $owner_guid = 0, $order_by = "", +$limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = null) { + + $day_start = get_day_start(); + $day_end = get_day_end(); + + return get_notable_entities($day_start, $day_end, $type, $subtype, $owner_guid, $order_by, + $limit, $offset, $count, $site_guid, $container_guid); +} + +/** + * Get entities for today from metadata. + * + * @param mixed $meta_name Metadata name + * @param mixed $meta_value Metadata value + * @param string $entity_type The type of entity to look for, eg 'site' or 'object' + * @param string $entity_subtype The subtype of the entity. + * @param int $owner_guid Owner GUID + * @param int $limit Limit + * @param int $offset Offset + * @param string $order_by Optional ordering. + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any. + * @param bool $count If true, returns count instead of entities. (Default: false) + * + * @return int|array A list of entities, or a count if $count is set to true + * @access private + */ +function get_todays_entities_from_metadata($meta_name, $meta_value = "", $entity_type = "", +$entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, +$count = false) { + + $day_start = get_day_start(); + $day_end = get_day_end(); + + return get_notable_entities_from_metadata($day_start, $day_end, $meta_name, $meta_value, + $entity_type, $entity_subtype, $owner_guid, $limit, $offset, $order_by, $site_guid, $count); +} + +/** + * Get entities for today from a relationship + * + * @param string $relationship The relationship eg "friends_of" + * @param int $relationship_guid The guid of the entity to use query + * @param bool $inverse_relationship Reverse the normal function of the query to say + * "give me all entities for whom $relationship_guid is a + * $relationship of" + * @param string $type Entity type + * @param string $subtype Entity subtype + * @param int $owner_guid Owner GUID + * @param string $order_by Optional Order by + * @param int $limit Limit + * @param int $offset Offset + * @param boolean $count If true returns a count of entities (default false) + * @param int $site_guid Site to get entities for. Default 0 = current site. -1 = any + * + * @return array|int|false An array of entities, or the number of entities, or false on failure + * @access private + */ +function get_todays_entities_from_relationship($relationship, $relationship_guid, +$inverse_relationship = false, $type = "", $subtype = "", $owner_guid = 0, +$order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0) { + + $day_start = get_day_start(); + $day_end = get_day_end(); + + return get_notable_entities_from_relationship($day_start, $day_end, $relationship, + $relationship_guid, $inverse_relationship, $type, $subtype, $owner_guid, $order_by, + $limit, $offset, $count, $site_guid); +} + +/** + * Returns a viewable list of entities for a given time period. + * + * @see elgg_view_entity_list + * + * @param int $start_time The start time as a unix timestamp. + * @param int $end_time The end time as a unix timestamp. + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param int $limit The number of entities to return; 10 by default + * @param boolean $fullview Whether or not to display the full view (default: true) + * @param boolean $listtypetoggle Whether or not to allow gallery view + * @param boolean $navigation Display pagination? Default: true + * + * @return string A viewable list of entities + * @access private + */ +function list_notable_entities($start_time, $end_time, $type= "", $subtype = "", $owner_guid = 0, +$limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) { + + $offset = (int) get_input('offset'); + $count = get_notable_entities($start_time, $end_time, $type, $subtype, + $owner_guid, "", $limit, $offset, true); + + $entities = get_notable_entities($start_time, $end_time, $type, $subtype, + $owner_guid, "", $limit, $offset); + + return elgg_view_entity_list($entities, $count, $offset, $limit, + $fullview, $listtypetoggle, $navigation); +} + +/** + * Return a list of today's entities. + * + * @see list_notable_entities + * + * @param string $type The type of entity (eg "user", "object" etc) + * @param string $subtype The arbitrary subtype of the entity + * @param int $owner_guid The GUID of the owning user + * @param int $limit The number of entities to return; 10 by default + * @param boolean $fullview Whether or not to display the full view (default: true) + * @param boolean $listtypetoggle Whether or not to allow gallery view + * @param boolean $navigation Display pagination? Default: true + * + * @return string A viewable list of entities + * @access private + */ +function list_todays_entities($type= "", $subtype = "", $owner_guid = 0, $limit = 10, +$fullview = true, $listtypetoggle = false, $navigation = true) { + + $day_start = get_day_start(); + $day_end = get_day_end(); + + return list_notable_entities($day_start, $day_end, $type, $subtype, $owner_guid, $limit, + $fullview, $listtypetoggle, $navigation); +} diff --git a/engine/lib/configuration.php b/engine/lib/configuration.php index 3fade8155..9bf1529d6 100644 --- a/engine/lib/configuration.php +++ b/engine/lib/configuration.php @@ -3,8 +3,9 @@ * Elgg configuration procedural code. * * Includes functions for manipulating the configuration values stored in the database - * Plugin authors should use the {@link get_config()}, {@link set_config()}, - * and {@unset_config()} functions to access or update config values. + * Plugin authors should use the {@link elgg_get_config()}, {@link elgg_set_config()}, + * {@link elgg_save_config()}, and {@unset_config()} functions to access or update + * config values. * * Elgg's configuration is split among 2 tables and 1 file: * - dbprefix_config @@ -302,7 +303,7 @@ function datalist_set($name, $value) { . " set name = '{$sanitised_name}', value = '{$sanitised_value}'" . " ON DUPLICATE KEY UPDATE value='{$sanitised_value}'"); - if ($success) { + if ($success !== FALSE) { $DATALIST_CACHE[$name] = $value; return true; } else { @@ -517,10 +518,10 @@ function get_all_config($site_guid = 0) { $site_guid = (int) $site_guid; if ($site_guid == 0) { - $site_guid = (int) $CONFIG->site_id; + $site_guid = (int) $CONFIG->site_guid; } - if ($result = get_data("SELECT * from {$CONFIG->dbprefix}config where site_guid = {$site_guid}")) { + if ($result = get_data("SELECT * FROM {$CONFIG->dbprefix}config WHERE site_guid = $site_guid")) { foreach ($result as $r) { $name = $r->name; $value = $r->value; @@ -533,37 +534,49 @@ function get_all_config($site_guid = 0) { } /** - * Sets defaults for or attempts to autodetect some common config values and - * loads them into $CONFIG. + * Loads configuration related to this site * - * @return true + * This loads from the config database table and the site entity * @access private */ -function set_default_config() { +function _elgg_load_site_config() { global $CONFIG; - $install_root = str_replace("\\", "/", dirname(dirname(dirname(__FILE__)))); - - // @todo this seldom works right. - $pathpart = str_replace("//", "/", str_replace($_SERVER['DOCUMENT_ROOT'], "", $install_root)); - if (substr($pathpart, 0, 1) != "/") { - $pathpart = "/" . $pathpart; + $CONFIG->site_guid = (int) datalist_get('default_site'); + $CONFIG->site_id = $CONFIG->site_guid; + $CONFIG->site = get_entity($CONFIG->site_guid); + if (!$CONFIG->site) { + throw new InstallationException(elgg_echo('InstallationException:SiteNotInstalled')); } - $www_root = "http://" . $_SERVER['HTTP_HOST'] . $pathpart; + $CONFIG->wwwroot = $CONFIG->site->url; + $CONFIG->sitename = $CONFIG->site->name; + $CONFIG->sitedescription = $CONFIG->site->description; + $CONFIG->siteemail = $CONFIG->site->email; + $CONFIG->url = $CONFIG->wwwroot; + + get_all_config(); +} + +/** + * Loads configuration related to Elgg as an application + * + * This loads from the datalists database table + * @access private + */ +function _elgg_load_application_config() { + global $CONFIG; + + $install_root = str_replace("\\", "/", dirname(dirname(dirname(__FILE__)))); $defaults = array( 'path' => "$install_root/", 'view_path' => "$install_root/views/", 'plugins_path' => "$install_root/mod/", - 'wwwroot' => $www_root, - 'url' => $www_root, - 'site_name' => 'New Elgg site', 'language' => 'en', - // compatibility with old names for ppl not using get_config() + // compatibility with old names for plugins not using elgg_get_config() 'viewpath' => "$install_root/views/", 'pluginspath' => "$install_root/mod/", - 'sitename' => 'New Elgg site', ); foreach ($defaults as $name => $value) { @@ -572,25 +585,6 @@ function set_default_config() { } } - $CONFIG->context = array(); - - return true; -} - -/** - * Loads values into $CONFIG. - * - * If Elgg is installed, this function pulls all rows from dbprefix_config - * and cherry picks some values from dbprefix_datalists. This also extracts - * some commonly used values from the default site object. - * - * @elgg_event boot system - * @return true|null - * @access private - */ -function configuration_boot() { - global $CONFIG; - $path = datalist_get('path'); if (!empty($path)) { $CONFIG->path = $path; @@ -605,22 +599,23 @@ function configuration_boot() { } else { $CONFIG->simplecache_enabled = 1; } - $viewpath_cache_enabled = datalist_get('viewpath_cache_enabled'); - if ($viewpath_cache_enabled !== false) { - $CONFIG->viewpath_cache_enabled = $viewpath_cache_enabled; + $system_cache_enabled = datalist_get('system_cache_enabled'); + if ($system_cache_enabled !== false) { + $CONFIG->system_cache_enabled = $system_cache_enabled; } else { - $CONFIG->viewpath_cache_enabled = 1; + $CONFIG->system_cache_enabled = 1; } - if (isset($CONFIG->site) && ($CONFIG->site instanceof ElggSite)) { - $CONFIG->wwwroot = $CONFIG->site->url; - $CONFIG->sitename = $CONFIG->site->name; - $CONFIG->sitedescription = $CONFIG->site->description; - $CONFIG->siteemail = $CONFIG->site->email; - } - $CONFIG->url = $CONFIG->wwwroot; - // Load default settings from database - get_all_config(); -} + // initialize context here so it is set before the get_input call + $CONFIG->context = array(); + + // needs to be set before system, init for links in html head + $viewtype = get_input('view', 'default'); + $lastcached = datalist_get("simplecache_lastcached_$viewtype"); + $CONFIG->lastcache = $lastcached; -elgg_register_event_handler('boot', 'system', 'configuration_boot', 10); + $CONFIG->i18n_loaded_from_cache = false; + + // this must be synced with the enum for the entities table + $CONFIG->entity_types = array('group', 'object', 'site', 'user'); +} diff --git a/engine/lib/database.php b/engine/lib/database.php index 444bb7cc4..cc2b99f6a 100644 --- a/engine/lib/database.php +++ b/engine/lib/database.php @@ -189,22 +189,6 @@ function db_delayedexecution_shutdown_hook() { } /** - * Registers shutdown functions for database profiling and delayed queries. - * - * @note Database connections are established upon first call to database. - * - * @return true - * @elgg_event_handler boot system - * @access private - */ -function init_db() { - register_shutdown_function('db_delayedexecution_shutdown_hook'); - register_shutdown_function('db_profiling_shutdown_hook'); - - return true; -} - -/** * Returns (if required, also creates) a database link resource. * * Database link resources are stored in the {@link $dblink} global. These @@ -757,6 +741,13 @@ function sanitize_int($int, $signed = true) { } /** - * @elgg_register_event boot system init_db + * Registers shutdown functions for database profiling and delayed queries. + * + * @access private */ -elgg_register_event_handler('boot', 'system', 'init_db', 0); +function init_db() { + register_shutdown_function('db_delayedexecution_shutdown_hook'); + register_shutdown_function('db_profiling_shutdown_hook'); +} + +elgg_register_event_handler('init', 'system', 'init_db'); diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php index e1866498b..4b9d41543 100644 --- a/engine/lib/deprecated-1.8.php +++ b/engine/lib/deprecated-1.8.php @@ -1674,7 +1674,7 @@ function get_plugin_list() { * otherwise you may experience view display artifacts. Do this with the following code: * * elgg_regenerate_simplecache(); - * elgg_filepath_cache_reset(); + * elgg_reset_system_cache(); * * @deprecated 1.8 Use elgg_generate_plugin_entities() and elgg_set_plugin_priorities() * @@ -1841,7 +1841,7 @@ function get_installed_plugins($status = 'all') { * otherwise you may experience view display artifacts. Do this with the following code: * * elgg_regenerate_simplecache(); - * elgg_filepath_cache_reset(); + * elgg_reset_system_cache(); * * @deprecated 1.8 Use ElggPlugin->activate() * @@ -1882,7 +1882,7 @@ function enable_plugin($plugin, $site_guid = null) { * otherwise you may experience view display artifacts. Do this with the following code: * * elgg_regenerate_simplecache(); - * elgg_filepath_cache_reset(); + * elgg_reset_system_cache(); * * @deprecated 1.8 Use ElggPlugin->deactivate() * diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index 9035d95f2..b55958a6c 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -995,7 +995,8 @@ function elgg_trigger_plugin_hook($hook, $type, $params = null, $returnvalue = n * @access private */ function _elgg_php_exception_handler($exception) { - error_log("*** FATAL EXCEPTION *** : " . $exception); + $timestamp = time(); + error_log("Exception #$timestamp: $exception"); // Wipe any existing output buffer ob_end_clean(); @@ -1011,7 +1012,17 @@ function _elgg_php_exception_handler($exception) { $CONFIG->pagesetupdone = true; elgg_set_viewtype('failsafe'); - $body = elgg_view("messages/exceptions/exception", array('object' => $exception)); + if (elgg_is_admin_logged_in()) { + $body = elgg_view("messages/exceptions/admin_exception", array( + 'object' => $exception, + 'ts' => $timestamp + )); + } else { + $body = elgg_view("messages/exceptions/exception", array( + 'object' => $exception, + 'ts' => $timestamp + )); + } echo elgg_view_page(elgg_echo('exception:title'), $body); } catch (Exception $e) { $timestamp = time(); @@ -1959,7 +1970,7 @@ function elgg_is_valid_options_for_batch_operation($options, $type) { // at least one of these is required. $required = array( // generic restraints - 'guid', 'guids', 'limit' + 'guid', 'guids' ); switch ($type) { @@ -2010,8 +2021,14 @@ function elgg_walled_garden_index() { elgg_load_css('elgg.walled_garden'); elgg_load_js('elgg.walled_garden'); - $body = elgg_view('core/walled_garden/body'); + $content = elgg_view('core/walled_garden/login'); + $params = array( + 'content' => $content, + 'class' => 'elgg-walledgarden-double', + 'id' => 'elgg-walledgarden-login', + ); + $body = elgg_view_layout('walled_garden', $params); echo elgg_view_page('', $body, 'walled_garden'); // return true to prevent other plugins from adding a front page @@ -2019,6 +2036,24 @@ function elgg_walled_garden_index() { } /** + * Serve walled garden sections + * + * @param array $page Array of URL segments + * @return string + * @access private + */ +function _elgg_walled_garden_ajax_handler($page) { + $view = $page[0]; + $params = array( + 'content' => elgg_view("core/walled_garden/$view"), + 'class' => 'elgg-walledgarden-single hidden', + 'id' => str_replace('_', '-', "elgg-walledgarden-$view"), + ); + echo elgg_view_layout('walled_garden', $params); + return true; +} + +/** * Checks the status of the Walled Garden and forwards to a login page * if required. * @@ -2038,6 +2073,8 @@ function elgg_walled_garden() { elgg_register_css('elgg.walled_garden', '/css/walled_garden.css'); elgg_register_js('elgg.walled_garden', '/js/walled_garden.js'); + elgg_register_page_handler('walled_garden', '_elgg_walled_garden_ajax_handler'); + // check for external page view if (isset($CONFIG->site) && $CONFIG->site instanceof ElggSite) { $CONFIG->site->checkWalledGarden(); @@ -2045,6 +2082,36 @@ function elgg_walled_garden() { } /** + * Boots the engine + * + * 1. sets error handlers + * 2. connects to database + * 3. verifies the installation suceeded + * 4. loads application configuration + * 5. loads i18n data + * 6. loads site configuration + * + * @access private + */ +function _elgg_engine_boot() { + // Register the error handlers + set_error_handler('_elgg_php_error_handler'); + set_exception_handler('_elgg_php_exception_handler'); + + setup_db_connections(); + + verify_installation(); + + _elgg_load_application_config(); + + register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/"); + + _elgg_load_site_config(); + + _elgg_load_cache(); +} + +/** * Elgg's main init. * * Handles core actions for comments, the JS pagehandler, and the shutdown function. @@ -2167,6 +2234,7 @@ define('REFERRER', -1); define('REFERER', -1); elgg_register_event_handler('init', 'system', 'elgg_init'); +elgg_register_event_handler('boot', 'system', '_elgg_engine_boot', 1); elgg_register_plugin_hook_handler('unit_test', 'system', 'elgg_api_test'); elgg_register_event_handler('init', 'system', 'add_custom_menu_items', 1000); diff --git a/engine/lib/entities.php b/engine/lib/entities.php index 82452fba1..4875b2c2f 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -738,6 +738,7 @@ function elgg_entity_exists($guid) { * Joined with subtypes by AND. See below) * * subtypes => NULL|STR entity subtype (SQL: subtype IN ('subtype1', 'subtype2)) + * Use ELGG_ENTITIES_NO_VALUE for no subtype. * * type_subtype_pairs => NULL|ARR (array('type' => 'subtype')) * (type = '$type' AND subtype = '$subtype') pairs @@ -960,8 +961,8 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair return ''; } - // these are the only valid types for entities in elgg as defined in the DB. - $valid_types = array('object', 'user', 'group', 'site'); + // these are the only valid types for entities in elgg + $valid_types = elgg_get_config('entity_types'); // pairs override $wheres = array(); @@ -1378,6 +1379,10 @@ function disable_entity($guid, $reason = "", $recursive = true) { } if ($recursive) { + $hidden = access_get_show_hidden_status(); + access_show_hidden_entities(true); + $ia = elgg_set_ignore_access(true); + $sub_entities = get_data("SELECT * FROM {$CONFIG->dbprefix}entities WHERE ( container_guid = $guid @@ -1391,6 +1396,8 @@ function disable_entity($guid, $reason = "", $recursive = true) { $e->disable($reason); } } + access_show_hidden_entities($hidden); + elgg_set_ignore_access($ia); } $entity->disableMetadata(); @@ -1514,18 +1521,23 @@ function delete_entity($guid, $recursive = true) { $entity_disable_override = access_get_show_hidden_status(); access_show_hidden_entities(true); $ia = elgg_set_ignore_access(true); - $sub_entities = get_data("SELECT * from {$CONFIG->dbprefix}entities - WHERE container_guid=$guid - or owner_guid=$guid - or site_guid=$guid", 'entity_row_to_elggstar'); - if ($sub_entities) { - foreach ($sub_entities as $e) { - // check for equality so that an entity that is its own - // owner or container does not cause infinite loop - if ($e->guid != $guid) { - $e->delete(true); - } - } + + // @todo there was logic in the original code that ignored + // entities with owner or container guids of themselves. + // this should probably be prevented in ElggEntity instead of checked for here + $options = array( + 'wheres' => array( + "((container_guid = $guid OR owner_guid = $guid OR site_guid = $guid)" + . " AND guid != $guid)" + ), + 'limit' => 0 + ); + + $batch = new ElggBatch('elgg_get_entities', $options); + $batch->setIncrementOffset(false); + + foreach ($batch as $e) { + $e->delete(true); } access_show_hidden_entities($entity_disable_override); @@ -1959,7 +1971,7 @@ function elgg_register_entity_type($type, $subtype = null) { global $CONFIG; $type = strtolower($type); - if (!in_array($type, array('object', 'site', 'group', 'user'))) { + if (!in_array($type, $CONFIG->entity_types)) { return FALSE; } @@ -1994,7 +2006,7 @@ function unregister_entity_type($type, $subtype) { global $CONFIG; $type = strtolower($type); - if (!in_array($type, array('object', 'site', 'group', 'user'))) { + if (!in_array($type, $CONFIG->entity_types)) { return FALSE; } @@ -2162,31 +2174,8 @@ function elgg_list_registered_entities(array $options = array()) { $entities = array(); } - return elgg_view_entity_list($entities, $count, $options['offset'], - $options['limit'], $options['full_view'], $options['list_type_toggle'], $options['pagination']); -} - -/** - * Check the recursive delete permissions token. - * - * If an entity is deleted recursively, a permissions override is required to allow - * contained or owned entities to be removed. - * - * @return bool - * @elgg_plugin_hook_handler permissions_check all - * @elgg_plugin_hook_handler permissions_check:metadata all - * @access private - */ -function recursive_delete_permissions_check() { - static $__RECURSIVE_DELETE_TOKEN; - - if ((elgg_is_logged_in()) && ($__RECURSIVE_DELETE_TOKEN) - && (strcmp($__RECURSIVE_DELETE_TOKEN, md5(elgg_get_logged_in_user_guid())))) { - return true; - } - - // consult next function - return NULL; + $options['count'] = $count; + return elgg_view_entity_list($entities, $options); } /** @@ -2303,11 +2292,6 @@ function entities_init() { elgg_register_plugin_hook_handler('unit_test', 'system', 'entities_test'); - // Allow a permission override for recursive entity deletion - // @todo Can this be done better? - elgg_register_plugin_hook_handler('permissions_check', 'all', 'recursive_delete_permissions_check'); - elgg_register_plugin_hook_handler('permissions_check:metadata', 'all', 'recursive_delete_permissions_check'); - elgg_register_plugin_hook_handler('gc', 'system', 'entities_gc'); } diff --git a/engine/lib/group.php b/engine/lib/group.php index 29330eeca..feb1f1e7f 100644 --- a/engine/lib/group.php +++ b/engine/lib/group.php @@ -261,16 +261,24 @@ function group_gatekeeper($forward = true) { if ($group = elgg_get_page_owner_entity()) { if ($group instanceof ElggGroup) { $url = $group->getURL(); - if ( - ((!elgg_is_logged_in()) && (!$group->isPublicMembership())) || - ((!$group->isMember(elgg_get_logged_in_user_entity()) && (!$group->isPublicMembership()))) - ) { - $allowed = false; - } + if (!$group->isPublicMembership()) { + // closed group so must be member or an admin + + if (!elgg_is_logged_in()) { + $allowed = false; + if ($forward == true) { + $_SESSION['last_forward_from'] = current_page_url(); + register_error(elgg_echo('loggedinrequired')); + forward('', 'login'); + } + } else if (!$group->isMember(elgg_get_logged_in_user_entity())) { + $allowed = false; + } - // Admin override - if (elgg_is_admin_logged_in()) { - $allowed = true; + // Admin override + if (elgg_is_admin_logged_in()) { + $allowed = true; + } } } } diff --git a/engine/lib/languages.php b/engine/lib/languages.php index 0400843af..bf6829a39 100644 --- a/engine/lib/languages.php +++ b/engine/lib/languages.php @@ -145,9 +145,16 @@ function elgg_echo($message_key, $args = array(), $language = "") { function register_translations($path, $load_all = false) { global $CONFIG; + static $load_from_cache; + static $cache_loaded_langs; + if (!isset($load_from_cache)) { + $load_from_cache = $CONFIG->system_cache_enabled; + $cache_loaded_langs = array(); + } + $path = sanitise_filepath($path); - // Make a note of this path just incase we need to register this language later + // Make a note of this path just in case we need to register this language later if (!isset($CONFIG->language_paths)) { $CONFIG->language_paths = array(); } @@ -155,7 +162,6 @@ function register_translations($path, $load_all = false) { // Get the current language based on site defaults and user preference $current_language = get_current_language(); - elgg_log("Translations loaded from: $path"); // only load these files unless $load_all is true. $load_language_files = array( @@ -165,6 +171,32 @@ function register_translations($path, $load_all = false) { $load_language_files = array_unique($load_language_files); + if ($load_from_cache && !$load_all) { + // load language files from cache + $data = array(); + foreach ($load_language_files as $lang_file) { + $lang = substr($lang_file, 0, strpos($lang_file, '.')); + if (!isset($cache_loaded_langs[$lang])) { + $data[$lang] = elgg_load_system_cache($lang_file); + if ($data[$lang]) { + $cache_loaded_langs[$lang] = true; + } else { + // this language file not cached yet + $load_from_cache = false; + } + } + } + + // are we still suppose to load from cache + if ($load_from_cache) { + foreach ($data as $lang => $map) { + add_translation($lang, unserialize($map)); + } + $CONFIG->i18n_loaded_from_cache = true; + return true; + } + } + $handle = opendir($path); if (!$handle) { elgg_log("Could not open language path: $path", 'ERROR'); @@ -186,6 +218,11 @@ function register_translations($path, $load_all = false) { } } + elgg_log("Translations loaded from: $path"); + + // make sure caching code saves language data if system cache is on + $CONFIG->i18n_loaded_from_cache = false; + return $return; } @@ -311,5 +348,3 @@ function elgg_languages_init() { } elgg_register_event_handler('init', 'system', 'elgg_languages_init'); - -register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/"); diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php index 19e8aa3c8..34a36d86e 100644 --- a/engine/lib/metadata.php +++ b/engine/lib/metadata.php @@ -294,11 +294,11 @@ function elgg_get_metadata(array $options = array()) { * Deletes metadata based on $options. * * @warning Unlike elgg_get_metadata() this will not accept an empty options array! - * This requires some constraints: metadata_owner_guid(s), - * metadata_name(s), metadata_value(s), or limit must be set. + * This requires at least one constraint: metadata_owner_guid(s), + * metadata_name(s), metadata_value(s), or guid(s) must be set. * - * @param array $options An options array. {@See elgg_get_metadata()} - * @return mixed + * @param array $options An options array. {@see elgg_get_metadata()} + * @return mixed Null if the metadata name is invalid. Bool on success or fail. * @since 1.8.0 */ function elgg_delete_metadata(array $options) { @@ -307,7 +307,8 @@ function elgg_delete_metadata(array $options) { } $options['metastring_type'] = 'metadata'; - return elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback'); + $result = elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback'); + return $result; } /** @@ -745,7 +746,7 @@ function export_metadata_plugin_hook($hook, $entity_type, $returnvalue, $params) /** * Takes in a comma-separated string and returns an array of tags - * which have been trimmed and set to lower case + * which have been trimmed * * @param string $string Comma-separated tag string * @@ -754,12 +755,7 @@ function export_metadata_plugin_hook($hook, $entity_type, $returnvalue, $params) function string_to_tag_array($string) { if (is_string($string)) { $ar = explode(",", $string); - // trim blank spaces $ar = array_map('trim', $ar); - // make lower case : [Marcus Povey 20090605 - Using mb wrapper function - // using UTF8 safe function where available] - $ar = array_map('elgg_strtolower', $ar); - // Remove null values $ar = array_filter($ar, 'is_not_null'); return $ar; } diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php index 62b60e279..d7cc4e0bc 100644 --- a/engine/lib/metastrings.php +++ b/engine/lib/metastrings.php @@ -727,38 +727,11 @@ function elgg_batch_metastring_based_objects(array $options, $callback) { return false; } - switch($options['metastring_type']) { - case 'metadata': - $objects = elgg_get_metadata($options); - break; - - case 'annotations': - $objects = elgg_get_annotations($options); - break; - - default: - return false; - } - - if (!is_array($objects)) { - $r = false; - } elseif (empty($objects)) { - // ElggBatch returns null if the results are an empty array - $r = null; - } else { - $r = true; - foreach($objects as $object) { - $r = $r && $callback($object); - } - } + // @todo restore once ElggBatch supports callbacks that delete rows. + $batch = new ElggBatch('elgg_get_metastring_based_objects', $options, $callback, 50, false); + $r = $batch->callbackResult; return $r; - -// // @todo restore once ElggBatch supports callbacks that delete rows. -// $batch = new ElggBatch('elgg_get_metastring_based_objects', $options, $callback); -// $r = $batch->callbackResult; -// -// return $r; } /** diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php index dcbd7b397..a7984ce5a 100644 --- a/engine/lib/navigation.php +++ b/engine/lib/navigation.php @@ -78,7 +78,11 @@ * link_class => STR A class or classes for the <a> tag * item_class => STR A class or classes for the <li> tag * - * Custom options can be added as key value pairs. + * Additional options that the view output/url takes can be + * passed in the array. If the 'confirm' key is passed, the + * menu link uses the 'output/confirmlink' view. Custom + * options can be added by using the 'data' key with the + * value being an associative array. * * @return bool * @since 1.8.0 diff --git a/engine/lib/output.php b/engine/lib/output.php index b96cf354c..b1245a924 100644 --- a/engine/lib/output.php +++ b/engine/lib/output.php @@ -310,15 +310,19 @@ function elgg_get_friendly_title($title) { return $result; } + // @todo not using this because of locale concerns //$title = iconv('UTF-8', 'ASCII//TRANSLIT', $title); + // @todo this uses a utf8 character class. can use if + // we want to support utf8 in the url. + //$title = preg_replace('/[^\p{L}\- ]/u', '', $title); + // use A-Za-z0-9_ instead of \w because \w is locale sensitive - $title = preg_replace("/[^A-Za-z0-9_ ]/", "", $title); - $title = preg_replace("/[^\w ]/", "", $title); + $title = preg_replace("/[^A-Za-z0-9_\- ]/", "", $title); $title = str_replace(" ", "-", $title); $title = str_replace("--", "-", $title); $title = trim($title); - $title = strtolower($title); + $title = elgg_strtolower($title); return $title; } diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index 7968f4a6e..123fb18d8 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -62,7 +62,7 @@ function elgg_get_plugin_ids_in_dir($dir = null) { $dir = elgg_get_plugins_path(); } - $plugin_idss = array(); + $plugin_ids = array(); $handle = opendir($dir); if ($handle) { @@ -298,19 +298,13 @@ function elgg_load_plugins() { // temporary disable all plugins if there is a file called 'disabled' in the plugin dir if (file_exists("$plugins_path/disabled")) { + if (elgg_is_admin_logged_in() && elgg_in_context('admin')) { + system_message(elgg_echo('plugins:disabled')); + } return false; } - // Load view caches if available - $cached_view_paths = elgg_filepath_cache_load('views'); - $cached_view_types = elgg_filepath_cache_load('view_types'); - $cached_view_info = is_string($cached_view_paths) && is_string($cached_view_types); - - if ($cached_view_info) { - $CONFIG->views = unserialize($cached_view_paths); - $CONFIG->view_types = unserialize($cached_view_types); - - // don't need to register views + if (elgg_get_config('system_cache_loaded')) { $start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_VIEWS; } @@ -332,12 +326,6 @@ function elgg_load_plugins() { } } - // Cache results - if (!$cached_view_info) { - elgg_filepath_cache_save('views', serialize($CONFIG->views)); - elgg_filepath_cache_save('view_types', serialize($CONFIG->view_types)); - } - return $return; } diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php index fabe2d2d6..f50c4a485 100644 --- a/engine/lib/relationships.php +++ b/engine/lib/relationships.php @@ -290,7 +290,7 @@ function elgg_get_entities_from_relationship($options) { $options['selects'] = array(); } - $select = array('r.*'); + $select = array('r.id'); $options['selects'] = array_merge($options['selects'], $select); } diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php index 97a05e2e8..419d36707 100644 --- a/engine/lib/sessions.php +++ b/engine/lib/sessions.php @@ -127,6 +127,10 @@ function elgg_is_admin_user($user_guid) { /** * Perform user authentication with a given username and password. * + * @warning This returns an error message on failure. Use the identical operator to check + * for access: if (true === elgg_authenticate()) { ... }. + * + * * @see login * * @param string $username The username @@ -355,7 +359,7 @@ function logout() { session_destroy(); // starting a default session to store any post-logout messages. - session_init(NULL, NULL, NULL); + _elgg_session_boot(NULL, NULL, NULL); $_SESSION['msg'] = $old_msg; return TRUE; @@ -379,7 +383,7 @@ function logout() { * @return bool * @access private */ -function session_init($event, $object_type, $object) { +function _elgg_session_boot($event, $object_type, $object) { global $DB_PREFIX, $CONFIG; // Use database for sessions @@ -444,8 +448,8 @@ function session_init($event, $object_type, $object) { set_last_action($_SESSION['guid']); } - elgg_register_action("login", '', 'public'); - elgg_register_action("logout"); + elgg_register_action('login', '', 'public'); + elgg_register_action('logout'); // Register a default PAM handler register_pam_handler('pam_auth_userpass'); @@ -655,4 +659,4 @@ function _elgg_session_gc($maxlifetime) { return true; } -elgg_register_event_handler("boot", "system", "session_init", 20); +elgg_register_event_handler('boot', 'system', '_elgg_session_boot', 2); diff --git a/engine/lib/sites.php b/engine/lib/sites.php index 337b2d180..850092cad 100644 --- a/engine/lib/sites.php +++ b/engine/lib/sites.php @@ -231,43 +231,6 @@ function get_site_domain($guid) { } /** - * Initialise site handling - * - * Called at the beginning of system running, to set the ID of the current site. - * This is 0 by default, but plugins may alter this behaviour by attaching functions - * to the sites init event and changing $CONFIG->site_id. - * - * @uses $CONFIG - * - * @param string $event Event API required parameter - * @param string $object_type Event API required parameter - * @param null $object Event API required parameter - * - * @return true - * @access private - */ -function sites_boot($event, $object_type, $object) { - global $CONFIG; - - $site = elgg_trigger_plugin_hook("siteid", "system"); - if ($site === null || $site === false) { - $CONFIG->site_id = (int) datalist_get('default_site'); - } else { - $CONFIG->site_id = $site; - } - $CONFIG->site_guid = $CONFIG->site_id; - $CONFIG->site = get_entity($CONFIG->site_guid); - - return true; -} - -// Register event handlers -elgg_register_event_handler('boot', 'system', 'sites_boot', 2); - -// Register with unit test -elgg_register_plugin_hook_handler('unit_test', 'system', 'sites_test'); - -/** * Unit tests for sites * * @param sting $hook unit_test @@ -283,3 +246,6 @@ function sites_test($hook, $type, $value, $params) { $value[] = "{$CONFIG->path}engine/tests/objects/sites.php"; return $value; } + +// Register with unit test +elgg_register_plugin_hook_handler('unit_test', 'system', 'sites_test'); diff --git a/engine/lib/statistics.php b/engine/lib/statistics.php index 7c170f3bb..e1f95ed97 100644 --- a/engine/lib/statistics.php +++ b/engine/lib/statistics.php @@ -95,14 +95,12 @@ function get_number_users($show_deactivated = false) { * @return string */ function get_online_users() { - $offset = get_input('offset', 0); $count = find_active_users(600, 10, $offset, true); $objects = find_active_users(600, 10, $offset); if ($objects) { return elgg_view_entity_list($objects, array( 'count' => $count, - 'offset' => $offset, 'limit' => 10 )); } diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php index fd5644135..28d90be56 100644 --- a/engine/lib/system_log.php +++ b/engine/lib/system_log.php @@ -11,6 +11,7 @@ * Retrieve the system log based on a number of parameters. * * @param int|array $by_user The guid(s) of the user(s) who initiated the event. + * Use 0 for unowned entries. Anything else falsey means anyone. * @param string $event The event you are searching on. * @param string $class The class of object it effects. * @param string $type The type @@ -21,11 +22,12 @@ * @param int $timebefore Lower time limit * @param int $timeafter Upper time limit * @param int $object_id GUID of an object - * + * @param str $ip_address The IP address. * @return mixed */ function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "", -$limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0) { +$limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0, +$ip_address = false) { global $CONFIG; @@ -37,16 +39,18 @@ $limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $obje } else { $by_user = (int)$by_user; } + $event = sanitise_string($event); $class = sanitise_string($class); $type = sanitise_string($type); $subtype = sanitise_string($subtype); + $ip_address = sanitise_string($ip_address); $limit = (int)$limit; $offset = (int)$offset; $where = array(); - if ($by_user_orig !== "") { + if ($by_user_orig !== "" && $by_user_orig !== false && $by_user_orig !== null) { if (is_int($by_user)) { $where[] = "performed_by_guid=$by_user"; } else if (is_array($by_user)) { @@ -75,6 +79,9 @@ $limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $obje if ($object_id) { $where[] = "object_id = " . ((int) $object_id); } + if ($ip_address) { + $where[] = "ip_address = '$ip_address'"; + } $select = "*"; if ($count) { @@ -91,7 +98,8 @@ $limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $obje } if ($count) { - if ($numrows = get_data_row($query)) { + $numrows = get_data_row($query); + if ($numrows) { return $numrows->count; } } else { @@ -171,6 +179,7 @@ function system_log($object, $event) { $object_subtype = $object->getSubtype(); $event = sanitise_string($event); $time = time(); + $ip_address = sanitise_string($_SERVER['REMOTE_ADDR']); $performed_by = elgg_get_logged_in_user_guid(); if (isset($object->access_id)) { @@ -194,10 +203,10 @@ function system_log($object, $event) { if (!isset($log_cache[$time][$object_id][$event])) { $query = "INSERT DELAYED into {$CONFIG->dbprefix}system_log (object_id, object_class, object_type, object_subtype, event, - performed_by_guid, owner_guid, access_id, enabled, time_created) + performed_by_guid, owner_guid, access_id, enabled, time_created, ip_address) VALUES ('$object_id','$object_class','$object_type', '$object_subtype', '$event', - $performed_by, $owner_guid, $access_id, '$enabled', '$time')"; + $performed_by, $owner_guid, $access_id, '$enabled', '$time', '$ip_address')"; insert_data($query); diff --git a/engine/lib/upgrades/2011010101.php b/engine/lib/upgrades/2011010101.php index b063c249b..a1ee92622 100644 --- a/engine/lib/upgrades/2011010101.php +++ b/engine/lib/upgrades/2011010101.php @@ -66,7 +66,7 @@ if ($old_enabled_plugins) { // invalidate caches elgg_invalidate_simplecache(); -elgg_filepath_cache_reset(); +elgg_reset_system_cache(); // clean up. remove_metadata($site->guid, 'pluginorder'); diff --git a/engine/lib/upgrades/2012012000-1.8.3-ip_in_syslog-87fe0f068cf62428.php b/engine/lib/upgrades/2012012000-1.8.3-ip_in_syslog-87fe0f068cf62428.php new file mode 100644 index 000000000..b9514e156 --- /dev/null +++ b/engine/lib/upgrades/2012012000-1.8.3-ip_in_syslog-87fe0f068cf62428.php @@ -0,0 +1,12 @@ +<?php +/** + * Elgg 1.8.3 upgrade 2012012000 + * ip_in_syslog + * + * Adds a field for an IP address in the system log table + */ + +$db_prefix = elgg_get_config('dbprefix'); +$q = "ALTER TABLE {$db_prefix}system_log ADD ip_address VARCHAR(15) NOT NULL AFTER time_created"; + +update_data($q);
\ No newline at end of file diff --git a/engine/lib/upgrades/2012012100-1.8.3-system_cache-93100e7d55a24a11.php b/engine/lib/upgrades/2012012100-1.8.3-system_cache-93100e7d55a24a11.php new file mode 100644 index 000000000..3a9aae2a1 --- /dev/null +++ b/engine/lib/upgrades/2012012100-1.8.3-system_cache-93100e7d55a24a11.php @@ -0,0 +1,13 @@ +<?php +/** + * Elgg 1.8.3 upgrade 2012012100 + * system_cache + * + * Convert viewpath cache to system cache + */ + +$value = datalist_get('viewpath_cache_enabled'); +datalist_set('system_cache_enabled', $value); + +$query = "DELETE FROM {$CONFIG->dbprefix}datalists WHERE name='viewpath_cache_enabled'"; +delete_data($query); diff --git a/engine/lib/upgrades/2012041800-1.8.3-dont_filter_passwords-c0ca4a18b38ae2bc.php b/engine/lib/upgrades/2012041800-1.8.3-dont_filter_passwords-c0ca4a18b38ae2bc.php new file mode 100644 index 000000000..b82ffbebf --- /dev/null +++ b/engine/lib/upgrades/2012041800-1.8.3-dont_filter_passwords-c0ca4a18b38ae2bc.php @@ -0,0 +1,11 @@ +<?php +/** + * Elgg 1.8.3 upgrade 2012041800 + * dont_filter_passwords + * + * Add admin notice that password handling has changed and if + * users can't login to have them reset their passwords. + */ +elgg_add_admin_notice('dont_filter_passwords', 'Password handling has been updated to be more secure and flexible. ' + . 'This change may prevent a small number of users from logging in with their existing passwords. ' + . 'If a user is unable to log in, please advise him or her to reset their password, or reset it as an admin user.'); diff --git a/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php b/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php new file mode 100644 index 000000000..07732f261 --- /dev/null +++ b/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php @@ -0,0 +1,13 @@ +<?php +/** + * Elgg 1.8.3 upgrade 2012041801 + * multiple_user_tokens + * + * Fixes http://trac.elgg.org/ticket/4291 + * Removes the unique index on users_apisessions for user_guid and site_guid + */ + +$db_prefix = elgg_get_config('dbprefix'); +$q = "ALTER TABLE {$db_prefix}users_apisessions DROP INDEX user_guid, + ADD INDEX user_guid (user_guid, site_guid)"; +update_data($q);
\ No newline at end of file diff --git a/engine/lib/user_settings.php b/engine/lib/user_settings.php index af30d8f0d..e4069fb53 100644 --- a/engine/lib/user_settings.php +++ b/engine/lib/user_settings.php @@ -33,9 +33,9 @@ function users_settings_save() { * @access private */ function elgg_set_user_password() { - $current_password = get_input('current_password'); - $password = get_input('password'); - $password2 = get_input('password2'); + $current_password = get_input('current_password', null, false); + $password = get_input('password', null, false); + $password2 = get_input('password2', null, false); $user_guid = get_input('guid'); if (!$user_guid) { diff --git a/engine/lib/users.php b/engine/lib/users.php index c38bb676e..6a881777e 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -497,20 +497,26 @@ $timelower = 0, $timeupper = 0) { * @param int $timelower The earliest time the entity can have been created. Default: all * @param int $timeupper The latest time the entity can have been created. Default: all * - * @return string The list in a form suitable to display + * @return string */ function list_user_friends_objects($user_guid, $subtype = "", $limit = 10, $full_view = true, $listtypetoggle = true, $pagination = true, $timelower = 0, $timeupper = 0) { - $offset = (int) get_input('offset'); - $limit = (int) $limit; - $count = (int) count_user_friends_objects($user_guid, $subtype, $timelower, $timeupper); + $offset = (int)get_input('offset'); + $limit = (int)$limit; + $count = (int)count_user_friends_objects($user_guid, $subtype, $timelower, $timeupper); $entities = get_user_friends_objects($user_guid, $subtype, $limit, $offset, $timelower, $timeupper); - return elgg_view_entity_list($entities, $count, $offset, $limit, $full_view, - $listtypetoggle, $pagination); + return elgg_view_entity_list($entities, array( + 'count' => $count, + 'offset' => $offset, + 'limit' => $limit, + 'full_view' => $full_view, + 'list_type_toggle' => $listtypetoggle, + 'pagination' => $pagination, + )); } /** @@ -606,11 +612,11 @@ function get_user_by_code($code) { } /** - * Get an array of users from their email addresses + * Get an array of users from an email address * * @param string $email Email address. * - * @return Array of users + * @return array */ function get_user_by_email($email) { global $CONFIG; @@ -963,8 +969,8 @@ $allow_multiple_emails = false, $friend_guid = 0, $invitecode = '') { $friend_user->addFriend($user->guid); // @todo Should this be in addFriend? - add_to_river('friends/river/create', 'friend', $user->getGUID(), $friend_guid); - add_to_river('friends/river/create', 'friend', $friend_guid, $user->getGUID()); + add_to_river('river/relationship/friend/create', 'friend', $user->getGUID(), $friend_guid); + add_to_river('river/relationship/friend/create', 'friend', $friend_guid, $user->getGUID()); } } } diff --git a/engine/lib/views.php b/engine/lib/views.php index e59edac96..ca0ce7196 100644 --- a/engine/lib/views.php +++ b/engine/lib/views.php @@ -560,7 +560,7 @@ function elgg_view($view, $vars = array(), $bypass = false, $debug = false, $vie * * @return void * @since 1.7.0 - * @link http://docs.elgg.org/Views/Ejxtend + * @link http://docs.elgg.org/Views/Extend * @example views/extend.php */ function elgg_extend_view($view, $view_extension, $priority = 501, $viewtype = '') { @@ -969,12 +969,7 @@ function elgg_view_annotation(ElggAnnotation $annotation, array $vars = array(), return elgg_view($view, $vars, $bypass, $debug); } - // @todo would be better to always make sure name is initialized properly $name = $annotation->name; - $intname = (int) $name; - if ("{$intname}" == "{$name}") { - $name = get_metastring($intname); - } if (empty($name)) { return false; } @@ -1330,21 +1325,18 @@ function elgg_view_form($action, $form_vars = array(), $body_vars = array()) { * @access private */ function elgg_view_list_item($item, array $vars = array()) { + global $CONFIG; - switch ($item->getType()) { - case 'user': - case 'object': - case 'group': - case 'site': - return elgg_view_entity($item, $vars); - case 'annotation': - return elgg_view_annotation($item, $vars); - case 'river': - return elgg_view_river_item($item, $vars); - default: - return false; - break; + $type = $item->getType(); + if (in_array($type, $CONFIG->entity_types)) { + return elgg_view_entity($item, $vars); + } else if ($type == 'annotation') { + return elgg_view_annotation($item, $vars); + } else if ($type == 'river') { + return elgg_view_river_item($item, $vars); } + + return false; } /** @@ -1671,5 +1663,5 @@ function elgg_views_boot() { } } -elgg_register_event_handler('boot', 'system', 'elgg_views_boot', 1000); +elgg_register_event_handler('boot', 'system', 'elgg_views_boot'); elgg_register_event_handler('init', 'system', 'elgg_views_handle_deprecated_views'); diff --git a/engine/lib/web_services.php b/engine/lib/web_services.php index 07be76ec6..da3ed76a9 100644 --- a/engine/lib/web_services.php +++ b/engine/lib/web_services.php @@ -1165,7 +1165,7 @@ function list_all_apis() { * @access private */ function auth_gettoken($username, $password) { - if (elgg_authenticate($username, $password)) { + if (true === elgg_authenticate($username, $password)) { $token = create_user_token($username); if ($token) { return $token; diff --git a/engine/lib/xml-rpc.php b/engine/lib/xml-rpc.php new file mode 100644 index 000000000..bfe1a8645 --- /dev/null +++ b/engine/lib/xml-rpc.php @@ -0,0 +1,203 @@ +<?php +/** + * Elgg XML-RPC library. + * Contains functions and classes to handle XML-RPC services, currently only server only. + * + * @package Elgg.Core + * @subpackage XMLRPC + */ + +/** + * parse XMLRPCCall parameters + * + * Convert an XMLRPCCall result array into native data types + * + * @param array $parameters An array of params + * + * @return array + * @access private + */ +function xmlrpc_parse_params($parameters) { + $result = array(); + + foreach ($parameters as $parameter) { + $result[] = xmlrpc_scalar_value($parameter); + } + + return $result; +} + +/** + * Extract the scalar value of an XMLObject type result array + * + * @param XMLObject $object And object + * + * @return mixed + * @access private + */ +function xmlrpc_scalar_value($object) { + if ($object->name == 'param') { + $object = $object->children[0]->children[0]; + } + + switch ($object->name) { + case 'string': + return $object->content; + + case 'array': + foreach ($object->children[0]->children as $child) { + $value[] = xmlrpc_scalar_value($child); + } + return $value; + + case 'struct': + foreach ($object->children as $child) { + if (isset($child->children[1]->children[0])) { + $value[$child->children[0]->content] = xmlrpc_scalar_value($child->children[1]->children[0]); + } else { + $value[$child->children[0]->content] = $child->children[1]->content; + } + } + return $value; + + case 'boolean': + return (boolean) $object->content; + + case 'i4': + case 'int': + return (int) $object->content; + + case 'double': + return (double) $object->content; + + case 'dateTime.iso8601': + return (int) strtotime($object->content); + + case 'base64': + return base64_decode($object->content); + + case 'value': + return xmlrpc_scalar_value($object->children[0]); + + default: + // @todo unsupported, throw an error + return false; + } +} + +// Functions for adding handlers ////////////////////////////////////////////////////////// + +/** XML-RPC Handlers */ +global $XML_RPC_HANDLERS; +$XML_RPC_HANDLERS = array(); + +/** + * Register a method handler for a given XML-RPC method. + * + * @param string $method Method parameter. + * @param string $handler The handler function. This function accepts + * one XMLRPCCall object and must return a XMLRPCResponse object. + * + * @return bool + */ +function register_xmlrpc_handler($method, $handler) { + global $XML_RPC_HANDLERS; + + $XML_RPC_HANDLERS[$method] = $handler; +} + +/** + * Trigger a method call and pass the relevant parameters to the funciton. + * + * @param XMLRPCCall $parameters The call and parameters. + * + * @return XMLRPCCall + * @access private + */ +function trigger_xmlrpc_handler(XMLRPCCall $parameters) { + global $XML_RPC_HANDLERS; + + // Go through and see if we have a handler + if (isset($XML_RPC_HANDLERS[$parameters->getMethodName()])) { + $handler = $XML_RPC_HANDLERS[$parameters->getMethodName()]; + $result = $handler($parameters); + + if (!($result instanceof XMLRPCResponse)) { + $msg = elgg_echo('InvalidParameterException:UnexpectedReturnFormat', + array($parameters->getMethodName())); + throw new InvalidParameterException($msg); + } + + // Result in right format, return it. + return $result; + } + + // if no handler then throw exception + $msg = elgg_echo('NotImplementedException:XMLRPCMethodNotImplemented', + array($parameters->getMethodName())); + throw new NotImplementedException($msg); +} + +/** + * PHP Error handler function. + * This function acts as a wrapper to catch and report PHP error messages. + * + * @see http://uk3.php.net/set-error-handler + * + * @param int $errno Error number + * @param string $errmsg Human readable message + * @param string $filename Filename + * @param int $linenum Line number + * @param array $vars Vars + * + * @return void + * @access private + */ +function _php_xmlrpc_error_handler($errno, $errmsg, $filename, $linenum, $vars) { + $error = date("Y-m-d H:i:s (T)") . ": \"" . $errmsg . "\" in file " + . $filename . " (line " . $linenum . ")"; + + switch ($errno) { + case E_USER_ERROR: + error_log("ERROR: " . $error); + + // Since this is a fatal error, we want to stop any further execution but do so gracefully. + throw new Exception("ERROR: " . $error); + break; + + case E_WARNING : + case E_USER_WARNING : + error_log("WARNING: " . $error); + break; + + default: + error_log("DEBUG: " . $error); + } +} + +/** + * PHP Exception handler for XMLRPC. + * + * @param Exception $exception The exception + * + * @return void + * @access private + */ +function _php_xmlrpc_exception_handler($exception) { + + error_log("*** FATAL EXCEPTION (XML-RPC) *** : " . $exception); + + $code = $exception->getCode(); + + if ($code == 0) { + $code = -32400; + } + + $result = new XMLRPCErrorResponse($exception->getMessage(), $code); + + $vars = array('result' => $result); + + $content = elgg_view("xml-rpc/output", $vars); + + echo elgg_view_page($exception->getMessage(), $content); +} diff --git a/engine/schema/mysql.sql b/engine/schema/mysql.sql index 74cf2ce74..6c6e9db89 100644 --- a/engine/schema/mysql.sql +++ b/engine/schema/mysql.sql @@ -361,6 +361,7 @@ CREATE TABLE `prefix_system_log` ( `access_id` int(11) NOT NULL, `enabled` enum('yes','no') NOT NULL DEFAULT 'yes', `time_created` int(11) NOT NULL, + `ip_address` varchar(15) NOT NULL, PRIMARY KEY (`id`), KEY `object_id` (`object_id`), KEY `object_class` (`object_class`), diff --git a/engine/settings.example.php b/engine/settings.example.php index e42f3e9c6..3b139d710 100644 --- a/engine/settings.example.php +++ b/engine/settings.example.php @@ -107,7 +107,7 @@ $CONFIG->broken_mta = FALSE; * * Elgg stores each query and its results in a query cache. * On large sites or long-running scripts, this cache can grow to be - * large. To disable query caching, set this to FALSE. + * large. To disable query caching, set this to TRUE. * * @global bool $CONFIG->db_disable_query_cache */ @@ -121,14 +121,3 @@ $CONFIG->db_disable_query_cache = FALSE; * @global int $CONFIG->min_password_length */ $CONFIG->min_password_length = 6; - -/** - * Multisite - * - * Override settings with the domain-specific seed configuration - */ -$settings_path = dirname(__FILE__); // Change it to "/srv/seeds/" if you can -$settings_path .= $_SERVER['HTTP_HOST']."/settings.php"; -if (is_readable($settings_path)) { - include($settings_path); -} diff --git a/engine/start.php b/engine/start.php index 1decc5d80..506e27380 100644 --- a/engine/start.php +++ b/engine/start.php @@ -1,12 +1,12 @@ <?php /** - * Bootstraps and starts the Elgg engine. + * Bootstraps the Elgg engine. * * This file loads the full Elgg engine, checks the installation - * state, then emits a series of events to finish booting Elgg: + * state, and triggers a series of events to finish booting Elgg: * - {@elgg_event boot system} - * - {@elgg_event plugins_boot system} * - {@elgg_event init system} + * - {@elgg_event ready system} * * If Elgg is fully uninstalled, the browser will be redirected to an * installation page. @@ -36,13 +36,12 @@ $START_MICROTIME = microtime(true); * Configuration values. * * The $CONFIG global contains configuration values required - * for running Elgg as defined in the settings.php file. The following - * array keys are defined by core Elgg: + * for running Elgg as defined in the settings.php file. * - * Plugin authors are encouraged to use get_config() instead of accessing the - * global directly. + * Plugin authors are encouraged to use elgg_get_config() instead of accessing + * the global directly. * - * @see get_config() + * @see elgg_get_config() * @see engine/settings.php * @global stdClass $CONFIG */ @@ -53,105 +52,58 @@ if (!isset($CONFIG)) { $lib_dir = dirname(__FILE__) . '/lib/'; -/** - * The minimum required libs to bootstrap an Elgg installation. - * - * @var array - */ -$required_files = array( - 'elgglib.php', 'views.php', 'access.php', 'system_log.php', 'export.php', - 'sessions.php', 'languages.php', 'pageowner.php', 'input.php', 'cache.php', - 'output.php' -); - -// include bootstraping libs -foreach ($required_files as $file) { - $path = $lib_dir . $file; - if (!include($path)) { - echo "Could not load file '$path'. " - . 'Please check your Elgg installation for all required files.'; - exit; - } +// Load the bootstrapping library +$path = $lib_dir . 'elgglib.php'; +if (!include_once($path)) { + echo "Could not load file '$path'. Please check your Elgg installation for all required files."; + exit; } -// Register the error handler -set_error_handler('_elgg_php_error_handler'); -set_exception_handler('_elgg_php_exception_handler'); - -/** - * Load the system settings - */ +// Load the system settings if (!include_once(dirname(__FILE__) . "/settings.php")) { - $msg = elgg_echo('InstallationException:CannotLoadSettings'); + $msg = 'Elgg could not load the settings file. It does not exist or there is a file permissions issue.'; throw new InstallationException($msg); } // load the rest of the library files from engine/lib/ $lib_files = array( - // these need to be loaded first. - 'database.php', 'actions.php', - - 'admin.php', 'annotations.php', - 'configuration.php', 'cron.php', 'entities.php', 'export.php', - 'extender.php', 'filestore.php', 'group.php', - 'location.php', 'mb_wrapper.php', 'memcache.php', 'metadata.php', - 'metastrings.php', 'navigation.php', 'notification.php', 'objects.php', - 'opendd.php', 'pagehandler.php', 'pam.php', 'plugins.php', - 'private_settings.php', 'relationships.php', 'river.php', 'sites.php', - 'statistics.php', 'tags.php', 'user_settings.php', 'users.php', - 'upgrade.php', 'web_services.php', 'widgets.php', 'xml.php', + 'access.php', 'actions.php', 'admin.php', 'annotations.php', 'cache.php', + 'calendar.php', 'configuration.php', 'cron.php', 'database.php', + 'entities.php', 'export.php', 'extender.php', 'filestore.php', 'group.php', + 'input.php', 'languages.php', 'location.php', 'mb_wrapper.php', + 'memcache.php', 'metadata.php', 'metastrings.php', 'navigation.php', + 'notification.php', 'objects.php', 'opendd.php', 'output.php', + 'pagehandler.php', 'pageowner.php', 'pam.php', 'plugins.php', + 'private_settings.php', 'relationships.php', 'river.php', 'sessions.php', + 'sites.php', 'statistics.php', 'system_log.php', 'tags.php', + 'user_settings.php', 'users.php', 'upgrade.php', 'views.php', + 'web_services.php', 'widgets.php', 'xml.php', 'xml-rpc.php', // backward compatibility - 'deprecated-1.7.php', 'deprecated-1.8.php', 'deprecated-1.9.php' + 'deprecated-1.7.php', 'deprecated-1.8.php', ); foreach ($lib_files as $file) { $file = $lib_dir . $file; elgg_log("Loading $file..."); if (!include_once($file)) { - $msg = sprintf(elgg_echo('InstallationException:MissingLibrary'), $file); + $msg = "Could not load $file"; throw new InstallationException($msg); } } -// connect to db -setup_db_connections(); - -// confirm that the installation completed successfully -verify_installation(); - -// Autodetect some default configuration settings -set_default_config(); - -// needs to be set for links in html head -$viewtype = get_input('view', 'default'); -$lastcached = datalist_get("simplecache_lastcached_$viewtype"); -$CONFIG->lastcache = $lastcached; - -// Trigger boot events for core. Plugins can't hook -// into this because they haven't been loaded yet. +// Connect to database, load language files, load configuration, init session +// Plugins can't use this event because they haven't been loaded yet. elgg_trigger_event('boot', 'system'); // Load the plugins that are active elgg_load_plugins(); +// @todo deprecate as plugins can use 'init', 'system' event elgg_trigger_event('plugins_boot', 'system'); -// Trigger system init event for plugins +// Complete the boot process for both engine and plugins elgg_trigger_event('init', 'system'); -// Regenerate the simple cache if expired. -// Don't do it on upgrade because upgrade does it itself. -// @todo - move into function and perhaps run off init system event -if (!defined('UPGRADING')) { - $lastupdate = datalist_get("simplecache_lastupdate_$viewtype"); - $lastcached = datalist_get("simplecache_lastcached_$viewtype"); - if ($lastupdate == 0 || $lastcached < $lastupdate) { - elgg_regenerate_simplecache($viewtype); - $lastcached = datalist_get("simplecache_lastcached_$viewtype"); - } - $CONFIG->lastcache = $lastcached; -} - // System loaded and ready elgg_trigger_event('ready', 'system'); diff --git a/engine/tests/api/helpers.php b/engine/tests/api/helpers.php index 77205138d..62e4471e0 100644 --- a/engine/tests/api/helpers.php +++ b/engine/tests/api/helpers.php @@ -518,4 +518,87 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { $this->assertIdentical($elements_sorted_string, $test_elements); } + + // see http://trac.elgg.org/ticket/4288 + public function testElggBatchIncOffset() { + // normal increment + $options = array( + 'offset' => 0, + 'limit' => 11 + ); + $batch = new ElggBatch(array('ElggCoreHelpersTest', 'elgg_batch_callback_test'), $options, + null, 5); + $j = 0; + foreach ($batch as $e) { + $offset = floor($j / 5) * 5; + $this->assertEqual($offset, $e['offset']); + $this->assertEqual($j + 1, $e['index']); + $j++; + } + + $this->assertEqual(11, $j); + + // no increment, 0 start + ElggCoreHelpersTest::elgg_batch_callback_test(array(), true); + $options = array( + 'offset' => 0, + 'limit' => 11 + ); + $batch = new ElggBatch(array('ElggCoreHelpersTest', 'elgg_batch_callback_test'), $options, + null, 5); + $batch->setIncrementOffset(false); + + $j = 0; + foreach ($batch as $e) { + $this->assertEqual(0, $e['offset']); + // should always be the same 5 + $this->assertEqual($e['index'], $j + 1 - (floor($j / 5) * 5)); + $j++; + } + $this->assertEqual(11, $j); + + // no increment, 3 start + ElggCoreHelpersTest::elgg_batch_callback_test(array(), true); + $options = array( + 'offset' => 3, + 'limit' => 11 + ); + $batch = new ElggBatch(array('ElggCoreHelpersTest', 'elgg_batch_callback_test'), $options, + null, 5); + $batch->setIncrementOffset(false); + + $j = 0; + foreach ($batch as $e) { + $this->assertEqual(3, $e['offset']); + // same 5 results + $this->assertEqual($e['index'], $j + 4 - (floor($j / 5) * 5)); + $j++; + } + + $this->assertEqual(11, $j); + } + + static function elgg_batch_callback_test($options, $reset = false) { + static $count = 1; + + if ($reset) { + $count = 1; + return true; + } + + if ($count > 20) { + return false; + } + + for ($j = 0; ($options['limit'] < 5) ? $j < $options['limit'] : $j < 5; $j++) { + $return[] = array( + 'offset' => $options['offset'], + 'limit' => $options['limit'], + 'count' => $count++, + 'index' => 1 + $options['offset'] + $j + ); + } + + return $return; + } }
\ No newline at end of file diff --git a/engine/tests/api/metastrings.php b/engine/tests/api/metastrings.php index a96388217..0a8945084 100644 --- a/engine/tests/api/metastrings.php +++ b/engine/tests/api/metastrings.php @@ -132,7 +132,7 @@ class ElggCoreMetastringsTest extends ElggCoreUnitTest { public function testKeepMeFromDeletingEverything() { foreach ($this->metastringTypes as $type) { $required = array( - 'guid', 'guids', 'limit' + 'guid', 'guids' ); switch ($type) { @@ -160,6 +160,10 @@ class ElggCoreMetastringsTest extends ElggCoreUnitTest { $options = array(); $this->assertFalse(elgg_is_valid_options_for_batch_operation($options, $type)); + // limit alone isn't valid: + $options = array('limit' => 10); + $this->assertFalse(elgg_is_valid_options_for_batch_operation($options, $type)); + foreach ($required as $key) { $options = array(); diff --git a/engine/tests/objects/objects.php b/engine/tests/objects/objects.php index 0d0df6b75..915594e0a 100644 --- a/engine/tests/objects/objects.php +++ b/engine/tests/objects/objects.php @@ -194,7 +194,99 @@ class ElggCoreObjectTest extends ElggCoreUnitTest { $old = elgg_set_ignore_access(true); } + // see http://trac.elgg.org/ticket/1196 + public function testElggEntityRecursiveDisableWhenLoggedOut() { + $e1 = new ElggObject(); + $e1->access_id = ACCESS_PUBLIC; + $e1->owner_guid = 0; + $e1->container_guid = 0; + $e1->save(); + $guid1 = $e1->getGUID(); + + $e2 = new ElggObject(); + $e2->container_guid = $guid1; + $e2->access_id = ACCESS_PUBLIC; + $e2->owner_guid = 0; + $e2->save(); + $guid2 = $e2->getGUID(); + + // fake being logged out + $user = $_SESSION['user']; + unset($_SESSION['user']); + $ia = elgg_set_ignore_access(true); + + $this->assertTrue(disable_entity($guid1, null, true)); + + // "log in" original user + $_SESSION['user'] = $user; + elgg_set_ignore_access($ia); + + $this->assertFalse(get_entity($guid1)); + $this->assertFalse(get_entity($guid2)); + + $db_prefix = get_config('dbprefix'); + $q = "SELECT * FROM {$db_prefix}entities WHERE guid = $guid1"; + $r = get_data_row($q); + $this->assertEqual('no', $r->enabled); + + $q = "SELECT * FROM {$db_prefix}entities WHERE guid = $guid2"; + $r = get_data_row($q); + $this->assertEqual('no', $r->enabled); + + access_show_hidden_entities(true); + delete_entity($guid1); + delete_entity($guid2); + access_show_hidden_entities(false); + } + + public function testElggRecursiveDelete() { + $types = array('ElggGroup', 'ElggObject', 'ElggUser', 'ElggSite'); + $db_prefix = elgg_get_config('dbprefix'); + + foreach ($types as $type) { + $parent = new $type(); + $this->assertTrue($parent->save()); + + $child = new ElggObject(); + $child->container_guid = $parent->guid; + $this->assertTrue($child->save()); + + $grandchild = new ElggObject(); + $grandchild->container_guid = $child->guid; + $this->assertTrue($grandchild->save()); + + $this->assertTrue($parent->delete(true)); + + $q = "SELECT * FROM {$db_prefix}entities WHERE guid = $parent->guid"; + $r = get_data($q); + $this->assertFalse($r); + + $q = "SELECT * FROM {$db_prefix}entities WHERE guid = $child->guid"; + $r = get_data($q); + $this->assertFalse($r); + $q = "SELECT * FROM {$db_prefix}entities WHERE guid = $grandchild->guid"; + $r = get_data($q); + $this->assertFalse($r); + } + + // object that owns itself + // can't check container_guid because of infinite loops in can_edit_entity() + $obj = new ElggObject(); + $obj->save(); + $obj->owner_guid = $obj->guid; + $obj->save(); + + $q = "SELECT * FROM {$db_prefix}entities WHERE guid = $obj->guid"; + $r = get_data_row($q); + $this->assertEqual($obj->guid, $r->owner_guid); + + $this->assertTrue($obj->delete(true)); + + $q = "SELECT * FROM {$db_prefix}entities WHERE guid = $obj->guid"; + $r = get_data_row($q); + $this->assertFalse($r); + } protected function get_object_row($guid) { global $CONFIG; diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php index 99cf81774..26a45ab6a 100644 --- a/engine/tests/regression/trac_bugs.php +++ b/engine/tests/regression/trac_bugs.php @@ -199,4 +199,22 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { $this->assertFalse($result); $this->assertEqual(array(), $DB_DELAYED_QUERIES); } + + /** + * http://trac.elgg.org/ticket/3210 - Don't remove -s in friendly titles + * @todo: http://trac.elgg.org/ticket/2276 - improve char encoding + */ + public function test_friendly_title() { + $cases = array( + 'Simple Test' => 'simple-test', + 'Test top-level page' => 'test-top-level-page', +// 'éclair' => 'éclair', +// 'English, Español, and 日本語' => 'english-español-and-日本語' + ); + + foreach ($cases as $case => $expected) { + $friendly_title = elgg_get_friendly_title($case); + $this->assertIdentical($expected, $friendly_title); + } + } } |