aboutsummaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engine')
-rw-r--r--engine/classes/APIException.php11
-rw-r--r--engine/classes/CallException.php10
-rw-r--r--engine/classes/ClassException.php10
-rw-r--r--engine/classes/ClassNotFoundException.php10
-rw-r--r--engine/classes/ConfigurationException.php10
-rw-r--r--engine/classes/CronException.php10
-rw-r--r--engine/classes/DataFormatException.php9
-rw-r--r--engine/classes/DatabaseException.php10
-rw-r--r--engine/classes/ElggAccess.php70
-rw-r--r--engine/classes/ElggAnnotation.php133
-rw-r--r--engine/classes/ElggAttributeLoader.php248
-rw-r--r--engine/classes/ElggAutoP.php336
-rw-r--r--engine/classes/ElggBatch.php433
-rw-r--r--engine/classes/ElggCache.php247
-rw-r--r--engine/classes/ElggCrypto.php208
-rw-r--r--engine/classes/ElggData.php309
-rw-r--r--engine/classes/ElggDiskFilestore.php417
-rw-r--r--engine/classes/ElggEntity.php1770
-rw-r--r--engine/classes/ElggExtender.php214
-rw-r--r--engine/classes/ElggFile.php440
-rw-r--r--engine/classes/ElggFileCache.php230
-rw-r--r--engine/classes/ElggFilestore.php139
-rw-r--r--engine/classes/ElggGroup.php393
-rw-r--r--engine/classes/ElggGroupItemVisibility.php93
-rw-r--r--engine/classes/ElggHMACCache.php99
-rw-r--r--engine/classes/ElggLRUCache.php181
-rw-r--r--engine/classes/ElggMemcache.php203
-rw-r--r--engine/classes/ElggMenuBuilder.php291
-rw-r--r--engine/classes/ElggMenuItem.php590
-rw-r--r--engine/classes/ElggMetadata.php158
-rw-r--r--engine/classes/ElggObject.php216
-rw-r--r--engine/classes/ElggPAM.php105
-rw-r--r--engine/classes/ElggPlugin.php1006
-rw-r--r--engine/classes/ElggPluginManifest.php656
-rw-r--r--engine/classes/ElggPluginManifestParser.php102
-rw-r--r--engine/classes/ElggPluginManifestParser17.php82
-rw-r--r--engine/classes/ElggPluginManifestParser18.php97
-rw-r--r--engine/classes/ElggPluginPackage.php640
-rw-r--r--engine/classes/ElggPriorityList.php366
-rw-r--r--engine/classes/ElggRelationship.php231
-rw-r--r--engine/classes/ElggRiverItem.php115
-rw-r--r--engine/classes/ElggSession.php153
-rw-r--r--engine/classes/ElggSharedMemoryCache.php40
-rw-r--r--engine/classes/ElggSite.php455
-rw-r--r--engine/classes/ElggStaticVariableCache.php96
-rw-r--r--engine/classes/ElggTranslit.php269
-rw-r--r--engine/classes/ElggUser.php588
-rw-r--r--engine/classes/ElggVolatileMetadataCache.php355
-rw-r--r--engine/classes/ElggWidget.php245
-rw-r--r--engine/classes/ElggXMLElement.php131
-rw-r--r--engine/classes/ErrorResult.php54
-rw-r--r--engine/classes/ExportException.php9
-rw-r--r--engine/classes/Exportable.php23
-rw-r--r--engine/classes/Friendable.php104
-rw-r--r--engine/classes/GenericResult.php125
-rw-r--r--engine/classes/IOException.php9
-rw-r--r--engine/classes/ImportException.php8
-rw-r--r--engine/classes/Importable.php19
-rw-r--r--engine/classes/IncompleteEntityException.php10
-rw-r--r--engine/classes/InstallationException.php9
-rw-r--r--engine/classes/InvalidClassException.php9
-rw-r--r--engine/classes/InvalidParameterException.php9
-rw-r--r--engine/classes/Locatable.php49
-rw-r--r--engine/classes/Loggable.php65
-rw-r--r--engine/classes/LoginException.php10
-rw-r--r--engine/classes/NotImplementedException.php10
-rw-r--r--engine/classes/Notable.php41
-rw-r--r--engine/classes/NotificationException.php8
-rw-r--r--engine/classes/ODD.php131
-rw-r--r--engine/classes/ODDDocument.php202
-rw-r--r--engine/classes/ODDEntity.php34
-rw-r--r--engine/classes/ODDMetaData.php39
-rw-r--r--engine/classes/ODDRelationship.php33
-rw-r--r--engine/classes/PluginException.php11
-rw-r--r--engine/classes/RegistrationException.php9
-rw-r--r--engine/classes/SecurityException.php10
-rw-r--r--engine/classes/SuccessResult.php34
-rw-r--r--engine/classes/XMLRPCArrayParameter.php56
-rw-r--r--engine/classes/XMLRPCBase64Parameter.php28
-rw-r--r--engine/classes/XMLRPCBoolParameter.php30
-rw-r--r--engine/classes/XMLRPCCall.php62
-rw-r--r--engine/classes/XMLRPCDateParameter.php33
-rw-r--r--engine/classes/XMLRPCDoubleParameter.php29
-rw-r--r--engine/classes/XMLRPCErrorResponse.php36
-rw-r--r--engine/classes/XMLRPCIntParameter.php29
-rw-r--r--engine/classes/XMLRPCParameter.php16
-rw-r--r--engine/classes/XMLRPCResponse.php71
-rw-r--r--engine/classes/XMLRPCStringParameter.php30
-rw-r--r--engine/classes/XMLRPCStructParameter.php55
-rw-r--r--engine/classes/XMLRPCSuccessResponse.php22
-rw-r--r--engine/classes/XmlElement.php20
-rw-r--r--engine/handlers/action_handler.php25
-rw-r--r--engine/handlers/cache_handler.php105
-rw-r--r--engine/handlers/cron_handler.php40
-rw-r--r--engine/handlers/export_handler.php118
-rw-r--r--engine/handlers/page_handler.php48
-rw-r--r--engine/handlers/pagehandler.php23
-rw-r--r--engine/handlers/service_handler.php28
-rw-r--r--engine/handlers/xml-rpc_handler.php22
-rw-r--r--engine/lib/access.php975
-rw-r--r--engine/lib/actions.php520
-rw-r--r--engine/lib/admin.php745
-rw-r--r--engine/lib/annotations.php1246
-rw-r--r--engine/lib/cache.php694
-rw-r--r--engine/lib/calendar.php332
-rw-r--r--engine/lib/configuration.php638
-rw-r--r--engine/lib/cron.php106
-rw-r--r--engine/lib/database.php686
-rw-r--r--engine/lib/deprecated-1.7.php1164
-rw-r--r--engine/lib/deprecated-1.8.php4820
-rw-r--r--engine/lib/deprecated-1.9.php582
-rw-r--r--engine/lib/elgglib.php3234
-rw-r--r--engine/lib/entities.php3732
-rw-r--r--engine/lib/exceptions.php161
-rw-r--r--engine/lib/export.php165
-rw-r--r--engine/lib/extender.php360
-rw-r--r--engine/lib/filestore.php1392
-rw-r--r--engine/lib/group.php898
-rw-r--r--engine/lib/input.php391
-rw-r--r--engine/lib/install.php140
-rw-r--r--engine/lib/languages.php216
-rw-r--r--engine/lib/location.php309
-rw-r--r--engine/lib/mb_wrapper.php242
-rw-r--r--engine/lib/memcache.php179
-rw-r--r--engine/lib/metadata.php1157
-rw-r--r--engine/lib/metastrings.php746
-rw-r--r--engine/lib/navigation.php527
-rw-r--r--engine/lib/notification.php248
-rw-r--r--engine/lib/objects.php351
-rw-r--r--engine/lib/opendd.php316
-rw-r--r--engine/lib/output.php447
-rw-r--r--engine/lib/pagehandler.php139
-rw-r--r--engine/lib/pageowner.php340
-rw-r--r--engine/lib/pam.php105
-rw-r--r--engine/lib/plugins.php1538
-rw-r--r--engine/lib/private_settings.php414
-rw-r--r--engine/lib/query.php889
-rw-r--r--engine/lib/relationships.php895
-rw-r--r--engine/lib/river.php1104
-rw-r--r--engine/lib/sessions.php406
-rw-r--r--engine/lib/sites.php614
-rw-r--r--engine/lib/social.php119
-rw-r--r--engine/lib/statistics.php61
-rw-r--r--engine/lib/system_log.php197
-rw-r--r--engine/lib/tags.php232
-rw-r--r--engine/lib/upgrade.php365
-rw-r--r--engine/lib/upgrades/2008100701.php13
-rw-r--r--engine/lib/upgrades/2008101303.php16
-rw-r--r--engine/lib/upgrades/2009022701.php13
-rw-r--r--engine/lib/upgrades/2009041701.php14
-rw-r--r--engine/lib/upgrades/2009070101.php16
-rw-r--r--engine/lib/upgrades/2009102801.php177
-rw-r--r--engine/lib/upgrades/2010033101.php19
-rw-r--r--engine/lib/upgrades/2010040201.php3
-rw-r--r--engine/lib/upgrades/2010050701.php17
-rw-r--r--engine/lib/upgrades/2010052601.php12
-rw-r--r--engine/lib/upgrades/2010060101.php2
-rw-r--r--engine/lib/upgrades/2010060401.php14
-rw-r--r--engine/lib/upgrades/2010061501.php51
-rw-r--r--engine/lib/upgrades/2010062301.php33
-rw-r--r--engine/lib/upgrades/2010062302.php33
-rw-r--r--engine/lib/upgrades/2010070301.php9
-rw-r--r--engine/lib/upgrades/2010071001.php58
-rw-r--r--engine/lib/upgrades/2010071002.php50
-rw-r--r--engine/lib/upgrades/2010111501.php33
-rw-r--r--engine/lib/upgrades/2010121601.php9
-rw-r--r--engine/lib/upgrades/2010121602.php10
-rw-r--r--engine/lib/upgrades/2010121701.php10
-rw-r--r--engine/lib/upgrades/2010123101.php9
-rw-r--r--engine/lib/upgrades/2011010101.php98
-rw-r--r--engine/lib/upgrades/2011021800-1.8_svn-goodbye_walled_garden-083121a656d06894.php34
-rw-r--r--engine/lib/upgrades/2011022000-1.8_svn-custom_profile_fields-390ac967b0bb5665.php59
-rw-r--r--engine/lib/upgrades/2011030700-1.8_svn-blog_status_metadata-4645225d7b440876.php24
-rw-r--r--engine/lib/upgrades/2011031300-1.8_svn-twitter_api-12b832a5a7a3e1bd.php54
-rw-r--r--engine/lib/upgrades/2011031600-1.8_svn-datalist_grows_up-0b8aec5a55cc1e1c.php18
-rw-r--r--engine/lib/upgrades/2011032000-1.8_svn-widgets_arent_plugins-61836261fa280a5c.php10
-rw-r--r--engine/lib/upgrades/2011032200-1.8_svn-admins_like_widgets-7f19d2783c1680d3.php13
-rw-r--r--engine/lib/upgrades/2011052801.php46
-rw-r--r--engine/lib/upgrades/2011061200-1.8b1-sites_need_a_site_guid-6d9dcbf46c0826cc.php31
-rw-r--r--engine/lib/upgrades/2011092500-1.8.0.1-forum_reply_river_view-5758ce8d86ac56ce.php12
-rw-r--r--engine/lib/upgrades/2011123100-1.8.2-fix_friend_river-b17e7ff8345c2269.php12
-rw-r--r--engine/lib/upgrades/2011123101-1.8.2-fix_blog_status-b14c2a0e7b9e7d55.php25
-rw-r--r--engine/lib/upgrades/2012012000-1.8.3-ip_in_syslog-87fe0f068cf62428.php12
-rw-r--r--engine/lib/upgrades/2012012100-1.8.3-system_cache-93100e7d55a24a11.php13
-rw-r--r--engine/lib/upgrades/2012041800-1.8.3-dont_filter_passwords-c0ca4a18b38ae2bc.php11
-rw-r--r--engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php13
-rw-r--r--engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php24
-rw-r--r--engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php28
-rw-r--r--engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php12
-rw-r--r--engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php16
-rw-r--r--engine/lib/upgrades/create_upgrade.php152
-rw-r--r--engine/lib/user_settings.php360
-rw-r--r--engine/lib/users.php1918
-rw-r--r--engine/lib/usersettings.php76
-rw-r--r--engine/lib/version.php132
-rw-r--r--engine/lib/views.php1818
-rw-r--r--engine/lib/web_services.php (renamed from engine/lib/api.php)834
-rw-r--r--engine/lib/widgets.php690
-rw-r--r--engine/lib/xml-rpc.php732
-rw-r--r--engine/lib/xml.php258
-rw-r--r--engine/schema/mysql.sql6
-rw-r--r--engine/schema/upgrades/2009100701.sql2
-rw-r--r--engine/schema/upgrades/2010062401.sql1
-rw-r--r--engine/settings.example.php139
-rw-r--r--engine/start.php201
-rw-r--r--engine/tests/api/access_collections.php290
-rw-r--r--engine/tests/api/annotations.php150
-rw-r--r--engine/tests/api/entity_getter_functions.php940
-rw-r--r--engine/tests/api/helpers.php646
-rw-r--r--engine/tests/api/metadata.php230
-rw-r--r--engine/tests/api/metadata_cache.php176
-rw-r--r--engine/tests/api/metastrings.php217
-rw-r--r--engine/tests/api/output.php74
-rw-r--r--engine/tests/api/plugins.php299
-rw-r--r--engine/tests/api/river.php21
-rw-r--r--engine/tests/objects/entities.php255
-rw-r--r--engine/tests/objects/filestore.php16
-rw-r--r--engine/tests/objects/metadata.php107
-rw-r--r--engine/tests/objects/objects.php133
-rw-r--r--engine/tests/objects/sites.php41
-rw-r--r--engine/tests/objects/users.php93
-rw-r--r--engine/tests/regression/trac_bugs.php363
-rw-r--r--engine/tests/services/api.php17
-rw-r--r--engine/tests/suite.php12
-rw-r--r--engine/tests/test_files/output/autop/block-a.exp.norun.html6
-rw-r--r--engine/tests/test_files/output/autop/block-a.in.norun.html9
-rw-r--r--engine/tests/test_files/output/autop/domdoc_exp.html46
-rw-r--r--engine/tests/test_files/output/autop/domdoc_in.html80
-rw-r--r--engine/tests/test_files/output/autop/typical-post.exp.html84
-rw-r--r--engine/tests/test_files/output/autop/typical-post.in.html89
-rw-r--r--engine/tests/test_files/output/autop/wp-welcome.exp.html22
-rw-r--r--engine/tests/test_files/output/autop/wp-welcome.in.html25
-rw-r--r--engine/tests/test_files/output/autop/wpautop-fails.exp.html31
-rw-r--r--engine/tests/test_files/output/autop/wpautop-fails.in.html41
-rw-r--r--engine/tests/test_files/output/autop/wysiwyg-test.exp.html51
-rw-r--r--engine/tests/test_files/output/autop/wysiwyg-test.in.html79
-rw-r--r--engine/tests/test_files/plugin_17/manifest.xml10
-rw-r--r--engine/tests/test_files/plugin_17/start.php0
-rw-r--r--engine/tests/test_files/plugin_18/manifest.xml108
-rw-r--r--engine/tests/test_files/plugin_18/start.php0
-rw-r--r--engine/tests/test_files/xxe/external_entity.txt1
-rw-r--r--engine/tests/test_files/xxe/request.xml8
-rw-r--r--engine/tests/test_skeleton.php7
-rw-r--r--engine/tests/ui/submenu.php97
244 files changed, 43846 insertions, 19900 deletions
diff --git a/engine/classes/APIException.php b/engine/classes/APIException.php
new file mode 100644
index 000000000..b6e1c347b
--- /dev/null
+++ b/engine/classes/APIException.php
@@ -0,0 +1,11 @@
+<?php
+
+/**
+ * API Exception Stub
+ *
+ * Generic parent class for API exceptions.
+ *
+ * @package Elgg.Core
+ * @subpackage Exceptions.Stub
+ */
+class APIException extends Exception {}
diff --git a/engine/classes/CallException.php b/engine/classes/CallException.php
new file mode 100644
index 000000000..22b8f14f5
--- /dev/null
+++ b/engine/classes/CallException.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Call Exception Stub
+ *
+ * Generic parent class for Call exceptions
+ *
+ * @package Elgg.Core
+ * @subpackage Exceptions.Stub
+ */
+class CallException extends Exception {}
diff --git a/engine/classes/ClassException.php b/engine/classes/ClassException.php
new file mode 100644
index 000000000..7544f0ec9
--- /dev/null
+++ b/engine/classes/ClassException.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Class Exception
+ *
+ * A generic parent class for Class exceptions
+ *
+ * @package Elgg.Core
+ * @subpackage Exceptions.Stub
+ */
+class ClassException extends Exception {}
diff --git a/engine/classes/ClassNotFoundException.php b/engine/classes/ClassNotFoundException.php
new file mode 100644
index 000000000..6a9bcd327
--- /dev/null
+++ b/engine/classes/ClassNotFoundException.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Class not found
+ *
+ * Thrown when trying to load a class that doesn't exist.
+ *
+ * @package Elgg.Core
+ * @subpackage Exceptions
+ */
+class ClassNotFoundException extends ClassException {}
diff --git a/engine/classes/ConfigurationException.php b/engine/classes/ConfigurationException.php
new file mode 100644
index 000000000..3ace5dd4b
--- /dev/null
+++ b/engine/classes/ConfigurationException.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Configuration exception
+ *
+ * A generic parent class for Configuration exceptions
+ *
+ * @package Elgg
+ * @subpackage Exceptions.Stub
+ */
+class ConfigurationException extends Exception {}
diff --git a/engine/classes/CronException.php b/engine/classes/CronException.php
new file mode 100644
index 000000000..86370ef31
--- /dev/null
+++ b/engine/classes/CronException.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Cron exception
+ *
+ * A generic parent class for cron exceptions
+ *
+ * @package Elgg
+ * @subpackage Exceptions.Stub
+ */
+class CronException extends Exception {}
diff --git a/engine/classes/DataFormatException.php b/engine/classes/DataFormatException.php
new file mode 100644
index 000000000..0f28a0902
--- /dev/null
+++ b/engine/classes/DataFormatException.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Data format exception
+ * An exception thrown when there is a problem in the format of some data.
+ *
+ * @package Elgg.Core
+ * @subpackage Exceptions.Stub
+ */
+class DataFormatException extends Exception {}
diff --git a/engine/classes/DatabaseException.php b/engine/classes/DatabaseException.php
new file mode 100644
index 000000000..6c8f57d7d
--- /dev/null
+++ b/engine/classes/DatabaseException.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Database Exception
+ *
+ * A generic parent class for database exceptions
+ *
+ * @package Elgg.Core
+ * @subpackage Exceptions.Stub
+ */
+class DatabaseException extends Exception {}
diff --git a/engine/classes/ElggAccess.php b/engine/classes/ElggAccess.php
new file mode 100644
index 000000000..0aed477fc
--- /dev/null
+++ b/engine/classes/ElggAccess.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Class used to determine if access is being ignored.
+ *
+ * @package Elgg.Core
+ * @subpackage Access
+ * @access private
+ * @see elgg_get_ignore_access()
+ *
+ * @todo I don't remember why this was required beyond scope concerns.
+ */
+class ElggAccess {
+ /**
+ * Bypass Elgg's access control if true.
+ * @var bool
+ */
+ private $ignore_access;
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Get current ignore access setting.
+ *
+ * @return bool
+ * @deprecated 1.8 Use ElggAccess::getIgnoreAccess()
+ */
+ public function get_ignore_access() {
+ elgg_deprecated_notice('ElggAccess::get_ignore_access() is deprecated by ElggAccess::getIgnoreAccess()', 1.8);
+ return $this->getIgnoreAccess();
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * Get current ignore access setting.
+ *
+ * @return bool
+ */
+ public function getIgnoreAccess() {
+ return $this->ignore_access;
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Set ignore access.
+ *
+ * @param bool $ignore Ignore access
+ *
+ * @return bool Previous setting
+ *
+ * @deprecated 1.8 Use ElggAccess:setIgnoreAccess()
+ */
+ public function set_ignore_access($ignore = true) {
+ elgg_deprecated_notice('ElggAccess::set_ignore_access() is deprecated by ElggAccess::setIgnoreAccess()', 1.8);
+ return $this->setIgnoreAccess($ignore);
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * Set ignore access.
+ *
+ * @param bool $ignore Ignore access
+ *
+ * @return bool Previous setting
+ */
+ public function setIgnoreAccess($ignore = true) {
+ $prev = $this->ignore_access;
+ $this->ignore_access = $ignore;
+
+ return $prev;
+ }
+}
diff --git a/engine/classes/ElggAnnotation.php b/engine/classes/ElggAnnotation.php
new file mode 100644
index 000000000..175e7049d
--- /dev/null
+++ b/engine/classes/ElggAnnotation.php
@@ -0,0 +1,133 @@
+<?php
+/**
+ * Elgg Annotations
+ *
+ * Annotations allow you to attach bits of information to entities.
+ * They are essentially the same as metadata, but with additional
+ * helper functions.
+ *
+ * @internal Annotations are stored in the annotations table.
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel.Annotations
+ * @link http://docs.elgg.org/DataModel/Annotations
+ *
+ * @property string $value_type
+ * @property string $enabled
+ */
+class ElggAnnotation extends ElggExtender {
+
+ /**
+ * (non-PHPdoc)
+ *
+ * @see ElggData::initializeAttributes()
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['type'] = 'annotation';
+ }
+
+ /**
+ * Construct a new annotation object
+ *
+ * @param mixed $id The annotation ID or a database row as stdClass object
+ */
+ function __construct($id = null) {
+ $this->initializeAttributes();
+
+ if (!empty($id)) {
+ // Create from db row
+ if ($id instanceof stdClass) {
+ $annotation = $id;
+
+ $objarray = (array) $annotation;
+ foreach ($objarray as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+ } else {
+ // get an ElggAnnotation object and copy its attributes
+ $annotation = elgg_get_annotation_from_id($id);
+ $this->attributes = $annotation->attributes;
+ }
+ }
+ }
+
+ /**
+ * Save this instance
+ *
+ * @return int an object id
+ *
+ * @throws IOException
+ */
+ function save() {
+ if ($this->id > 0) {
+ return update_annotation($this->id, $this->name, $this->value, $this->value_type,
+ $this->owner_guid, $this->access_id);
+ } else {
+ $this->id = create_annotation($this->entity_guid, $this->name, $this->value,
+ $this->value_type, $this->owner_guid, $this->access_id);
+
+ if (!$this->id) {
+ throw new IOException(elgg_echo('IOException:UnableToSaveNew', array(get_class())));
+ }
+ return $this->id;
+ }
+ }
+
+ /**
+ * Delete the annotation.
+ *
+ * @return bool
+ */
+ function delete() {
+ elgg_delete_river(array('annotation_id' => $this->id));
+ return elgg_delete_metastring_based_object_by_id($this->id, 'annotations');
+ }
+
+ /**
+ * Disable the annotation.
+ *
+ * @return bool
+ * @since 1.8
+ */
+ function disable() {
+ return elgg_set_metastring_based_object_enabled_by_id($this->id, 'no', 'annotations');
+ }
+
+ /**
+ * Enable the annotation.
+ *
+ * @return bool
+ * @since 1.8
+ */
+ function enable() {
+ return elgg_set_metastring_based_object_enabled_by_id($this->id, 'yes', 'annotations');
+ }
+
+ /**
+ * Get a url for this annotation.
+ *
+ * @return string
+ */
+ public function getURL() {
+ return get_annotation_url($this->id);
+ }
+
+ // SYSTEM LOG INTERFACE
+
+ /**
+ * For a given ID, return the object associated with it.
+ * This is used by the river functionality primarily.
+ * This is useful for checking access permissions etc on objects.
+ *
+ * @param int $id An annotation ID.
+ *
+ * @return ElggAnnotation
+ */
+ public function getObjectFromID($id) {
+ return elgg_get_annotation_from_id($id);
+ }
+}
diff --git a/engine/classes/ElggAttributeLoader.php b/engine/classes/ElggAttributeLoader.php
new file mode 100644
index 000000000..ffc80b02d
--- /dev/null
+++ b/engine/classes/ElggAttributeLoader.php
@@ -0,0 +1,248 @@
+<?php
+
+/**
+ * Loads ElggEntity attributes from DB or validates those passed in via constructor
+ *
+ * @access private
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel
+ */
+class ElggAttributeLoader {
+
+ /**
+ * @var array names of attributes in all entities
+ */
+ protected static $primary_attr_names = array(
+ 'guid',
+ 'type',
+ 'subtype',
+ 'owner_guid',
+ 'container_guid',
+ 'site_guid',
+ 'access_id',
+ 'time_created',
+ 'time_updated',
+ 'last_action',
+ 'enabled',
+ );
+
+ /**
+ * @var array names of secondary attributes required for the entity
+ */
+ protected $secondary_attr_names = array();
+
+ /**
+ * @var string entity type (not class) required for fetched primaries
+ */
+ protected $required_type;
+
+ /**
+ * @var array
+ */
+ protected $initialized_attributes;
+
+ /**
+ * @var string class of object being loaded
+ */
+ protected $class;
+
+ /**
+ * @var bool should access control be considered when fetching entity?
+ */
+ public $requires_access_control = true;
+
+ /**
+ * @var callable function used to load attributes from {prefix}entities table
+ */
+ public $primary_loader = 'get_entity_as_row';
+
+ /**
+ * @var callable function used to load attributes from secondary table
+ */
+ public $secondary_loader = '';
+
+ /**
+ * @var callable function used to load all necessary attributes
+ */
+ public $full_loader = '';
+
+ /**
+ * Constructor
+ *
+ * @param string $class class of object being loaded
+ * @param string $required_type entity type this is being used to populate
+ * @param array $initialized_attrs attributes after initializeAttributes() has been run
+ * @throws InvalidArgumentException
+ */
+ public function __construct($class, $required_type, array $initialized_attrs) {
+ if (!is_string($class)) {
+ throw new InvalidArgumentException('$class must be a class name.');
+ }
+ $this->class = $class;
+
+ if (!is_string($required_type)) {
+ throw new InvalidArgumentException('$requiredType must be a system entity type.');
+ }
+ $this->required_type = $required_type;
+
+ $this->initialized_attributes = $initialized_attrs;
+ unset($initialized_attrs['tables_split'], $initialized_attrs['tables_loaded']);
+ $all_attr_names = array_keys($initialized_attrs);
+ $this->secondary_attr_names = array_diff($all_attr_names, self::$primary_attr_names);
+ }
+
+ /**
+ * Get primary attributes missing that are missing
+ *
+ * @param stdClass $row Database row
+ * @return array
+ */
+ protected function isMissingPrimaries($row) {
+ return array_diff(self::$primary_attr_names, array_keys($row)) !== array();
+ }
+
+ /**
+ * Get secondary attributes that are missing
+ *
+ * @param stdClass $row Database row
+ * @return array
+ */
+ protected function isMissingSecondaries($row) {
+ return array_diff($this->secondary_attr_names, array_keys($row)) !== array();
+ }
+
+ /**
+ * Check that the type is correct
+ *
+ * @param stdClass $row Database row
+ * @return void
+ * @throws InvalidClassException
+ */
+ protected function checkType($row) {
+ if ($row['type'] !== $this->required_type) {
+ $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($row['guid'], $this->class));
+ throw new InvalidClassException($msg);
+ }
+ }
+
+ /**
+ * Get all required attributes for the entity, validating any that are passed in. Returns empty array
+ * if can't be loaded (Check $failure_reason).
+ *
+ * This function splits loading between "primary" attributes (those in {prefix}entities table) and
+ * "secondary" attributes (e.g. those in {prefix}objects_entity), but can load all at once if a
+ * combined loader is available.
+ *
+ * @param mixed $row a row loaded from DB (array or stdClass) or a GUID
+ * @return array will be empty if failed to load all attributes (access control or entity doesn't exist)
+ *
+ * @throws InvalidArgumentException|LogicException|IncompleteEntityException
+ */
+ public function getRequiredAttributes($row) {
+ if (!is_array($row) && !($row instanceof stdClass)) {
+ // assume row is the GUID
+ $row = array('guid' => $row);
+ }
+ $row = (array) $row;
+ if (empty($row['guid'])) {
+ throw new InvalidArgumentException('$row must be or contain a GUID');
+ }
+
+ // these must be present to support isFullyLoaded()
+ foreach (array('tables_split', 'tables_loaded') as $key) {
+ if (isset($this->initialized_attributes[$key])) {
+ $row[$key] = $this->initialized_attributes[$key];
+ }
+ }
+
+ $was_missing_primaries = $this->isMissingPrimaries($row);
+ $was_missing_secondaries = $this->isMissingSecondaries($row);
+
+ // some types have a function to load all attributes at once, it should be faster
+ if (($was_missing_primaries || $was_missing_secondaries) && is_callable($this->full_loader)) {
+ $fetched = (array) call_user_func($this->full_loader, $row['guid']);
+ if (!$fetched) {
+ return array();
+ }
+ $row = array_merge($row, $fetched);
+ $this->checkType($row);
+ } else {
+ if ($was_missing_primaries) {
+ if (!is_callable($this->primary_loader)) {
+ throw new LogicException('Primary attribute loader must be callable');
+ }
+ if ($this->requires_access_control) {
+ $fetched = (array) call_user_func($this->primary_loader, $row['guid']);
+ } else {
+ $ignoring_access = elgg_set_ignore_access();
+ $fetched = (array) call_user_func($this->primary_loader, $row['guid']);
+ elgg_set_ignore_access($ignoring_access);
+ }
+ if (!$fetched) {
+ return array();
+ }
+ $row = array_merge($row, $fetched);
+ }
+
+ // We must test type before trying to load the secondaries so that InvalidClassException
+ // gets thrown. Otherwise the secondary loader will fail and return false.
+ $this->checkType($row);
+
+ if ($was_missing_secondaries) {
+ if (!is_callable($this->secondary_loader)) {
+ throw new LogicException('Secondary attribute loader must be callable');
+ }
+ $fetched = (array) call_user_func($this->secondary_loader, $row['guid']);
+ if (!$fetched) {
+ if ($row['type'] === 'site') {
+ // A special case is needed for sites: When vanilla ElggEntities are created and
+ // saved, these are stored w/ type "site", but with no sites_entity row. These
+ // are probably only created in the unit tests.
+ // @todo Don't save vanilla ElggEntities with type "site"
+
+ $row = $this->filterAddedColumns($row);
+ $row['guid'] = (int) $row['guid'];
+ return $row;
+ }
+ throw new IncompleteEntityException("Secondary loader failed to return row for {$row['guid']}");
+ }
+ $row = array_merge($row, $fetched);
+ }
+ }
+
+ $row = $this->filterAddedColumns($row);
+
+ // Note: If there are still missing attributes, we're running on a 1.7 or earlier schema. We let
+ // this pass so the upgrades can run.
+
+ // guid needs to be an int https://github.com/elgg/elgg/issues/4111
+ $row['guid'] = (int) $row['guid'];
+
+ return $row;
+ }
+
+ /**
+ * Filter out keys returned by the query which should not appear in the entity's attributes
+ *
+ * @param array $row All columns from the query
+ * @return array Columns acceptable for the entity's attributes
+ */
+ protected function filterAddedColumns($row) {
+ // make an array with keys as acceptable attribute names
+ $acceptable_attrs = self::$primary_attr_names;
+ array_splice($acceptable_attrs, count($acceptable_attrs), 0, $this->secondary_attr_names);
+ $acceptable_attrs = array_combine($acceptable_attrs, $acceptable_attrs);
+
+ // @todo remove these when #4584 is in place
+ $acceptable_attrs['tables_split'] = true;
+ $acceptable_attrs['tables_loaded'] = true;
+
+ foreach ($row as $key => $val) {
+ if (!isset($acceptable_attrs[$key])) {
+ unset($row[$key]);
+ }
+ }
+ return $row;
+ }
+}
diff --git a/engine/classes/ElggAutoP.php b/engine/classes/ElggAutoP.php
new file mode 100644
index 000000000..05842d1b2
--- /dev/null
+++ b/engine/classes/ElggAutoP.php
@@ -0,0 +1,336 @@
+<?php
+
+/**
+ * Create wrapper P and BR elements in HTML depending on newlines. Useful when
+ * users use newlines to signal line and paragraph breaks. In all cases output
+ * should be well-formed markup.
+ *
+ * In DIV elements, Ps are only added when there would be at
+ * least two of them.
+ *
+ * @package Elgg.Core
+ * @subpackage Output
+ */
+class ElggAutoP {
+
+ public $encoding = 'UTF-8';
+
+ /**
+ * @var DOMDocument
+ */
+ protected $_doc = null;
+
+ /**
+ * @var DOMXPath
+ */
+ protected $_xpath = null;
+
+ protected $_blocks = 'address article area aside blockquote caption col colgroup dd
+ details div dl dt fieldset figure figcaption footer form h1 h2 h3 h4 h5 h6 header
+ hr hgroup legend map math menu nav noscript p pre section select style summary
+ table tbody td tfoot th thead tr ul ol option li';
+
+ /**
+ * @var array
+ */
+ protected $_inlines = 'a abbr audio b button canvas caption cite code command datalist
+ del dfn em embed i iframe img input ins kbd keygen label map mark meter object
+ output progress q rp rt ruby s samp script select small source span strong style
+ sub sup textarea time var video wbr';
+
+ /**
+ * Descend into these elements to add Ps
+ *
+ * @var array
+ */
+ protected $_descendList = 'article aside blockquote body details div footer form
+ header section';
+
+ /**
+ * Add Ps inside these elements
+ *
+ * @var array
+ */
+ protected $_alterList = 'article aside blockquote body details div footer header
+ section';
+
+ /** @var string */
+ protected $_unique = '';
+
+ /**
+ * Constructor
+ */
+ public function __construct() {
+ $this->_blocks = preg_split('@\\s+@', $this->_blocks);
+ $this->_descendList = preg_split('@\\s+@', $this->_descendList);
+ $this->_alterList = preg_split('@\\s+@', $this->_alterList);
+ $this->_inlines = preg_split('@\\s+@', $this->_inlines);
+ $this->_unique = md5(__FILE__);
+ }
+
+ /**
+ * Intance of class for singleton pattern.
+ * @var ElggAutoP
+ */
+ private static $instance;
+
+ /**
+ * Singleton pattern.
+ * @return ElggAutoP
+ */
+ public static function getInstance() {
+ $className = __CLASS__;
+ if (!(self::$instance instanceof $className)) {
+ self::$instance = new $className();
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Create wrapper P and BR elements in HTML depending on newlines. Useful when
+ * users use newlines to signal line and paragraph breaks. In all cases output
+ * should be well-formed markup.
+ *
+ * In DIV, LI, TD, and TH elements, Ps are only added when their would be at
+ * least two of them.
+ *
+ * @param string $html snippet
+ * @return string|false output or false if parse error occurred
+ */
+ public function process($html) {
+ // normalize whitespace
+ $html = str_replace(array("\r\n", "\r"), "\n", $html);
+
+ // allows preserving entities untouched
+ $html = str_replace('&', $this->_unique . 'AMP', $html);
+
+ $this->_doc = new DOMDocument();
+
+ // parse to DOM, suppressing loadHTML warnings
+ // http://www.php.net/manual/en/domdocument.loadhtml.php#95463
+ libxml_use_internal_errors(true);
+
+ // Do not load entities. May be unnecessary, better safe than sorry
+ $disable_load_entities = libxml_disable_entity_loader(true);
+
+ if (!$this->_doc->loadHTML("<html><meta http-equiv='content-type' "
+ . "content='text/html; charset={$this->encoding}'><body>{$html}</body>"
+ . "</html>")) {
+
+ libxml_disable_entity_loader($disable_load_entities);
+ return false;
+ }
+
+ libxml_disable_entity_loader($disable_load_entities);
+
+ $this->_xpath = new DOMXPath($this->_doc);
+ // start processing recursively at the BODY element
+ $nodeList = $this->_xpath->query('//body[1]');
+ $this->addParagraphs($nodeList->item(0));
+
+ // serialize back to HTML
+ $html = $this->_doc->saveHTML();
+
+ // Note: we create <autop> elements, which will later be converted to paragraphs
+
+ // split AUTOPs into multiples at /\n\n+/
+ $html = preg_replace('/(' . $this->_unique . 'NL){2,}/', '</autop><autop>', $html);
+ $html = str_replace(array($this->_unique . 'BR', $this->_unique . 'NL', '<br>'),
+ '<br />',
+ $html);
+ $html = str_replace('<br /></autop>', '</autop>', $html);
+
+ // re-parse so we can handle new AUTOP elements
+
+ // Do not load entities. May be unnecessary, better safe than sorry
+ $disable_load_entities = libxml_disable_entity_loader(true);
+
+ if (!$this->_doc->loadHTML($html)) {
+ libxml_disable_entity_loader($disable_load_entities);
+ return false;
+ }
+
+ libxml_disable_entity_loader($disable_load_entities);
+
+ // must re-create XPath object after DOM load
+ $this->_xpath = new DOMXPath($this->_doc);
+
+ // strip AUTOPs that only have comments/whitespace
+ foreach ($this->_xpath->query('//autop') as $autop) {
+ /* @var DOMElement $autop */
+ $hasContent = false;
+ if (trim($autop->textContent) !== '') {
+ $hasContent = true;
+ } else {
+ foreach ($autop->childNodes as $node) {
+ if ($node->nodeType === XML_ELEMENT_NODE) {
+ $hasContent = true;
+ break;
+ }
+ }
+ }
+ if (!$hasContent) {
+ // mark to be later replaced w/ preg_replace (faster than moving nodes out)
+ $autop->setAttribute("r", "1");
+ }
+ }
+
+ // If a DIV contains a single AUTOP, remove it
+ foreach ($this->_xpath->query('//div') as $el) {
+ /* @var DOMElement $el */
+ $autops = $this->_xpath->query('./autop', $el);
+ if ($autops->length === 1) {
+ $firstAutop = $autops->item(0);
+ /* @var DOMElement $firstAutop */
+ $firstAutop->setAttribute("r", "1");
+ }
+ }
+
+ $html = $this->_doc->saveHTML();
+
+ // trim to the contents of BODY
+ $bodyStart = strpos($html, '<body>');
+ $bodyEnd = strpos($html, '</body>', $bodyStart + 6);
+ $html = substr($html, $bodyStart + 6, $bodyEnd - $bodyStart - 6);
+
+ // strip AUTOPs that should be removed
+ $html = preg_replace('@<autop r="1">(.*?)</autop>@', '\\1', $html);
+
+ // commit to converting AUTOPs to Ps
+ $html = str_replace('<autop>', "\n<p>", $html);
+ $html = str_replace('</autop>', "</p>\n", $html);
+
+ $html = str_replace('<br>', '<br />', $html);
+ $html = str_replace($this->_unique . 'AMP', '&', $html);
+ return $html;
+ }
+
+ /**
+ * Add P and BR elements as necessary
+ *
+ * @param DOMElement $el DOM element
+ * @return void
+ */
+ protected function addParagraphs(DOMElement $el) {
+ // no need to call recursively, just queue up
+ $elsToProcess = array($el);
+ $inlinesToProcess = array();
+ while ($el = array_shift($elsToProcess)) {
+ // if true, we can alter all child nodes, if not, we'll just call
+ // addParagraphs on each element in the descendInto list
+ $alterInline = in_array($el->nodeName, $this->_alterList);
+
+ // inside affected elements, we want to trim leading whitespace from
+ // the first text node
+ $ltrimFirstTextNode = true;
+
+ // should we open a new AUTOP element to move inline elements into?
+ $openP = true;
+ $autop = null;
+
+ // after BR, ignore a newline
+ $isFollowingBr = false;
+
+ $node = $el->firstChild;
+ while (null !== $node) {
+ if ($alterInline) {
+ if ($openP) {
+ $openP = false;
+ // create a P to move inline content into (this may be removed later)
+ $autop = $el->insertBefore($this->_doc->createElement('autop'), $node);
+ }
+ }
+
+ $isElement = ($node->nodeType === XML_ELEMENT_NODE);
+ if ($isElement) {
+ $isBlock = in_array($node->nodeName, $this->_blocks);
+ } else {
+ $isBlock = false;
+ }
+
+ if ($alterInline) {
+ $isText = ($node->nodeType === XML_TEXT_NODE);
+ $isLastInline = (! $node->nextSibling
+ || ($node->nextSibling->nodeType === XML_ELEMENT_NODE
+ && in_array($node->nextSibling->nodeName, $this->_blocks)));
+ if ($isElement) {
+ $isFollowingBr = ($node->nodeName === 'br');
+ }
+
+ if ($isText) {
+ $nodeText = $node->nodeValue;
+ if ($ltrimFirstTextNode) {
+ $nodeText = ltrim($nodeText);
+ $ltrimFirstTextNode = false;
+ }
+ if ($isFollowingBr && preg_match('@^[ \\t]*\\n[ \\t]*@', $nodeText, $m)) {
+ // if a user ends a line with <br>, don't add a second BR
+ $nodeText = substr($nodeText, strlen($m[0]));
+ }
+ if ($isLastInline) {
+ $nodeText = rtrim($nodeText);
+ }
+ $nodeText = str_replace("\n", $this->_unique . 'NL', $nodeText);
+ $tmpNode = $node;
+ $node = $node->nextSibling; // move loop to next node
+
+ // alter node in place, then move into AUTOP
+ $tmpNode->nodeValue = $nodeText;
+ $autop->appendChild($tmpNode);
+
+ continue;
+ }
+ }
+ if ($isBlock || ! $node->nextSibling) {
+ if ($isBlock) {
+ if (in_array($node->nodeName, $this->_descendList)) {
+ $elsToProcess[] = $node;
+ //$this->addParagraphs($node);
+ }
+ }
+ $openP = true;
+ $ltrimFirstTextNode = true;
+ }
+ if ($alterInline) {
+ if (! $isBlock) {
+ $tmpNode = $node;
+ if ($isElement && false !== strpos($tmpNode->textContent, "\n")) {
+ $inlinesToProcess[] = $tmpNode;
+ }
+ $node = $node->nextSibling;
+ $autop->appendChild($tmpNode);
+ continue;
+ }
+ }
+
+ $node = $node->nextSibling;
+ }
+ }
+
+ // handle inline nodes
+ // no need to recurse, just queue up
+ while ($el = array_shift($inlinesToProcess)) {
+ $ignoreLeadingNewline = false;
+ foreach ($el->childNodes as $node) {
+ if ($node->nodeType === XML_ELEMENT_NODE) {
+ if ($node->nodeValue === 'BR') {
+ $ignoreLeadingNewline = true;
+ } else {
+ $ignoreLeadingNewline = false;
+ if (false !== strpos($node->textContent, "\n")) {
+ $inlinesToProcess[] = $node;
+ }
+ }
+ continue;
+ } elseif ($node->nodeType === XML_TEXT_NODE) {
+ $text = $node->nodeValue;
+ if ($text[0] === "\n" && $ignoreLeadingNewline) {
+ $text = substr($text, 1);
+ $ignoreLeadingNewline = false;
+ }
+ $node->nodeValue = str_replace("\n", $this->_unique . 'BR', $text);
+ }
+ }
+ }
+ }
+}
diff --git a/engine/classes/ElggBatch.php b/engine/classes/ElggBatch.php
new file mode 100644
index 000000000..d810ea066
--- /dev/null
+++ b/engine/classes/ElggBatch.php
@@ -0,0 +1,433 @@
+<?php
+/**
+ * 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().
+ *
+ * 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.
+ *
+ * Results from the callback are stored in callbackResult. If the callback
+ * returns only booleans, callbackResults will be the combined result of
+ * all calls. If no entities are processed, callbackResults will be null.
+ *
+ * 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("Looking at annotation id: $result->id");
+ * return true;
+ * }
+ *
+ * $batch = new ElggBatch('elgg_get_annotations', array('guid' => 2), $callback);
+ * </code>
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel
+ * @link http://docs.elgg.org/DataModel/ElggBatch
+ * @since 1.8
+ */
+class ElggBatch
+ implements Iterator {
+
+ /**
+ * The objects to interator over.
+ *
+ * @var array
+ */
+ private $results = array();
+
+ /**
+ * The function used to get results.
+ *
+ * @var mixed A string, array, or closure, or lamda function
+ */
+ private $getter = null;
+
+ /**
+ * The number of results to grab at a time.
+ *
+ * @var int
+ */
+ private $chunkSize = 25;
+
+ /**
+ * A callback function to pass results through.
+ *
+ * @var mixed A string, array, or closure, or lamda function
+ */
+ private $callback = null;
+
+ /**
+ * Start after this many results.
+ *
+ * @var int
+ */
+ private $offset = 0;
+
+ /**
+ * Stop after this many results.
+ *
+ * @var int
+ */
+ private $limit = 0;
+
+ /**
+ * Number of processed results.
+ *
+ * @var int
+ */
+ private $retrievedResults = 0;
+
+ /**
+ * The index of the current result within the current chunk
+ *
+ * @var int
+ */
+ private $resultIndex = 0;
+
+ /**
+ * The index of the current chunk
+ *
+ * @var int
+ */
+ private $chunkIndex = 0;
+
+ /**
+ * The number of results iterated through
+ *
+ * @var int
+ */
+ private $processedResults = 0;
+
+ /**
+ * Is the getter a valid callback
+ *
+ * @var bool
+ */
+ private $validGetter = null;
+
+ /**
+ * The result of running all entities through the callback function.
+ *
+ * @var mixed
+ */
+ public $callbackResult = null;
+
+ /**
+ * If false, offset will not be incremented. This is used for callbacks/loops that delete.
+ *
+ * @var bool
+ */
+ private $incrementOffset = true;
+
+ /**
+ * Entities that could not be instantiated during a fetch
+ *
+ * @var stdClass[]
+ */
+ private $incompleteEntities = array();
+
+ /**
+ * Total number of incomplete entities fetched
+ *
+ * @var int
+ */
+ private $totalIncompletes = 0;
+
+ /**
+ * Batches operations on any elgg_get_*() or compatible function that supports
+ * an options array.
+ *
+ * Instead of returning all objects in memory, it goes through $chunk_size
+ * objects, then requests more from the server. This avoids OOM errors.
+ *
+ * @param string $getter The function used to get objects. Usually
+ * an elgg_get_*() function, but can be any valid PHP callback.
+ * @param array $options The options array to pass to the getter function. If limit is
+ * not set, 10 is used as the default. In most cases that is not
+ * what you want.
+ * @param mixed $callback An optional callback function that all results will be passed
+ * to upon load. The callback needs to accept $result, $getter,
+ * $options.
+ * @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,
+ $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;
+ }
+
+ // store these so we can compare later
+ $this->offset = elgg_extract('offset', $options, 0);
+ $this->limit = elgg_extract('limit', $options, 10);
+
+ // 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, $inc_offset);
+
+ $all_results = null;
+
+ foreach ($batch as $result) {
+ if (is_string($callback)) {
+ $result = $callback($result, $getter, $options);
+ } else {
+ $result = call_user_func_array($callback, array($result, $getter, $options));
+ }
+
+ if (!isset($all_results)) {
+ if ($result === true || $result === false || $result === null) {
+ $all_results = $result;
+ } else {
+ $all_results = array();
+ }
+ }
+
+ if (($result === true || $result === false || $result === null) && !is_array($all_results)) {
+ $all_results = $result && $all_results;
+ } else {
+ $all_results[] = $result;
+ }
+ }
+
+ $this->callbackResult = $all_results;
+ }
+ }
+
+ /**
+ * Tell the process that an entity was incomplete during a fetch
+ *
+ * @param stdClass $row
+ *
+ * @access private
+ */
+ public function reportIncompleteEntity(stdClass $row) {
+ $this->incompleteEntities[] = $row;
+ }
+
+ /**
+ * Fetches the next chunk of results
+ *
+ * @return bool
+ */
+ private function getNextResultsChunk() {
+
+ // always reset results.
+ $this->results = array();
+
+ if (!isset($this->validGetter)) {
+ $this->validGetter = is_callable($this->getter);
+ }
+
+ if (!$this->validGetter) {
+ return false;
+ }
+
+ $limit = $this->chunkSize;
+
+ // if someone passed limit = 0 they want everything.
+ if ($this->limit != 0) {
+ if ($this->retrievedResults >= $this->limit) {
+ return false;
+ }
+
+ // if original limit < chunk size, set limit to original limit
+ // else if the number of results we'll fetch if greater than the original limit
+ if ($this->limit < $this->chunkSize) {
+ $limit = $this->limit;
+ } elseif ($this->retrievedResults + $this->chunkSize > $this->limit) {
+ // set the limit to the number of results remaining in the original limit
+ $limit = $this->limit - $this->retrievedResults;
+ }
+ }
+
+ if ($this->incrementOffset) {
+ $offset = $this->offset + $this->retrievedResults;
+ } else {
+ $offset = $this->offset + $this->totalIncompletes;
+ }
+
+ $current_options = array(
+ 'limit' => $limit,
+ 'offset' => $offset,
+ '__ElggBatch' => $this,
+ );
+
+ $options = array_merge($this->options, $current_options);
+
+ $this->incompleteEntities = array();
+ $this->results = call_user_func_array($this->getter, array($options));
+
+ $num_results = count($this->results);
+ $num_incomplete = count($this->incompleteEntities);
+
+ $this->totalIncompletes += $num_incomplete;
+
+ if ($this->incompleteEntities) {
+ // pad the front of the results with nulls representing the incompletes
+ array_splice($this->results, 0, 0, array_pad(array(), $num_incomplete, null));
+ // ...and skip past them
+ reset($this->results);
+ for ($i = 0; $i < $num_incomplete; $i++) {
+ next($this->results);
+ }
+ }
+
+ if ($this->results) {
+ $this->chunkIndex++;
+
+ // let the system know we've jumped past the nulls
+ $this->resultIndex = $num_incomplete;
+
+ $this->retrievedResults += ($num_results + $num_incomplete);
+ if ($num_results == 0) {
+ // This fetch was *all* incompletes! We need to fetch until we can either
+ // offer at least one row to iterate over, or give up.
+ return $this->getNextResultsChunk();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Increment the offset from the original options array? Setting to
+ * false is required for callbacks that delete rows.
+ *
+ * @param bool $increment Set to false when deleting data
+ * @return void
+ */
+ public function setIncrementOffset($increment = true) {
+ $this->incrementOffset = (bool) $increment;
+ }
+
+ /**
+ * Implements Iterator
+ */
+
+ /**
+ * PHP Iterator Interface
+ *
+ * @see Iterator::rewind()
+ * @return void
+ */
+ public function rewind() {
+ $this->resultIndex = 0;
+ $this->retrievedResults = 0;
+ $this->processedResults = 0;
+
+ // only grab results if we haven't yet or we're crossing chunks
+ if ($this->chunkIndex == 0 || $this->limit > $this->chunkSize) {
+ $this->chunkIndex = 0;
+ $this->getNextResultsChunk();
+ }
+ }
+
+ /**
+ * PHP Iterator Interface
+ *
+ * @see Iterator::current()
+ * @return mixed
+ */
+ public function current() {
+ return current($this->results);
+ }
+
+ /**
+ * PHP Iterator Interface
+ *
+ * @see Iterator::key()
+ * @return int
+ */
+ public function key() {
+ return $this->processedResults;
+ }
+
+ /**
+ * PHP Iterator Interface
+ *
+ * @see Iterator::next()
+ * @return mixed
+ */
+ public function next() {
+ // if we'll be at the end.
+ if (($this->processedResults + 1) >= $this->limit && $this->limit > 0) {
+ $this->results = array();
+ return false;
+ }
+
+ // if we'll need new results.
+ if (($this->resultIndex + 1) >= $this->chunkSize) {
+ if (!$this->getNextResultsChunk()) {
+ $this->results = array();
+ return false;
+ }
+
+ $result = current($this->results);
+ } else {
+ // the function above resets the indexes, so only inc if not
+ // getting new set
+ $this->resultIndex++;
+ $result = next($this->results);
+ }
+
+ $this->processedResults++;
+ return $result;
+ }
+
+ /**
+ * PHP Iterator Interface
+ *
+ * @see Iterator::valid()
+ * @return bool
+ */
+ public function valid() {
+ if (!is_array($this->results)) {
+ return false;
+ }
+ $key = key($this->results);
+ return ($key !== NULL && $key !== FALSE);
+ }
+}
diff --git a/engine/classes/ElggCache.php b/engine/classes/ElggCache.php
new file mode 100644
index 000000000..909eab39b
--- /dev/null
+++ b/engine/classes/ElggCache.php
@@ -0,0 +1,247 @@
+<?php
+/**
+ * ElggCache The elgg cache superclass.
+ * This defines the interface for a cache (wherever that cache is stored).
+ *
+ * @package Elgg.Core
+ * @subpackage Cache
+ */
+abstract class ElggCache implements ArrayAccess {
+ /**
+ * Variables for the cache object.
+ *
+ * @var array
+ */
+ private $variables;
+
+ /**
+ * Set the constructor.
+ */
+ function __construct() {
+ $this->variables = array();
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Set a cache variable.
+ *
+ * @param string $variable Name
+ * @param string $value Value
+ *
+ * @return void
+ *
+ * @deprecated 1.8 Use ElggCache:setVariable()
+ */
+ public function set_variable($variable, $value) {
+ elgg_deprecated_notice('ElggCache::set_variable() is deprecated by ElggCache::setVariable()', 1.8);
+ $this->setVariable($variable, $value);
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * Set a cache variable.
+ *
+ * @param string $variable Name
+ * @param string $value Value
+ *
+ * @return void
+ */
+ public function setVariable($variable, $value) {
+ if (!is_array($this->variables)) {
+ $this->variables = array();
+ }
+
+ $this->variables[$variable] = $value;
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Get variables for this cache.
+ *
+ * @param string $variable Name
+ *
+ * @return mixed The value or null;
+ *
+ * @deprecated 1.8 Use ElggCache::getVariable()
+ */
+ public function get_variable($variable) {
+ elgg_deprecated_notice('ElggCache::get_variable() is deprecated by ElggCache::getVariable()', 1.8);
+ return $this->getVariable($variable);
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * Get variables for this cache.
+ *
+ * @param string $variable Name
+ *
+ * @return mixed The variable or null;
+ */
+ public function getVariable($variable) {
+ if (isset($this->variables[$variable])) {
+ return $this->variables[$variable];
+ }
+
+ return null;
+ }
+
+ /**
+ * Class member get overloading, returning key using $this->load defaults.
+ *
+ * @param string $key Name
+ *
+ * @return mixed
+ */
+ function __get($key) {
+ return $this->load($key);
+ }
+
+ /**
+ * Class member set overloading, setting a key using $this->save defaults.
+ *
+ * @param string $key Name
+ * @param mixed $value Value
+ *
+ * @return mixed
+ */
+ function __set($key, $value) {
+ return $this->save($key, $value);
+ }
+
+ /**
+ * Supporting isset, using $this->load() with default values.
+ *
+ * @param string $key The name of the attribute or metadata.
+ *
+ * @return bool
+ */
+ function __isset($key) {
+ return (bool)$this->load($key);
+ }
+
+ /**
+ * Supporting unsetting of magic attributes.
+ *
+ * @param string $key The name of the attribute or metadata.
+ *
+ * @return bool
+ */
+ function __unset($key) {
+ return $this->delete($key);
+ }
+
+ /**
+ * Save data in a cache.
+ *
+ * @param string $key Name
+ * @param string $data Value
+ *
+ * @return bool
+ */
+ abstract public function save($key, $data);
+
+ /**
+ * Load data from the cache using a given key.
+ *
+ * @todo $offset is a horrible variable name because it creates confusion
+ * with the ArrayAccess methods
+ *
+ * @param string $key Name
+ * @param int $offset Offset
+ * @param int $limit Limit
+ *
+ * @return mixed The stored data or false.
+ */
+ abstract public function load($key, $offset = 0, $limit = null);
+
+ /**
+ * Invalidate a key
+ *
+ * @param string $key Name
+ *
+ * @return bool
+ */
+ abstract public function delete($key);
+
+ /**
+ * Clear out all the contents of the cache.
+ *
+ * @return bool
+ */
+ abstract public function clear();
+
+ /**
+ * Add a key only if it doesn't already exist.
+ * Implemented simply here, if you extend this class and your caching engine
+ * provides a better way then override this accordingly.
+ *
+ * @param string $key Name
+ * @param string $data Value
+ *
+ * @return bool
+ */
+ public function add($key, $data) {
+ if (!isset($this[$key])) {
+ return $this->save($key, $data);
+ }
+
+ return false;
+ }
+
+ // ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
+
+ /**
+ * Assigns a value for the specified key
+ *
+ * @see ArrayAccess::offsetSet()
+ *
+ * @param mixed $key The key (offset) to assign the value to.
+ * @param mixed $value The value to set.
+ *
+ * @return void
+ */
+ function offsetSet($key, $value) {
+ $this->save($key, $value);
+ }
+
+ /**
+ * Get the value for specified key
+ *
+ * @see ArrayAccess::offsetGet()
+ *
+ * @param mixed $key The key (offset) to retrieve.
+ *
+ * @return mixed
+ */
+ function offsetGet($key) {
+ return $this->load($key);
+ }
+
+ /**
+ * Unsets a key.
+ *
+ * @see ArrayAccess::offsetUnset()
+ *
+ * @param mixed $key The key (offset) to unset.
+ *
+ * @return void
+ */
+ function offsetUnset($key) {
+ if (isset($this->$key)) {
+ unset($this->$key);
+ }
+ }
+
+ /**
+ * Does key exist
+ *
+ * @see ArrayAccess::offsetExists()
+ *
+ * @param mixed $key A key (offset) to check for.
+ *
+ * @return bool
+ */
+ function offsetExists($key) {
+ return isset($this->$key);
+ }
+}
diff --git a/engine/classes/ElggCrypto.php b/engine/classes/ElggCrypto.php
new file mode 100644
index 000000000..317d371e4
--- /dev/null
+++ b/engine/classes/ElggCrypto.php
@@ -0,0 +1,208 @@
+<?php
+/**
+ * ElggCrypto
+ *
+ * @package Elgg.Core
+ * @subpackage Crypto
+ *
+ * @access private
+ */
+class ElggCrypto {
+
+ /**
+ * Character set for temp passwords (no risk of embedded profanity/glyphs that look similar)
+ */
+ const CHARS_PASSWORD = 'bcdfghjklmnpqrstvwxyz2346789';
+
+ /**
+ * Generate a string of highly randomized bytes (over the full 8-bit range).
+ *
+ * @param int $length Number of bytes needed
+ * @return string Random bytes
+ *
+ * @author George Argyros <argyros.george@gmail.com>
+ * @copyright 2012, George Argyros. All rights reserved.
+ * @license Modified BSD
+ * @link https://github.com/GeorgeArgyros/Secure-random-bytes-in-PHP/blob/master/srand.php Original
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the <organization> nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL GEORGE ARGYROS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ public function getRandomBytes($length) {
+ /**
+ * Our primary choice for a cryptographic strong randomness function is
+ * openssl_random_pseudo_bytes.
+ */
+ $SSLstr = '4'; // http://xkcd.com/221/
+ if (function_exists('openssl_random_pseudo_bytes')
+ && (version_compare(PHP_VERSION, '5.3.4') >= 0 || substr(PHP_OS, 0, 3) !== 'WIN')) {
+ $SSLstr = openssl_random_pseudo_bytes($length, $strong);
+ if ($strong) {
+ return $SSLstr;
+ }
+ }
+
+ /**
+ * If mcrypt extension is available then we use it to gather entropy from
+ * the operating system's PRNG. This is better than reading /dev/urandom
+ * directly since it avoids reading larger blocks of data than needed.
+ * Older versions of mcrypt_create_iv may be broken or take too much time
+ * to finish so we only use this function with PHP 5.3.7 and above.
+ * @see https://bugs.php.net/bug.php?id=55169
+ */
+ if (function_exists('mcrypt_create_iv')
+ && (version_compare(PHP_VERSION, '5.3.7') >= 0 || substr(PHP_OS, 0, 3) !== 'WIN')) {
+ $str = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
+ if ($str !== false) {
+ return $str;
+ }
+ }
+
+ /**
+ * No build-in crypto randomness function found. We collect any entropy
+ * available in the PHP core PRNGs along with some filesystem info and memory
+ * stats. To make this data cryptographically strong we add data either from
+ * /dev/urandom or if its unavailable, we gather entropy by measuring the
+ * time needed to compute a number of SHA-1 hashes.
+ */
+ $str = '';
+ $bits_per_round = 2; // bits of entropy collected in each clock drift round
+ $msec_per_round = 400; // expected running time of each round in microseconds
+ $hash_len = 20; // SHA-1 Hash length
+ $total = $length; // total bytes of entropy to collect
+
+ $handle = @fopen('/dev/urandom', 'rb');
+ if ($handle && function_exists('stream_set_read_buffer')) {
+ @stream_set_read_buffer($handle, 0);
+ }
+
+ do {
+ $bytes = ($total > $hash_len) ? $hash_len : $total;
+ $total -= $bytes;
+
+ //collect any entropy available from the PHP system and filesystem
+ $entropy = rand() . uniqid(mt_rand(), true) . $SSLstr;
+ $entropy .= implode('', @fstat(@fopen(__FILE__, 'r')));
+ $entropy .= memory_get_usage() . getmypid();
+ $entropy .= serialize($_ENV) . serialize($_SERVER);
+ if (function_exists('posix_times')) {
+ $entropy .= serialize(posix_times());
+ }
+ if (function_exists('zend_thread_id')) {
+ $entropy .= zend_thread_id();
+ }
+
+ if ($handle) {
+ $entropy .= @fread($handle, $bytes);
+ } else {
+ // Measure the time that the operations will take on average
+ for ($i = 0; $i < 3; $i++) {
+ $c1 = microtime(true);
+ $var = sha1(mt_rand());
+ for ($j = 0; $j < 50; $j++) {
+ $var = sha1($var);
+ }
+ $c2 = microtime(true);
+ $entropy .= $c1 . $c2;
+ }
+
+ // Based on the above measurement determine the total rounds
+ // in order to bound the total running time.
+ $rounds = (int) ($msec_per_round * 50 / (int) (($c2 - $c1) * 1000000));
+
+ // Take the additional measurements. On average we can expect
+ // at least $bits_per_round bits of entropy from each measurement.
+ $iter = $bytes * (int) (ceil(8 / $bits_per_round));
+
+ for ($i = 0; $i < $iter; $i++) {
+ $c1 = microtime();
+ $var = sha1(mt_rand());
+ for ($j = 0; $j < $rounds; $j++) {
+ $var = sha1($var);
+ }
+ $c2 = microtime();
+ $entropy .= $c1 . $c2;
+ }
+ }
+
+ // We assume sha1 is a deterministic extractor for the $entropy variable.
+ $str .= sha1($entropy, true);
+
+ } while ($length > strlen($str));
+
+ if ($handle) {
+ @fclose($handle);
+ }
+
+ return substr($str, 0, $length);
+ }
+
+ /**
+ * Generate a random string of specified length.
+ *
+ * Uses supplied character list for generating the new string.
+ * If no character list provided - uses Base64 URL character set.
+ *
+ * @param int $length Desired length of the string
+ * @param string|null $chars Characters to be chosen from randomly. If not given, the Base64 URL
+ * charset will be used.
+ *
+ * @return string The random string
+ *
+ * @throws InvalidArgumentException
+ *
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ *
+ * @see https://github.com/zendframework/zf2/blob/master/library/Zend/Math/Rand.php#L179
+ */
+ public static function getRandomString($length, $chars = null) {
+ if ($length < 1) {
+ throw new InvalidArgumentException('Length should be >= 1');
+ }
+
+ if (empty($chars)) {
+ $numBytes = ceil($length * 0.75);
+ $bytes = self::getRandomBytes($numBytes);
+ $string = substr(rtrim(base64_encode($bytes), '='), 0, $length);
+
+ // Base64 URL
+ return strtr($string, '+/', '-_');
+ }
+
+ $listLen = strlen($chars);
+
+ if ($listLen == 1) {
+ return str_repeat($chars, $length);
+ }
+
+ $bytes = self::getRandomBytes($length);
+ $pos = 0;
+ $result = '';
+ for ($i = 0; $i < $length; $i++) {
+ $pos = ($pos + ord($bytes[$i])) % $listLen;
+ $result .= $chars[$pos];
+ }
+
+ return $result;
+ }
+}
diff --git a/engine/classes/ElggData.php b/engine/classes/ElggData.php
new file mode 100644
index 000000000..4f843cde4
--- /dev/null
+++ b/engine/classes/ElggData.php
@@ -0,0 +1,309 @@
+<?php
+/**
+ * A generic class that contains shared code b/w
+ * ElggExtender, ElggEntity, and ElggRelationship
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel
+ *
+ * @property int $owner_guid
+ * @property int $time_created
+ */
+abstract class ElggData implements
+ Loggable, // Can events related to this object class be logged
+ Iterator, // Override foreach behaviour
+ ArrayAccess, // Override for array access
+ Exportable
+{
+
+ /**
+ * The main attributes of an entity.
+ * Holds attributes to save to database
+ * This contains the site's main properties (id, etc)
+ * Blank entries for all database fields should be created by the constructor.
+ * Subclasses should add to this in their constructors.
+ * Any field not appearing in this will be viewed as a
+ */
+ protected $attributes = array();
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Initialise the attributes array.
+ *
+ * This is vital to distinguish between metadata and base parameters.
+ *
+ * @param bool $pre18_api Compatibility for subclassing in 1.7 -> 1.8 change.
+ * Passing true (default) emits a deprecation notice.
+ * Passing false returns false. Core constructors always pass false.
+ * Does nothing either way since attributes are initialized by the time
+ * this is called.
+ * @return void
+ * @deprecated 1.8 Use initializeAttributes()
+ */
+ protected function initialise_attributes($pre18_api = true) {
+ if ($pre18_api) {
+ elgg_deprecated_notice('initialise_attributes() is deprecated by initializeAttributes()', 1.8);
+ }
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * Initialize the attributes array.
+ *
+ * This is vital to distinguish between metadata and base parameters.
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ // Create attributes array if not already created
+ if (!is_array($this->attributes)) {
+ $this->attributes = array();
+ }
+
+ $this->attributes['time_created'] = NULL;
+ }
+
+ /**
+ * Return an attribute or a piece of metadata.
+ *
+ * @param string $name Name
+ *
+ * @return mixed
+ */
+ public function __get($name) {
+ return $this->get($name);
+ }
+
+ /**
+ * Set an attribute or a piece of metadata.
+ *
+ * @param string $name Name
+ * @param mixed $value Value
+ *
+ * @return mixed
+ */
+ public function __set($name, $value) {
+ return $this->set($name, $value);
+ }
+
+ /**
+ * Test if property is set either as an attribute or metadata.
+ *
+ * @tip Use isset($entity->property)
+ *
+ * @param string $name The name of the attribute or metadata.
+ *
+ * @return bool
+ */
+ function __isset($name) {
+ return $this->$name !== NULL;
+ }
+
+ /**
+ * Fetch the specified attribute
+ *
+ * @param string $name The attribute to fetch
+ *
+ * @return mixed The attribute, if it exists. Otherwise, null.
+ */
+ abstract protected function get($name);
+
+ /**
+ * Set the specified attribute
+ *
+ * @param string $name The attribute to set
+ * @param mixed $value The value to set it to
+ *
+ * @return bool The success of your set function?
+ */
+ abstract protected function set($name, $value);
+
+ /**
+ * Get a URL for this object
+ *
+ * @return string
+ */
+ abstract public function getURL();
+
+ /**
+ * Save this data to the appropriate database table.
+ *
+ * @return bool
+ */
+ abstract public function save();
+
+ /**
+ * Delete this data.
+ *
+ * @return bool
+ */
+ abstract public function delete();
+
+ /**
+ * Returns the UNIX epoch time that this entity was created
+ *
+ * @return int UNIX epoch time
+ */
+ public function getTimeCreated() {
+ return $this->time_created;
+ }
+
+ /*
+ * SYSTEM LOG INTERFACE
+ */
+
+ /**
+ * Return the class name of the object.
+ *
+ * @return string
+ */
+ public function getClassName() {
+ return get_class($this);
+ }
+
+ /**
+ * Return the GUID of the owner of this object.
+ *
+ * @return int
+ * @deprecated 1.8 Use getOwnerGUID() instead
+ */
+ public function getObjectOwnerGUID() {
+ elgg_deprecated_notice("getObjectOwnerGUID() was deprecated. Use getOwnerGUID().", 1.8);
+ return $this->owner_guid;
+ }
+
+ /*
+ * ITERATOR INTERFACE
+ */
+
+ /*
+ * This lets an entity's attributes be displayed using foreach as a normal array.
+ * Example: http://www.sitepoint.com/print/php5-standard-library
+ */
+ protected $valid = FALSE;
+
+ /**
+ * Iterator interface
+ *
+ * @see Iterator::rewind()
+ *
+ * @return void
+ */
+ public function rewind() {
+ $this->valid = (FALSE !== reset($this->attributes));
+ }
+
+ /**
+ * Iterator interface
+ *
+ * @see Iterator::current()
+ *
+ * @return mixed
+ */
+ public function current() {
+ return current($this->attributes);
+ }
+
+ /**
+ * Iterator interface
+ *
+ * @see Iterator::key()
+ *
+ * @return string
+ */
+ public function key() {
+ return key($this->attributes);
+ }
+
+ /**
+ * Iterator interface
+ *
+ * @see Iterator::next()
+ *
+ * @return void
+ */
+ public function next() {
+ $this->valid = (FALSE !== next($this->attributes));
+ }
+
+ /**
+ * Iterator interface
+ *
+ * @see Iterator::valid()
+ *
+ * @return bool
+ */
+ public function valid() {
+ return $this->valid;
+ }
+
+ /*
+ * ARRAY ACCESS INTERFACE
+ */
+
+ /*
+ * This lets an entity's attributes be accessed like an associative array.
+ * Example: http://www.sitepoint.com/print/php5-standard-library
+ */
+
+ /**
+ * Array access interface
+ *
+ * @see ArrayAccess::offsetSet()
+ *
+ * @param mixed $key Name
+ * @param mixed $value Value
+ *
+ * @return void
+ */
+ public function offsetSet($key, $value) {
+ if (array_key_exists($key, $this->attributes)) {
+ $this->attributes[$key] = $value;
+ }
+ }
+
+ /**
+ * Array access interface
+ *
+ * @see ArrayAccess::offsetGet()
+ *
+ * @param mixed $key Name
+ *
+ * @return mixed
+ */
+ public function offsetGet($key) {
+ if (array_key_exists($key, $this->attributes)) {
+ return $this->attributes[$key];
+ }
+ return null;
+ }
+
+ /**
+ * Array access interface
+ *
+ * @see ArrayAccess::offsetUnset()
+ *
+ * @param mixed $key Name
+ *
+ * @return void
+ */
+ public function offsetUnset($key) {
+ if (array_key_exists($key, $this->attributes)) {
+ // Full unsetting is dangerous for our objects
+ $this->attributes[$key] = "";
+ }
+ }
+
+ /**
+ * Array access interface
+ *
+ * @see ArrayAccess::offsetExists()
+ *
+ * @param int $offset Offset
+ *
+ * @return int
+ */
+ public function offsetExists($offset) {
+ return array_key_exists($offset, $this->attributes);
+ }
+}
diff --git a/engine/classes/ElggDiskFilestore.php b/engine/classes/ElggDiskFilestore.php
new file mode 100644
index 000000000..6e2354012
--- /dev/null
+++ b/engine/classes/ElggDiskFilestore.php
@@ -0,0 +1,417 @@
+<?php
+/**
+ * A filestore that uses disk as storage.
+ *
+ * @warning This should be used by a wrapper class
+ * like {@link ElggFile}.
+ *
+ * @package Elgg.Core
+ * @subpackage FileStore.Disk
+ * @link http://docs.elgg.org/DataModel/FileStore/Disk
+ */
+class ElggDiskFilestore extends ElggFilestore {
+ /**
+ * Directory root.
+ */
+ private $dir_root;
+
+ /**
+ * Default depth of file directory matrix
+ */
+ private $matrix_depth = 5;
+
+ /**
+ * Construct a disk filestore using the given directory root.
+ *
+ * @param string $directory_root Root directory, must end in "/"
+ */
+ public function __construct($directory_root = "") {
+ global $CONFIG;
+
+ if ($directory_root) {
+ $this->dir_root = $directory_root;
+ } else {
+ $this->dir_root = $CONFIG->dataroot;
+ }
+ }
+
+ /**
+ * Open a file for reading, writing, or both.
+ *
+ * @note All files are opened binary safe.
+ * @warning This will try to create the a directory if it doesn't exist,
+ * even in read-only mode.
+ *
+ * @param ElggFile $file The file to open
+ * @param string $mode read, write, or append.
+ *
+ * @throws InvalidParameterException
+ * @return resource File pointer resource
+ * @todo This really shouldn't try to create directories if not writing.
+ */
+ public function open(ElggFile $file, $mode) {
+ $fullname = $this->getFilenameOnFilestore($file);
+
+ // Split into path and name
+ $ls = strrpos($fullname, "/");
+ if ($ls === false) {
+ $ls = 0;
+ }
+
+ $path = substr($fullname, 0, $ls);
+ $name = substr($fullname, $ls);
+ // @todo $name is unused, remove it or do we need to fix something?
+
+ // Try and create the directory
+ try {
+ $this->makeDirectoryRoot($path);
+ } catch (Exception $e) {
+
+ }
+
+ if (($mode != 'write') && (!file_exists($fullname))) {
+ return false;
+ }
+
+ switch ($mode) {
+ case "read" :
+ $mode = "rb";
+ break;
+ case "write" :
+ $mode = "w+b";
+ break;
+ case "append" :
+ $mode = "a+b";
+ break;
+ default:
+ $msg = elgg_echo('InvalidParameterException:UnrecognisedFileMode', array($mode));
+ throw new InvalidParameterException($msg);
+ }
+
+ return fopen($fullname, $mode);
+
+ }
+
+ /**
+ * Write data to a file.
+ *
+ * @param resource $f File pointer resource
+ * @param mixed $data The data to write.
+ *
+ * @return bool
+ */
+ public function write($f, $data) {
+ return fwrite($f, $data);
+ }
+
+ /**
+ * Read data from a file.
+ *
+ * @param resource $f File pointer resource
+ * @param int $length The number of bytes to read
+ * @param int $offset The number of bytes to start after
+ *
+ * @return mixed Contents of file or false on fail.
+ */
+ public function read($f, $length, $offset = 0) {
+ if ($offset) {
+ $this->seek($f, $offset);
+ }
+
+ return fread($f, $length);
+ }
+
+ /**
+ * Close a file pointer
+ *
+ * @param resource $f A file pointer resource
+ *
+ * @return bool
+ */
+ public function close($f) {
+ return fclose($f);
+ }
+
+ /**
+ * Delete an ElggFile file.
+ *
+ * @param ElggFile $file File to delete
+ *
+ * @return bool
+ */
+ public function delete(ElggFile $file) {
+ $filename = $this->getFilenameOnFilestore($file);
+ if (file_exists($filename)) {
+ return unlink($filename);
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Seek to the specified position.
+ *
+ * @param resource $f File resource
+ * @param int $position Position in bytes
+ *
+ * @return bool
+ */
+ public function seek($f, $position) {
+ return fseek($f, $position);
+ }
+
+ /**
+ * Return the current location of the internal pointer
+ *
+ * @param resource $f File pointer resource
+ *
+ * @return int|false
+ */
+ public function tell($f) {
+ return ftell($f);
+ }
+
+ /**
+ * Tests for end of file on a file pointer
+ *
+ * @param resource $f File pointer resource
+ *
+ * @return bool
+ */
+ public function eof($f) {
+ return feof($f);
+ }
+
+ /**
+ * Returns the file size of an ElggFile file.
+ *
+ * @param ElggFile $file File object
+ *
+ * @return int The file size
+ */
+ public function getFileSize(ElggFile $file) {
+ return filesize($this->getFilenameOnFilestore($file));
+ }
+
+ /**
+ * Get the filename as saved on disk for an ElggFile object
+ *
+ * Returns an empty string if no filename set
+ *
+ * @param ElggFile $file File object
+ *
+ * @return string The full path of where the file is stored
+ * @throws InvalidParameterException
+ */
+ public function getFilenameOnFilestore(ElggFile $file) {
+ $owner_guid = $file->getOwnerGuid();
+ if (!$owner_guid) {
+ $owner_guid = elgg_get_logged_in_user_guid();
+ }
+
+ if (!$owner_guid) {
+ $msg = elgg_echo('InvalidParameterException:MissingOwner',
+ array($file->getFilename(), $file->guid));
+ throw new InvalidParameterException($msg);
+ }
+
+ $filename = $file->getFilename();
+ if (!$filename) {
+ return '';
+ }
+
+ return $this->dir_root . $this->makeFileMatrix($owner_guid) . $filename;
+ }
+
+ /**
+ * Returns the contents of the ElggFile file.
+ *
+ * @param ElggFile $file File object
+ *
+ * @return string
+ */
+ public function grabFile(ElggFile $file) {
+ return file_get_contents($file->getFilenameOnFilestore());
+ }
+
+ /**
+ * Tests if an ElggFile file exists.
+ *
+ * @param ElggFile $file File object
+ *
+ * @return bool
+ */
+ public function exists(ElggFile $file) {
+ if (!$file->getFilename()) {
+ return false;
+ }
+ return file_exists($this->getFilenameOnFilestore($file));
+ }
+
+ /**
+ * Returns the size of all data stored under a directory in the disk store.
+ *
+ * @param string $prefix Optional/ The prefix to check under.
+ * @param string $container_guid The guid of the entity whose data you want to check.
+ *
+ * @return int|false
+ */
+ public function getSize($prefix = '', $container_guid) {
+ if ($container_guid) {
+ return get_dir_size($this->dir_root . $this->makeFileMatrix($container_guid) . $prefix);
+ } else {
+ return false;
+ }
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Create a directory $dirroot
+ *
+ * @param string $dirroot The full path of the directory to create
+ *
+ * @throws IOException
+ * @return true
+ * @deprecated 1.8 Use ElggDiskFilestore::makeDirectoryRoot()
+ */
+ protected function make_directory_root($dirroot) {
+ elgg_deprecated_notice('ElggDiskFilestore::make_directory_root() is deprecated by ::makeDirectoryRoot()', 1.8);
+
+ return $this->makeDirectoryRoot($dirroot);
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * Create a directory $dirroot
+ *
+ * @param string $dirroot The full path of the directory to create
+ *
+ * @throws IOException
+ * @return true
+ */
+ protected function makeDirectoryRoot($dirroot) {
+ if (!file_exists($dirroot)) {
+ if (!@mkdir($dirroot, 0700, true)) {
+ throw new IOException(elgg_echo('IOException:CouldNotMake', array($dirroot)));
+ }
+ }
+
+ return true;
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Multibyte string tokeniser.
+ *
+ * Splits a string into an array. Will fail safely if mbstring is
+ * not installed.
+ *
+ * @param string $string String
+ * @param string $charset The charset, defaults to UTF8
+ *
+ * @return array
+ * @deprecated 1.8 Files are stored by date and guid; no need for this.
+ */
+ private function mb_str_split($string, $charset = 'UTF8') {
+ elgg_deprecated_notice('ElggDiskFilestore::mb_str_split() is deprecated.', 1.8);
+
+ if (is_callable('mb_substr')) {
+ $length = mb_strlen($string);
+ $array = array();
+
+ while ($length) {
+ $array[] = mb_substr($string, 0, 1, $charset);
+ $string = mb_substr($string, 1, $length, $charset);
+
+ $length = mb_strlen($string);
+ }
+
+ return $array;
+ } else {
+ return str_split($string);
+ }
+ }
+ // @codingStandardsIgnoreEnd
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Construct a file path matrix for an entity.
+ *
+ * @param int $identifier The guide of the entity to store the data under.
+ *
+ * @return string The path where the entity's data will be stored.
+ * @deprecated 1.8 Use ElggDiskFilestore::makeFileMatrix()
+ */
+ protected function make_file_matrix($identifier) {
+ elgg_deprecated_notice('ElggDiskFilestore::make_file_matrix() is deprecated by ::makeFileMatrix()', 1.8);
+
+ return $this->makeFileMatrix($identifier);
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * Construct a file path matrix for an entity.
+ *
+ * @param int $guid The guide of the entity to store the data under.
+ *
+ * @return string The path where the entity's data will be stored.
+ */
+ protected function makeFileMatrix($guid) {
+ $entity = get_entity($guid);
+
+ if (!($entity instanceof ElggEntity) || !$entity->time_created) {
+ return false;
+ }
+
+ $time_created = date('Y/m/d', $entity->time_created);
+
+ return "$time_created/$entity->guid/";
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Construct a filename matrix.
+ *
+ * Generates a matrix using the entity's creation time and
+ * unique guid.
+ *
+ * File path matrixes are:
+ * YYYY/MM/DD/guid/
+ *
+ * @param int $guid The entity to contrust a matrix for
+ *
+ * @return string The
+ */
+ protected function user_file_matrix($guid) {
+ elgg_deprecated_notice('ElggDiskFilestore::user_file_matrix() is deprecated by ::makeFileMatrix()', 1.8);
+
+ return $this->makeFileMatrix($guid);
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * Returns a list of attributes to save to the database when saving
+ * the ElggFile object using this file store.
+ *
+ * @return array
+ */
+ public function getParameters() {
+ return array("dir_root" => $this->dir_root);
+ }
+
+ /**
+ * Sets parameters that should be saved to database.
+ *
+ * @param array $parameters Set parameters to save to DB for this filestore.
+ *
+ * @return bool
+ */
+ public function setParameters(array $parameters) {
+ if (isset($parameters['dir_root'])) {
+ $this->dir_root = $parameters['dir_root'];
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php
new file mode 100644
index 000000000..a563f6fad
--- /dev/null
+++ b/engine/classes/ElggEntity.php
@@ -0,0 +1,1770 @@
+<?php
+/**
+ * The parent class for all Elgg Entities.
+ *
+ * An ElggEntity is one of the basic data models in Elgg. It is the primary
+ * means of storing and retrieving data from the database. An ElggEntity
+ * represents one row of the entities table.
+ *
+ * The ElggEntity class handles CRUD operations for the entities table.
+ * ElggEntity should always be extended by another class to handle CRUD
+ * operations on the type-specific table.
+ *
+ * ElggEntity uses magic methods for get and set, so any property that isn't
+ * declared will be assumed to be metadata and written to the database
+ * as metadata on the object. All children classes must declare which
+ * properties are columns of the type table or they will be assumed
+ * to be metadata. See ElggObject::initialise_entities() for examples.
+ *
+ * Core supports 4 types of entities: ElggObject, ElggUser, ElggGroup, and
+ * ElggSite.
+ *
+ * @tip Most plugin authors will want to extend the ElggObject class
+ * instead of this class.
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel.Entities
+ *
+ * @property string $type object, user, group, or site (read-only after save)
+ * @property string $subtype Further clarifies the nature of the entity (read-only after save)
+ * @property int $guid The unique identifier for this entity (read only)
+ * @property int $owner_guid The GUID of the creator of this entity
+ * @property int $container_guid The GUID of the entity containing this entity
+ * @property int $site_guid The GUID of the website this entity is associated with
+ * @property int $access_id Specifies the visibility level of this entity
+ * @property int $time_created A UNIX timestamp of when the entity was created (read-only, set on first save)
+ * @property int $time_updated A UNIX timestamp of when the entity was last updated (automatically updated on save)
+ * @property-read string $enabled
+ */
+abstract class ElggEntity extends ElggData implements
+ Notable, // Calendar interface
+ Locatable, // Geocoding interface
+ Importable // Allow import of data
+{
+
+ /**
+ * If set, overrides the value of getURL()
+ */
+ protected $url_override;
+
+ /**
+ * Icon override, overrides the value of getIcon().
+ */
+ protected $icon_override;
+
+ /**
+ * Holds metadata until entity is saved. Once the entity is saved,
+ * metadata are written immediately to the database.
+ */
+ protected $temp_metadata = array();
+
+ /**
+ * Holds annotations until entity is saved. Once the entity is saved,
+ * annotations are written immediately to the database.
+ */
+ protected $temp_annotations = array();
+
+ /**
+ * Holds private settings until entity is saved. Once the entity is saved,
+ * private settings are written immediately to the database.
+ */
+ protected $temp_private_settings = array();
+
+ /**
+ * Volatile data structure for this object, allows for storage of data
+ * in-memory that isn't sync'd back to the metadata table.
+ */
+ protected $volatile = array();
+
+ /**
+ * Initialize the attributes array.
+ *
+ * This is vital to distinguish between metadata and base parameters.
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['guid'] = NULL;
+ $this->attributes['type'] = NULL;
+ $this->attributes['subtype'] = NULL;
+
+ $this->attributes['owner_guid'] = elgg_get_logged_in_user_guid();
+ $this->attributes['container_guid'] = elgg_get_logged_in_user_guid();
+
+ $this->attributes['site_guid'] = NULL;
+ $this->attributes['access_id'] = ACCESS_PRIVATE;
+ $this->attributes['time_created'] = NULL;
+ $this->attributes['time_updated'] = NULL;
+ $this->attributes['last_action'] = NULL;
+ $this->attributes['enabled'] = "yes";
+
+ // There now follows a bit of a hack
+ /* Problem: To speed things up, some objects are split over several tables,
+ * this means that it requires n number of database reads to fully populate
+ * an entity. This causes problems for caching and create events
+ * since it is not possible to tell whether a subclassed entity is complete.
+ *
+ * Solution: We have two counters, one 'tables_split' which tells whatever is
+ * interested how many tables are going to need to be searched in order to fully
+ * populate this object, and 'tables_loaded' which is how many have been
+ * loaded thus far.
+ *
+ * If the two are the same then this object is complete.
+ *
+ * Use: isFullyLoaded() to check
+ */
+ $this->attributes['tables_split'] = 1;
+ $this->attributes['tables_loaded'] = 0;
+ }
+
+ /**
+ * Clone an entity
+ *
+ * Resets the guid so that the entity can be saved as a distinct entity from
+ * the original. Creation time will be set when this new entity is saved.
+ * The owner and container guids come from the original entity. The clone
+ * method copies metadata but does not copy annotations or private settings.
+ *
+ * @note metadata will have its owner and access id set when the entity is saved
+ * and it will be the same as that of the entity.
+ *
+ * @return void
+ */
+ public function __clone() {
+ $orig_entity = get_entity($this->guid);
+ if (!$orig_entity) {
+ elgg_log("Failed to clone entity with GUID $this->guid", "ERROR");
+ return;
+ }
+
+ $metadata_array = elgg_get_metadata(array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ ));
+
+ $this->attributes['guid'] = "";
+
+ $this->attributes['subtype'] = $orig_entity->getSubtype();
+
+ // copy metadata over to new entity - slightly convoluted due to
+ // handling of metadata arrays
+ if (is_array($metadata_array)) {
+ // create list of metadata names
+ $metadata_names = array();
+ foreach ($metadata_array as $metadata) {
+ $metadata_names[] = $metadata['name'];
+ }
+ // arrays are stored with multiple enties per name
+ $metadata_names = array_unique($metadata_names);
+
+ // move the metadata over
+ foreach ($metadata_names as $name) {
+ $this->set($name, $orig_entity->$name);
+ }
+ }
+ }
+
+ /**
+ * Return the value of a property.
+ *
+ * If $name is defined in $this->attributes that value is returned, otherwise it will
+ * pull from the entity's metadata.
+ *
+ * Q: Why are we not using __get overload here?
+ * A: Because overload operators cause problems during subclassing, so we put the code here and
+ * create overloads in subclasses.
+ *
+ * @todo What problems are these?
+ *
+ * @warning Subtype is returned as an id rather than the subtype string. Use getSubtype()
+ * to get the subtype string.
+ *
+ * @param string $name Name
+ *
+ * @return mixed Returns the value of a given value, or null.
+ */
+ public function get($name) {
+ // See if its in our base attributes
+ if (array_key_exists($name, $this->attributes)) {
+ return $this->attributes[$name];
+ }
+
+ // No, so see if its in the meta data for this entity
+ $meta = $this->getMetaData($name);
+
+ // getMetaData returns NULL if $name is not found
+ return $meta;
+ }
+
+ /**
+ * Sets the value of a property.
+ *
+ * If $name is defined in $this->attributes that value is set, otherwise it is
+ * saved as metadata.
+ *
+ * @warning Metadata set this way will inherit the entity's owner and access ID. If you want
+ * to set metadata with a different owner, use create_metadata().
+ *
+ * @warning It is important that your class populates $this->attributes with keys
+ * for all base attributes, anything not in their gets set as METADATA.
+ *
+ * Q: Why are we not using __set overload here?
+ * A: Because overload operators cause problems during subclassing, so we put the code here and
+ * create overloads in subclasses.
+ *
+ * @todo What problems?
+ *
+ * @param string $name Name
+ * @param mixed $value Value
+ *
+ * @return bool
+ */
+ public function set($name, $value) {
+ if (array_key_exists($name, $this->attributes)) {
+ // Certain properties should not be manually changed!
+ switch ($name) {
+ case 'guid':
+ case 'time_updated':
+ case 'last_action':
+ return FALSE;
+ break;
+ default:
+ $this->attributes[$name] = $value;
+ break;
+ }
+ } else {
+ return $this->setMetaData($name, $value);
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Return the value of a piece of metadata.
+ *
+ * @param string $name Name
+ *
+ * @return mixed The value, or NULL if not found.
+ */
+ public function getMetaData($name) {
+ $guid = $this->getGUID();
+
+ if (! $guid) {
+ if (isset($this->temp_metadata[$name])) {
+ // md is returned as an array only if more than 1 entry
+ if (count($this->temp_metadata[$name]) == 1) {
+ return $this->temp_metadata[$name][0];
+ } else {
+ return $this->temp_metadata[$name];
+ }
+ } else {
+ return null;
+ }
+ }
+
+ // upon first cache miss, just load/cache all the metadata and retry.
+ // if this works, the rest of this function may not be needed!
+ $cache = elgg_get_metadata_cache();
+ if ($cache->isKnown($guid, $name)) {
+ return $cache->load($guid, $name);
+ } else {
+ $cache->populateFromEntities(array($guid));
+ // in case ignore_access was on, we have to check again...
+ if ($cache->isKnown($guid, $name)) {
+ return $cache->load($guid, $name);
+ }
+ }
+
+ $md = elgg_get_metadata(array(
+ 'guid' => $guid,
+ 'metadata_name' => $name,
+ 'limit' => 0,
+ ));
+
+ $value = null;
+
+ if ($md && !is_array($md)) {
+ $value = $md->value;
+ } elseif (count($md) == 1) {
+ $value = $md[0]->value;
+ } else if ($md && is_array($md)) {
+ $value = metadata_array_to_values($md);
+ }
+
+ $cache->save($guid, $name, $value);
+
+ return $value;
+ }
+
+ /**
+ * Unset a property from metadata or attribute.
+ *
+ * @warning If you use this to unset an attribute, you must save the object!
+ *
+ * @param string $name The name of the attribute or metadata.
+ *
+ * @return void
+ */
+ function __unset($name) {
+ if (array_key_exists($name, $this->attributes)) {
+ $this->attributes[$name] = "";
+ } else {
+ $this->deleteMetadata($name);
+ }
+ }
+
+ /**
+ * Set a piece of metadata.
+ *
+ * Plugin authors should use the magic methods or create_metadata().
+ *
+ * @warning The metadata will inherit the parent entity's owner and access ID.
+ * If you want to write metadata with a different owner, use create_metadata().
+ *
+ * @access private
+ *
+ * @param string $name Name of the metadata
+ * @param mixed $value Value of the metadata (doesn't support assoc arrays)
+ * @param string $value_type Types supported: integer and string. Will auto-identify if not set
+ * @param bool $multiple Allow multiple values for a single name (doesn't support assoc arrays)
+ *
+ * @return bool
+ */
+ public function setMetaData($name, $value, $value_type = null, $multiple = false) {
+
+ // normalize value to an array that we will loop over
+ // remove indexes if value already an array.
+ if (is_array($value)) {
+ $value = array_values($value);
+ } else {
+ $value = array($value);
+ }
+
+ // saved entity. persist md to db.
+ if ($this->guid) {
+ // if overwriting, delete first.
+ if (!$multiple) {
+ $options = array(
+ 'guid' => $this->getGUID(),
+ 'metadata_name' => $name,
+ 'limit' => 0
+ );
+ // @todo in 1.9 make this return false if can't add metadata
+ // https://github.com/elgg/elgg/issues/4520
+ //
+ // need to remove access restrictions right now to delete
+ // because this is the expected behavior
+ $ia = elgg_set_ignore_access(true);
+ if (false === elgg_delete_metadata($options)) {
+ return false;
+ }
+ elgg_set_ignore_access($ia);
+ }
+
+ // add new md
+ $result = true;
+ foreach ($value as $value_tmp) {
+ // at this point $value should be appended because it was cleared above if needed.
+ $md_id = create_metadata($this->getGUID(), $name, $value_tmp, $value_type,
+ $this->getOwnerGUID(), $this->getAccessId(), true);
+ if (!$md_id) {
+ return false;
+ }
+ }
+
+ return $result;
+ } else {
+ // unsaved entity. store in temp array
+ // returning single entries instead of an array of 1 element is decided in
+ // getMetaData(), just like pulling from the db.
+ //
+ // if overwrite, delete first
+ if (!$multiple || !isset($this->temp_metadata[$name])) {
+ $this->temp_metadata[$name] = array();
+ }
+
+ // add new md
+ $this->temp_metadata[$name] = array_merge($this->temp_metadata[$name], $value);
+ return true;
+ }
+ }
+
+ /**
+ * Deletes all metadata on this object (metadata.entity_guid = $this->guid).
+ * If you pass a name, only metadata matching that name will be deleted.
+ *
+ * @warning Calling this with no $name will clear all metadata on the entity.
+ *
+ * @param null|string $name The name of the metadata to remove.
+ * @return bool
+ * @since 1.8
+ */
+ public function deleteMetadata($name = null) {
+
+ if (!$this->guid) {
+ return false;
+ }
+
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['metadata_name'] = $name;
+ }
+
+ return elgg_delete_metadata($options);
+ }
+
+ /**
+ * Deletes all metadata owned by this object (metadata.owner_guid = $this->guid).
+ * If you pass a name, only metadata matching that name will be deleted.
+ *
+ * @param null|string $name The name of metadata to delete.
+ * @return bool
+ * @since 1.8
+ */
+ public function deleteOwnedMetadata($name = null) {
+ // access is turned off for this because they might
+ // no longer have access to an entity they created metadata on.
+ $ia = elgg_set_ignore_access(true);
+ $options = array(
+ 'metadata_owner_guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['metadata_name'] = $name;
+ }
+
+ $r = elgg_delete_metadata($options);
+ elgg_set_ignore_access($ia);
+ return $r;
+ }
+
+ /**
+ * Remove metadata
+ *
+ * @warning Calling this with no or empty arguments will clear all metadata on the entity.
+ *
+ * @param string $name The name of the metadata to clear
+ * @return mixed bool
+ * @deprecated 1.8 Use deleteMetadata()
+ */
+ public function clearMetaData($name = '') {
+ elgg_deprecated_notice('ElggEntity->clearMetadata() is deprecated by ->deleteMetadata()', 1.8);
+ return $this->deleteMetadata($name);
+ }
+
+ /**
+ * Disables metadata for this entity, optionally based on name.
+ *
+ * @param string $name An options name of metadata to disable.
+ * @return bool
+ * @since 1.8
+ */
+ public function disableMetadata($name = '') {
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['metadata_name'] = $name;
+ }
+
+ return elgg_disable_metadata($options);
+ }
+
+ /**
+ * Enables metadata for this entity, optionally based on name.
+ *
+ * @warning Before calling this, you must use {@link access_show_hidden_entities()}
+ *
+ * @param string $name An options name of metadata to enable.
+ * @return bool
+ * @since 1.8
+ */
+ public function enableMetadata($name = '') {
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['metadata_name'] = $name;
+ }
+
+ return elgg_enable_metadata($options);
+ }
+
+ /**
+ * Get a piece of volatile (non-persisted) data on this entity.
+ *
+ * @param string $name The name of the volatile data
+ *
+ * @return mixed The value or NULL if not found.
+ */
+ public function getVolatileData($name) {
+ if (!is_array($this->volatile)) {
+ $this->volatile = array();
+ }
+
+ if (array_key_exists($name, $this->volatile)) {
+ return $this->volatile[$name];
+ } else {
+ return NULL;
+ }
+ }
+
+ /**
+ * Set a piece of volatile (non-persisted) data on this entity
+ *
+ * @param string $name Name
+ * @param mixed $value Value
+ *
+ * @return void
+ */
+ public function setVolatileData($name, $value) {
+ if (!is_array($this->volatile)) {
+ $this->volatile = array();
+ }
+
+ $this->volatile[$name] = $value;
+ }
+
+ /**
+ * Remove all relationships to and from this entity.
+ *
+ * @return true
+ * @todo This should actually return if it worked.
+ * @see ElggEntity::addRelationship()
+ * @see ElggEntity::removeRelationship()
+ */
+ public function deleteRelationships() {
+ remove_entity_relationships($this->getGUID());
+ remove_entity_relationships($this->getGUID(), "", true);
+ return true;
+ }
+
+ /**
+ * Remove all relationships to and from this entity.
+ *
+ * @return bool
+ * @see ElggEntity::addRelationship()
+ * @see ElggEntity::removeRelationship()
+ * @deprecated 1.8 Use ->deleteRelationship()
+ */
+ public function clearRelationships() {
+ elgg_deprecated_notice('ElggEntity->clearRelationships() is deprecated by ->deleteRelationships()', 1.8);
+ return $this->deleteRelationships();
+ }
+
+ /**
+ * Add a relationship between this an another entity.
+ *
+ * @tip Read the relationship like "$guid is a $relationship of this entity."
+ *
+ * @param int $guid Entity to link to.
+ * @param string $relationship The type of relationship.
+ *
+ * @return bool
+ * @see ElggEntity::removeRelationship()
+ * @see ElggEntity::clearRelationships()
+ */
+ public function addRelationship($guid, $relationship) {
+ return add_entity_relationship($this->getGUID(), $relationship, $guid);
+ }
+
+ /**
+ * Remove a relationship
+ *
+ * @param int $guid GUID of the entity to make a relationship with
+ * @param str $relationship Name of relationship
+ *
+ * @return bool
+ * @see ElggEntity::addRelationship()
+ * @see ElggEntity::clearRelationships()
+ */
+ public function removeRelationship($guid, $relationship) {
+ return remove_entity_relationship($this->getGUID(), $relationship, $guid);
+ }
+
+ /**
+ * Adds a private setting to this entity.
+ *
+ * Private settings are similar to metadata but will not
+ * be searched and there are fewer helper functions for them.
+ *
+ * @param string $name Name of private setting
+ * @param mixed $value Value of private setting
+ *
+ * @return bool
+ */
+ function setPrivateSetting($name, $value) {
+ if ((int) $this->guid > 0) {
+ return set_private_setting($this->getGUID(), $name, $value);
+ } else {
+ $this->temp_private_settings[$name] = $value;
+ return true;
+ }
+ }
+
+ /**
+ * Returns a private setting value
+ *
+ * @param string $name Name of the private setting
+ *
+ * @return mixed
+ */
+ function getPrivateSetting($name) {
+ if ((int) ($this->guid) > 0) {
+ return get_private_setting($this->getGUID(), $name);
+ } else {
+ if (isset($this->temp_private_settings[$name])) {
+ return $this->temp_private_settings[$name];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes private setting
+ *
+ * @param string $name Name of the private setting
+ *
+ * @return bool
+ */
+ function removePrivateSetting($name) {
+ return remove_private_setting($this->getGUID(), $name);
+ }
+
+ /**
+ * Deletes all annotations on this object (annotations.entity_guid = $this->guid).
+ * If you pass a name, only annotations matching that name will be deleted.
+ *
+ * @warning Calling this with no or empty arguments will clear all annotations on the entity.
+ *
+ * @param null|string $name The annotations name to remove.
+ * @return bool
+ * @since 1.8
+ */
+ public function deleteAnnotations($name = null) {
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ return elgg_delete_annotations($options);
+ }
+
+ /**
+ * Deletes all annotations owned by this object (annotations.owner_guid = $this->guid).
+ * If you pass a name, only annotations matching that name will be deleted.
+ *
+ * @param null|string $name The name of annotations to delete.
+ * @return bool
+ * @since 1.8
+ */
+ public function deleteOwnedAnnotations($name = null) {
+ // access is turned off for this because they might
+ // no longer have access to an entity they created annotations on.
+ $ia = elgg_set_ignore_access(true);
+ $options = array(
+ 'annotation_owner_guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ $r = elgg_delete_annotations($options);
+ elgg_set_ignore_access($ia);
+ return $r;
+ }
+
+ /**
+ * Disables annotations for this entity, optionally based on name.
+ *
+ * @param string $name An options name of annotations to disable.
+ * @return bool
+ * @since 1.8
+ */
+ public function disableAnnotations($name = '') {
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ return elgg_disable_annotations($options);
+ }
+
+ /**
+ * Enables annotations for this entity, optionally based on name.
+ *
+ * @warning Before calling this, you must use {@link access_show_hidden_entities()}
+ *
+ * @param string $name An options name of annotations to enable.
+ * @return bool
+ * @since 1.8
+ */
+ public function enableAnnotations($name = '') {
+ $options = array(
+ 'guid' => $this->guid,
+ 'limit' => 0
+ );
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ return elgg_enable_annotations($options);
+ }
+
+ /**
+ * Helper function to return annotation calculation results
+ *
+ * @param string $name The annotation name.
+ * @param string $calculation A valid MySQL function to run its values through
+ * @return mixed
+ */
+ private function getAnnotationCalculation($name, $calculation) {
+ $options = array(
+ 'guid' => $this->getGUID(),
+ 'annotation_name' => $name,
+ 'annotation_calculation' => $calculation
+ );
+
+ return elgg_get_annotations($options);
+ }
+
+ /**
+ * Adds an annotation to an entity.
+ *
+ * @warning By default, annotations are private.
+ *
+ * @warning Annotating an unsaved entity more than once with the same name
+ * will only save the last annotation.
+ *
+ * @param string $name Annotation name
+ * @param mixed $value Annotation value
+ * @param int $access_id Access ID
+ * @param int $owner_id GUID of the annotation owner
+ * @param string $vartype The type of annotation value
+ *
+ * @return bool
+ */
+ function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_id = 0, $vartype = "") {
+ if ((int) $this->guid > 0) {
+ return create_annotation($this->getGUID(), $name, $value, $vartype, $owner_id, $access_id);
+ } else {
+ $this->temp_annotations[$name] = $value;
+ }
+ return true;
+ }
+
+ /**
+ * Returns an array of annotations.
+ *
+ * @param string $name Annotation name
+ * @param int $limit Limit
+ * @param int $offset Offset
+ * @param string $order Order by time: asc or desc
+ *
+ * @return array
+ */
+ function getAnnotations($name, $limit = 50, $offset = 0, $order = "asc") {
+ if ((int) ($this->guid) > 0) {
+
+ $options = array(
+ 'guid' => $this->guid,
+ 'annotation_name' => $name,
+ 'limit' => $limit,
+ 'offset' => $offset,
+ );
+
+ if ($order != 'asc') {
+ $options['reverse_order_by'] = true;
+ }
+
+ return elgg_get_annotations($options);
+ } else if (isset($this->temp_annotations[$name])) {
+ return array($this->temp_annotations[$name]);
+ } else {
+ return array();
+ }
+ }
+
+ /**
+ * Remove an annotation or all annotations for this entity.
+ *
+ * @warning Calling this method with no or an empty argument will remove
+ * all annotations on the entity.
+ *
+ * @param string $name Annotation name
+ * @return bool
+ * @deprecated 1.8 Use ->deleteAnnotations()
+ */
+ function clearAnnotations($name = "") {
+ elgg_deprecated_notice('ElggEntity->clearAnnotations() is deprecated by ->deleteAnnotations()', 1.8);
+ return $this->deleteAnnotations($name);
+ }
+
+ /**
+ * Count annotations.
+ *
+ * @param string $name The type of annotation.
+ *
+ * @return int
+ */
+ function countAnnotations($name = "") {
+ return $this->getAnnotationCalculation($name, 'count');
+ }
+
+ /**
+ * Get the average of an integer type annotation.
+ *
+ * @param string $name Annotation name
+ *
+ * @return int
+ */
+ function getAnnotationsAvg($name) {
+ return $this->getAnnotationCalculation($name, 'avg');
+ }
+
+ /**
+ * Get the sum of integer type annotations of a given name.
+ *
+ * @param string $name Annotation name
+ *
+ * @return int
+ */
+ function getAnnotationsSum($name) {
+ return $this->getAnnotationCalculation($name, 'sum');
+ }
+
+ /**
+ * Get the minimum of integer type annotations of given name.
+ *
+ * @param string $name Annotation name
+ *
+ * @return int
+ */
+ function getAnnotationsMin($name) {
+ return $this->getAnnotationCalculation($name, 'min');
+ }
+
+ /**
+ * Get the maximum of integer type annotations of a given name.
+ *
+ * @param string $name Annotation name
+ *
+ * @return int
+ */
+ function getAnnotationsMax($name) {
+ return $this->getAnnotationCalculation($name, 'max');
+ }
+
+ /**
+ * Count the number of comments attached to this entity.
+ *
+ * @return int Number of comments
+ * @since 1.8.0
+ */
+ function countComments() {
+ $params = array('entity' => $this);
+ $num = elgg_trigger_plugin_hook('comments:count', $this->getType(), $params);
+
+ if (is_int($num)) {
+ return $num;
+ } else {
+ return $this->getAnnotationCalculation('generic_comment', 'count');
+ }
+ }
+
+ /**
+ * Gets an array of entities with a relationship to this entity.
+ *
+ * @param string $relationship Relationship type (eg "friends")
+ * @param bool $inverse Is this an inverse relationship?
+ * @param int $limit Number of elements to return
+ * @param int $offset Indexing offset
+ *
+ * @return array|false An array of entities or false on failure
+ */
+ function getEntitiesFromRelationship($relationship, $inverse = false, $limit = 50, $offset = 0) {
+ return elgg_get_entities_from_relationship(array(
+ 'relationship' => $relationship,
+ 'relationship_guid' => $this->getGUID(),
+ 'inverse_relationship' => $inverse,
+ 'limit' => $limit,
+ 'offset' => $offset
+ ));
+ }
+
+ /**
+ * Gets the number of of entities from a specific relationship type
+ *
+ * @param string $relationship Relationship type (eg "friends")
+ * @param bool $inverse_relationship Invert relationship
+ *
+ * @return int|false The number of entities or false on failure
+ */
+ function countEntitiesFromRelationship($relationship, $inverse_relationship = FALSE) {
+ return elgg_get_entities_from_relationship(array(
+ 'relationship' => $relationship,
+ 'relationship_guid' => $this->getGUID(),
+ 'inverse_relationship' => $inverse_relationship,
+ 'count' => TRUE
+ ));
+ }
+
+ /**
+ * Can a user edit this entity.
+ *
+ * @param int $user_guid The user GUID, optionally (default: logged in user)
+ *
+ * @return bool
+ */
+ function canEdit($user_guid = 0) {
+ return can_edit_entity($this->getGUID(), $user_guid);
+ }
+
+ /**
+ * Can a user edit metadata on this entity
+ *
+ * @param ElggMetadata $metadata The piece of metadata to specifically check
+ * @param int $user_guid The user GUID, optionally (default: logged in user)
+ *
+ * @return bool
+ */
+ function canEditMetadata($metadata = null, $user_guid = 0) {
+ return can_edit_entity_metadata($this->getGUID(), $user_guid, $metadata);
+ }
+
+ /**
+ * Can a user add an entity to this container
+ *
+ * @param int $user_guid The user.
+ * @param string $type The type of entity we're looking to write
+ * @param string $subtype The subtype of the entity we're looking to write
+ *
+ * @return bool
+ */
+ public function canWriteToContainer($user_guid = 0, $type = 'all', $subtype = 'all') {
+ return can_write_to_container($user_guid, $this->guid, $type, $subtype);
+ }
+
+ /**
+ * Can a user comment on an entity?
+ *
+ * @tip Can be overridden by registering for the permissions_check:comment,
+ * <entity type> plugin hook.
+ *
+ * @param int $user_guid User guid (default is logged in user)
+ *
+ * @return bool
+ */
+ public function canComment($user_guid = 0) {
+ if ($user_guid == 0) {
+ $user_guid = elgg_get_logged_in_user_guid();
+ }
+ $user = get_entity($user_guid);
+
+ // By default, we don't take a position of whether commenting is allowed
+ // because it is handled by the subclasses of ElggEntity
+ $params = array('entity' => $this, 'user' => $user);
+ return elgg_trigger_plugin_hook('permissions_check:comment', $this->type, $params, null);
+ }
+
+ /**
+ * Can a user annotate an entity?
+ *
+ * @tip Can be overridden by registering for the permissions_check:annotate,
+ * <entity type> plugin hook.
+ *
+ * @tip If you want logged out users to annotate an object, do not call
+ * canAnnotate(). It's easier than using the plugin hook.
+ *
+ * @param int $user_guid User guid (default is logged in user)
+ * @param string $annotation_name The name of the annotation (default is unspecified)
+ *
+ * @return bool
+ */
+ public function canAnnotate($user_guid = 0, $annotation_name = '') {
+ if ($user_guid == 0) {
+ $user_guid = elgg_get_logged_in_user_guid();
+ }
+ $user = get_entity($user_guid);
+
+ $return = true;
+ if (!$user) {
+ $return = false;
+ }
+
+ $params = array(
+ 'entity' => $this,
+ 'user' => $user,
+ 'annotation_name' => $annotation_name,
+ );
+ return elgg_trigger_plugin_hook('permissions_check:annotate', $this->type, $params, $return);
+ }
+
+ /**
+ * Returns the access_id.
+ *
+ * @return int The access ID
+ */
+ public function getAccessID() {
+ return $this->get('access_id');
+ }
+
+ /**
+ * Returns the guid.
+ *
+ * @return int|null GUID
+ */
+ public function getGUID() {
+ return $this->get('guid');
+ }
+
+ /**
+ * Returns the entity type
+ *
+ * @return string Entity type
+ */
+ public function getType() {
+ return $this->get('type');
+ }
+
+ /**
+ * Returns the entity subtype string
+ *
+ * @note This returns a string. If you want the id, use ElggEntity::subtype.
+ *
+ * @return string The entity subtype
+ */
+ public function getSubtype() {
+ // If this object hasn't been saved, then return the subtype string.
+ if (!((int) $this->guid > 0)) {
+ return $this->get('subtype');
+ }
+
+ return get_subtype_from_id($this->get('subtype'));
+ }
+
+ /**
+ * Get the guid of the entity's owner.
+ *
+ * @return int The owner GUID
+ */
+ public function getOwnerGUID() {
+ return $this->owner_guid;
+ }
+
+ /**
+ * Return the guid of the entity's owner.
+ *
+ * @return int The owner GUID
+ * @deprecated 1.8 Use getOwnerGUID()
+ */
+ public function getOwner() {
+ elgg_deprecated_notice("ElggEntity::getOwner deprecated for ElggEntity::getOwnerGUID", 1.8);
+ return $this->getOwnerGUID();
+ }
+
+ /**
+ * Gets the ElggEntity that owns this entity.
+ *
+ * @return ElggEntity The owning entity
+ */
+ public function getOwnerEntity() {
+ return get_entity($this->owner_guid);
+ }
+
+ /**
+ * Set the container for this object.
+ *
+ * @param int $container_guid The ID of the container.
+ *
+ * @return bool
+ */
+ public function setContainerGUID($container_guid) {
+ $container_guid = (int)$container_guid;
+
+ return $this->set('container_guid', $container_guid);
+ }
+
+ /**
+ * Set the container for this object.
+ *
+ * @param int $container_guid The ID of the container.
+ *
+ * @return bool
+ * @deprecated 1.8 use setContainerGUID()
+ */
+ public function setContainer($container_guid) {
+ elgg_deprecated_notice("ElggObject::setContainer deprecated for ElggEntity::setContainerGUID", 1.8);
+ $container_guid = (int)$container_guid;
+
+ return $this->set('container_guid', $container_guid);
+ }
+
+ /**
+ * Gets the container GUID for this entity.
+ *
+ * @return int
+ */
+ public function getContainerGUID() {
+ return $this->get('container_guid');
+ }
+
+ /**
+ * Gets the container GUID for this entity.
+ *
+ * @return int
+ * @deprecated 1.8 Use getContainerGUID()
+ */
+ public function getContainer() {
+ elgg_deprecated_notice("ElggObject::getContainer deprecated for ElggEntity::getContainerGUID", 1.8);
+ return $this->get('container_guid');
+ }
+
+ /**
+ * Get the container entity for this object.
+ *
+ * @return ElggEntity
+ * @since 1.8.0
+ */
+ public function getContainerEntity() {
+ return get_entity($this->getContainerGUID());
+ }
+
+ /**
+ * Returns the UNIX epoch time that this entity was last updated
+ *
+ * @return int UNIX epoch time
+ */
+ public function getTimeUpdated() {
+ return $this->get('time_updated');
+ }
+
+ /**
+ * Returns the URL for this entity
+ *
+ * @return string The URL
+ * @see register_entity_url_handler()
+ * @see ElggEntity::setURL()
+ */
+ public function getURL() {
+ if (!empty($this->url_override)) {
+ return $this->url_override;
+ }
+ return get_entity_url($this->getGUID());
+ }
+
+ /**
+ * Overrides the URL returned by getURL()
+ *
+ * @warning This override exists only for the life of the object.
+ *
+ * @param string $url The new item URL
+ *
+ * @return string The URL
+ */
+ public function setURL($url) {
+ $this->url_override = $url;
+ return $url;
+ }
+
+ /**
+ * Get the URL for this entity's icon
+ *
+ * Plugins can register for the 'entity:icon:url', <type> plugin hook
+ * to customize the icon for an entity.
+ *
+ * @param string $size Size of the icon: tiny, small, medium, large
+ *
+ * @return string The URL
+ * @since 1.8.0
+ */
+ public function getIconURL($size = 'medium') {
+ $size = elgg_strtolower($size);
+
+ if (isset($this->icon_override[$size])) {
+ elgg_deprecated_notice("icon_override on an individual entity is deprecated", 1.8);
+ return $this->icon_override[$size];
+ }
+
+ $type = $this->getType();
+ $params = array(
+ 'entity' => $this,
+ 'size' => $size,
+ );
+
+ $url = elgg_trigger_plugin_hook('entity:icon:url', $type, $params, null);
+ if ($url == null) {
+ $url = "_graphics/icons/default/$size.png";
+ }
+
+ return elgg_normalize_url($url);
+ }
+
+ /**
+ * Returns a URL for the entity's icon.
+ *
+ * @param string $size Either 'large', 'medium', 'small' or 'tiny'
+ *
+ * @return string The url or false if no url could be worked out.
+ * @deprecated Use getIconURL()
+ */
+ public function getIcon($size = 'medium') {
+ elgg_deprecated_notice("getIcon() deprecated by getIconURL()", 1.8);
+ return $this->getIconURL($size);
+ }
+
+ /**
+ * Set an icon override for an icon and size.
+ *
+ * @warning This override exists only for the life of the object.
+ *
+ * @param string $url The url of the icon.
+ * @param string $size The size its for.
+ *
+ * @return bool
+ * @deprecated 1.8 See getIconURL() for the plugin hook to use
+ */
+ public function setIcon($url, $size = 'medium') {
+ elgg_deprecated_notice("icon_override on an individual entity is deprecated", 1.8);
+
+ $url = sanitise_string($url);
+ $size = sanitise_string($size);
+
+ if (!$this->icon_override) {
+ $this->icon_override = array();
+ }
+ $this->icon_override[$size] = $url;
+
+ return true;
+ }
+
+ /**
+ * Tests to see whether the object has been fully loaded.
+ *
+ * @return bool
+ */
+ public function isFullyLoaded() {
+ return ! ($this->attributes['tables_loaded'] < $this->attributes['tables_split']);
+ }
+
+ /**
+ * Save an entity.
+ *
+ * @return bool|int
+ * @throws IOException
+ */
+ public function save() {
+ $guid = $this->getGUID();
+ if ($guid > 0) {
+
+ // See #5600. This ensures the lower level can_edit_entity() check will use a
+ // fresh entity from the DB so it sees the persisted owner_guid
+ _elgg_disable_caching_for_entity($guid);
+
+ $ret = update_entity(
+ $guid,
+ $this->get('owner_guid'),
+ $this->get('access_id'),
+ $this->get('container_guid'),
+ $this->get('time_created')
+ );
+
+ _elgg_enable_caching_for_entity($guid);
+ _elgg_cache_entity($this);
+
+ return $ret;
+ } else {
+ // Create a new entity (nb: using attribute array directly
+ // 'cos set function does something special!)
+ $this->attributes['guid'] = create_entity($this->attributes['type'],
+ $this->attributes['subtype'], $this->attributes['owner_guid'],
+ $this->attributes['access_id'], $this->attributes['site_guid'],
+ $this->attributes['container_guid']);
+
+ if (!$this->attributes['guid']) {
+ throw new IOException(elgg_echo('IOException:BaseEntitySaveFailed'));
+ }
+
+ // Save any unsaved metadata
+ // @todo How to capture extra information (access id etc)
+ if (sizeof($this->temp_metadata) > 0) {
+ foreach ($this->temp_metadata as $name => $value) {
+ $this->$name = $value;
+ unset($this->temp_metadata[$name]);
+ }
+ }
+
+ // Save any unsaved annotations.
+ if (sizeof($this->temp_annotations) > 0) {
+ foreach ($this->temp_annotations as $name => $value) {
+ $this->annotate($name, $value);
+ unset($this->temp_annotations[$name]);
+ }
+ }
+
+ // Save any unsaved private settings.
+ if (sizeof($this->temp_private_settings) > 0) {
+ foreach ($this->temp_private_settings as $name => $value) {
+ $this->setPrivateSetting($name, $value);
+ unset($this->temp_private_settings[$name]);
+ }
+ }
+
+ // set the subtype to id now rather than a string
+ $this->attributes['subtype'] = get_subtype_id($this->attributes['type'],
+ $this->attributes['subtype']);
+
+ _elgg_cache_entity($this);
+
+ return $this->attributes['guid'];
+ }
+ }
+
+ /**
+ * Loads attributes from the entities table into the object.
+ *
+ * @param mixed $guid GUID of entity or stdClass object from entities table
+ *
+ * @return bool
+ */
+ protected function load($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
+ if (!is_array($this->attributes)) {
+ $this->attributes = array();
+ }
+
+ // Now put these into the attributes array as core values
+ $objarray = (array) $row;
+ foreach ($objarray as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+
+ // Increment the portion counter
+ if (!$this->isFullyLoaded()) {
+ $this->attributes['tables_loaded']++;
+ }
+
+ // guid needs to be an int https://github.com/elgg/elgg/issues/4111
+ $this->attributes['guid'] = (int)$this->attributes['guid'];
+
+ // Cache object handle
+ if ($this->attributes['guid']) {
+ _elgg_cache_entity($this);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Disable this entity.
+ *
+ * Disabled entities are not returned by getter functions.
+ * To enable an entity, use {@link enable_entity()}.
+ *
+ * Recursively disabling an entity will disable all entities
+ * owned or contained by the parent entity.
+ *
+ * @internal Disabling an entity sets the 'enabled' column to 'no'.
+ *
+ * @param string $reason Optional reason
+ * @param bool $recursive Recursively disable all contained entities?
+ *
+ * @return bool
+ * @see enable_entity()
+ * @see ElggEntity::enable()
+ */
+ public function disable($reason = "", $recursive = true) {
+ if ($r = disable_entity($this->get('guid'), $reason, $recursive)) {
+ $this->attributes['enabled'] = 'no';
+ }
+
+ return $r;
+ }
+
+ /**
+ * Enable an entity
+ *
+ * @warning Disabled entities can't be loaded unless
+ * {@link access_show_hidden_entities(true)} has been called.
+ *
+ * @see enable_entity()
+ * @see access_show_hiden_entities()
+ * @return bool
+ */
+ public function enable() {
+ if ($r = enable_entity($this->get('guid'))) {
+ $this->attributes['enabled'] = 'yes';
+ }
+
+ return $r;
+ }
+
+ /**
+ * Is this entity enabled?
+ *
+ * @return boolean
+ */
+ public function isEnabled() {
+ if ($this->enabled == 'yes') {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Delete this entity.
+ *
+ * @param bool $recursive Whether to delete all the entities contained by this entity
+ *
+ * @return bool
+ */
+ public function delete($recursive = true) {
+ return delete_entity($this->get('guid'), $recursive);
+ }
+
+ /*
+ * LOCATABLE INTERFACE
+ */
+
+ /**
+ * Gets the 'location' metadata for the entity
+ *
+ * @return string The location
+ */
+ public function getLocation() {
+ return $this->location;
+ }
+
+ /**
+ * Sets the 'location' metadata for the entity
+ *
+ * @todo Unimplemented
+ *
+ * @param string $location String representation of the location
+ *
+ * @return bool
+ */
+ public function setLocation($location) {
+ $this->location = $location;
+ return true;
+ }
+
+ /**
+ * Set latitude and longitude metadata tags for a given entity.
+ *
+ * @param float $lat Latitude
+ * @param float $long Longitude
+ *
+ * @return bool
+ * @todo Unimplemented
+ */
+ public function setLatLong($lat, $long) {
+ $this->set('geo:lat', $lat);
+ $this->set('geo:long', $long);
+
+ return true;
+ }
+
+ /**
+ * Return the entity's latitude.
+ *
+ * @return float
+ * @todo Unimplemented
+ */
+ public function getLatitude() {
+ return (float)$this->get('geo:lat');
+ }
+
+ /**
+ * Return the entity's longitude
+ *
+ * @return float
+ */
+ public function getLongitude() {
+ return (float)$this->get('geo:long');
+ }
+
+ /*
+ * NOTABLE INTERFACE
+ */
+
+ /**
+ * Set the time and duration of an object
+ *
+ * @param int $hour If ommitted, now is assumed.
+ * @param int $minute If ommitted, now is assumed.
+ * @param int $second If ommitted, now is assumed.
+ * @param int $day If ommitted, now is assumed.
+ * @param int $month If ommitted, now is assumed.
+ * @param int $year If ommitted, now is assumed.
+ * @param int $duration Duration of event, remainder of the day is assumed.
+ *
+ * @return true
+ * @todo Unimplemented
+ */
+ public function setCalendarTimeAndDuration($hour = NULL, $minute = NULL, $second = NULL,
+ $day = NULL, $month = NULL, $year = NULL, $duration = NULL) {
+
+ $start = mktime($hour, $minute, $second, $month, $day, $year);
+ $end = $start + abs($duration);
+ if (!$duration) {
+ $end = get_day_end($day, $month, $year);
+ }
+
+ $this->calendar_start = $start;
+ $this->calendar_end = $end;
+
+ return true;
+ }
+
+ /**
+ * Returns the start timestamp.
+ *
+ * @return int
+ * @todo Unimplemented
+ */
+ public function getCalendarStartTime() {
+ return (int)$this->calendar_start;
+ }
+
+ /**
+ * Returns the end timestamp.
+ *
+ * @todo Unimplemented
+ *
+ * @return int
+ */
+ public function getCalendarEndTime() {
+ return (int)$this->calendar_end;
+ }
+
+ /*
+ * EXPORTABLE INTERFACE
+ */
+
+ /**
+ * Returns an array of fields which can be exported.
+ *
+ * @return array
+ */
+ public function getExportableValues() {
+ return array(
+ 'guid',
+ 'type',
+ 'subtype',
+ 'time_created',
+ 'time_updated',
+ 'container_guid',
+ 'owner_guid',
+ 'site_guid'
+ );
+ }
+
+ /**
+ * Export this class into an array of ODD Elements containing all necessary fields.
+ * Override if you wish to return more information than can be found in
+ * $this->attributes (shouldn't happen)
+ *
+ * @return array
+ */
+ public function export() {
+ $tmp = array();
+
+ // Generate uuid
+ $uuid = guid_to_uuid($this->getGUID());
+
+ // Create entity
+ $odd = new ODDEntity(
+ $uuid,
+ $this->attributes['type'],
+ get_subtype_from_id($this->attributes['subtype'])
+ );
+
+ $tmp[] = $odd;
+
+ $exportable_values = $this->getExportableValues();
+
+ // Now add its attributes
+ foreach ($this->attributes as $k => $v) {
+ $meta = NULL;
+
+ if (in_array($k, $exportable_values)) {
+ switch ($k) {
+ case 'guid': // Dont use guid in OpenDD
+ case 'type': // Type and subtype already taken care of
+ case 'subtype':
+ break;
+
+ case 'time_created': // Created = published
+ $odd->setAttribute('published', date("r", $v));
+ break;
+
+ case 'site_guid': // Container
+ $k = 'site_uuid';
+ $v = guid_to_uuid($v);
+ $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
+ break;
+
+ case 'container_guid': // Container
+ $k = 'container_uuid';
+ $v = guid_to_uuid($v);
+ $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
+ break;
+
+ case 'owner_guid': // Convert owner guid to uuid, this will be stored in metadata
+ $k = 'owner_uuid';
+ $v = guid_to_uuid($v);
+ $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
+ break;
+
+ default:
+ $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
+ }
+
+ // set the time of any metadata created
+ if ($meta) {
+ $meta->setAttribute('published', date("r", $this->time_created));
+ $tmp[] = $meta;
+ }
+ }
+ }
+
+ // Now we do something a bit special.
+ /*
+ * This provides a rendered view of the entity to foreign sites.
+ */
+
+ elgg_set_viewtype('default');
+ $view = elgg_view_entity($this, array('full_view' => true));
+ elgg_set_viewtype();
+
+ $tmp[] = new ODDMetaData($uuid . "volatile/renderedentity/", $uuid,
+ 'renderedentity', $view, 'volatile');
+
+ return $tmp;
+ }
+
+ /*
+ * IMPORTABLE INTERFACE
+ */
+
+ /**
+ * Import data from an parsed ODD xml data array.
+ *
+ * @param ODD $data XML data
+ *
+ * @return true
+ *
+ * @throws InvalidParameterException
+ */
+ public function import(ODD $data) {
+ if (!($data instanceof ODDEntity)) {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnexpectedODDClass'));
+ }
+
+ // Set type and subtype
+ $this->attributes['type'] = $data->getAttribute('class');
+ $this->attributes['subtype'] = $data->getAttribute('subclass');
+
+ // Set owner
+ $this->attributes['owner_guid'] = elgg_get_logged_in_user_guid(); // Import as belonging to importer.
+
+ // Set time
+ $this->attributes['time_created'] = strtotime($data->getAttribute('published'));
+ $this->attributes['time_updated'] = time();
+
+ return true;
+ }
+
+ /*
+ * SYSTEM LOG INTERFACE
+ */
+
+ /**
+ * Return an identification for the object for storage in the system log.
+ * This id must be an integer.
+ *
+ * @return int
+ */
+ public function getSystemLogID() {
+ return $this->getGUID();
+ }
+
+ /**
+ * For a given ID, return the object associated with it.
+ * This is used by the river functionality primarily.
+ *
+ * This is useful for checking access permissions etc on objects.
+ *
+ * @param int $id GUID.
+ *
+ * @todo How is this any different or more useful than get_entity($guid)
+ * or new ElggEntity($guid)?
+ *
+ * @return int GUID
+ */
+ public function getObjectFromID($id) {
+ return get_entity($id);
+ }
+
+ /**
+ * Returns tags for this entity.
+ *
+ * @warning Tags must be registered by {@link elgg_register_tag_metadata_name()}.
+ *
+ * @param array $tag_names Optionally restrict by tag metadata names.
+ *
+ * @return array
+ */
+ public function getTags($tag_names = NULL) {
+ if ($tag_names && !is_array($tag_names)) {
+ $tag_names = array($tag_names);
+ }
+
+ $valid_tags = elgg_get_registered_tag_metadata_names();
+ $entity_tags = array();
+
+ foreach ($valid_tags as $tag_name) {
+ if (is_array($tag_names) && !in_array($tag_name, $tag_names)) {
+ continue;
+ }
+
+ if ($tags = $this->$tag_name) {
+ // if a single tag, metadata returns a string.
+ // if multiple tags, metadata returns an array.
+ if (is_array($tags)) {
+ $entity_tags = array_merge($entity_tags, $tags);
+ } else {
+ $entity_tags[] = $tags;
+ }
+ }
+ }
+
+ return $entity_tags;
+ }
+}
diff --git a/engine/classes/ElggExtender.php b/engine/classes/ElggExtender.php
new file mode 100644
index 000000000..25aba354f
--- /dev/null
+++ b/engine/classes/ElggExtender.php
@@ -0,0 +1,214 @@
+<?php
+/**
+ * The base class for ElggEntity extenders.
+ *
+ * Extenders allow you to attach extended information to an
+ * ElggEntity. Core supports two: ElggAnnotation and ElggMetadata.
+ *
+ * Saving the extender data to database is handled by the child class.
+ *
+ * @tip Plugin authors would probably want to extend either ElggAnnotation
+ * or ElggMetadata instead of this class.
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel.Extender
+ * @link http://docs.elgg.org/DataModel/Extenders
+ * @see ElggAnnotation
+ * @see ElggMetadata
+ *
+ * @property string $type annotation or metadata (read-only after save)
+ * @property int $id The unique identifier (read-only)
+ * @property int $entity_guid The GUID of the entity that this extender describes
+ * @property int $access_id Specifies the visibility level of this extender
+ * @property string $name The name of this extender
+ * @property mixed $value The value of the extender (int or string)
+ * @property int $time_created A UNIX timestamp of when the extender was created (read-only, set on first save)
+ */
+abstract class ElggExtender extends ElggData {
+
+ /**
+ * (non-PHPdoc)
+ *
+ * @see ElggData::initializeAttributes()
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['type'] = NULL;
+ }
+
+ /**
+ * Returns an attribute
+ *
+ * @param string $name Name
+ *
+ * @return mixed
+ */
+ protected function get($name) {
+ if (array_key_exists($name, $this->attributes)) {
+ // Sanitise value if necessary
+ if ($name == 'value') {
+ switch ($this->attributes['value_type']) {
+ case 'integer' :
+ return (int)$this->attributes['value'];
+ break;
+
+ //case 'tag' :
+ //case 'file' :
+ case 'text' :
+ return ($this->attributes['value']);
+ break;
+
+ default :
+ $msg = elgg_echo('InstallationException:TypeNotSupported', array(
+ $this->attributes['value_type']));
+
+ throw new InstallationException($msg);
+ break;
+ }
+ }
+
+ return $this->attributes[$name];
+ }
+ return null;
+ }
+
+ /**
+ * Set an attribute
+ *
+ * @param string $name Name
+ * @param mixed $value Value
+ * @param string $value_type Value type
+ *
+ * @return boolean
+ */
+ protected function set($name, $value, $value_type = "") {
+ $this->attributes[$name] = $value;
+ if ($name == 'value') {
+ $this->attributes['value_type'] = detect_extender_valuetype($value, $value_type);
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the GUID of the extender's owner entity.
+ *
+ * @return int The owner GUID
+ */
+ public function getOwnerGUID() {
+ return $this->owner_guid;
+ }
+
+ /**
+ * Return the guid of the entity's owner.
+ *
+ * @return int The owner GUID
+ * @deprecated 1.8 Use getOwnerGUID
+ */
+ public function getOwner() {
+ elgg_deprecated_notice("ElggExtender::getOwner deprecated for ElggExtender::getOwnerGUID", 1.8);
+ return $this->getOwnerGUID();
+ }
+
+ /**
+ * Get the entity that owns this extender
+ *
+ * @return ElggEntity
+ */
+ public function getOwnerEntity() {
+ return get_entity($this->owner_guid);
+ }
+
+ /**
+ * Get the entity this describes.
+ *
+ * @return ElggEntity The entity
+ */
+ public function getEntity() {
+ return get_entity($this->entity_guid);
+ }
+
+ /**
+ * Returns if a user can edit this extended data.
+ *
+ * @param int $user_guid The GUID of the user (defaults to currently logged in user)
+ *
+ * @return bool
+ */
+ public function canEdit($user_guid = 0) {
+ return can_edit_extender($this->id, $this->type, $user_guid);
+ }
+
+ /*
+ * EXPORTABLE INTERFACE
+ */
+
+ /**
+ * Return an array of fields which can be exported.
+ *
+ * @return array
+ */
+ public function getExportableValues() {
+ return array(
+ 'id',
+ 'entity_guid',
+ 'name',
+ 'value',
+ 'value_type',
+ 'owner_guid',
+ 'type',
+ );
+ }
+
+ /**
+ * Export this object
+ *
+ * @return array
+ */
+ public function export() {
+ $uuid = get_uuid_from_object($this);
+
+ $meta = new ODDMetaData($uuid, guid_to_uuid($this->entity_guid), $this->attributes['name'],
+ $this->attributes['value'], $this->attributes['type'], guid_to_uuid($this->owner_guid));
+ $meta->setAttribute('published', date("r", $this->time_created));
+
+ return $meta;
+ }
+
+ /*
+ * SYSTEM LOG INTERFACE
+ */
+
+ /**
+ * Return an identification for the object for storage in the system log.
+ * This id must be an integer.
+ *
+ * @return int
+ */
+ public function getSystemLogID() {
+ return $this->id;
+ }
+
+ /**
+ * Return a type of extension.
+ *
+ * @return string
+ */
+ public function getType() {
+ return $this->type;
+ }
+
+ /**
+ * Return a subtype. For metadata & annotations this is the 'name' and
+ * for relationship this is the relationship type.
+ *
+ * @return string
+ */
+ public function getSubtype() {
+ return $this->name;
+ }
+
+}
diff --git a/engine/classes/ElggFile.php b/engine/classes/ElggFile.php
new file mode 100644
index 000000000..23080834b
--- /dev/null
+++ b/engine/classes/ElggFile.php
@@ -0,0 +1,440 @@
+<?php
+
+/**
+ * This class represents a physical file.
+ *
+ * Create a new ElggFile object and specify a filename, and optionally a
+ * FileStore (if one isn't specified then the default is assumed.)
+ *
+ * Open the file using the appropriate mode, and you will be able to
+ * read and write to the file.
+ *
+ * Optionally, you can also call the file's save() method, this will
+ * turn the file into an entity in the system and permit you to do
+ * things like attach tags to the file etc. This is not done automatically
+ * since there are many occasions where you may want access to file data
+ * on datastores using the ElggFile interface but do not want to create
+ * an Entity reference to it in the system (temporary files for example).
+ *
+ * @class ElggFile
+ * @package Elgg.Core
+ * @subpackage DataModel.File
+ */
+class ElggFile extends ElggObject {
+ /** Filestore */
+ private $filestore;
+
+ /** File handle used to identify this file in a filestore. Created by open. */
+ private $handle;
+
+ /**
+ * Set subtype to 'file'.
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['subtype'] = "file";
+ }
+
+ /**
+ * Loads an ElggFile entity.
+ *
+ * @param int $guid GUID of the ElggFile object
+ */
+ public function __construct($guid = null) {
+ parent::__construct($guid);
+
+ // Set default filestore
+ $this->filestore = $this->getFilestore();
+ }
+
+ /**
+ * Set the filename of this file.
+ *
+ * @param string $name The filename.
+ *
+ * @return void
+ */
+ public function setFilename($name) {
+ $this->filename = $name;
+ }
+
+ /**
+ * Return the filename.
+ *
+ * @return string
+ */
+ public function getFilename() {
+ return $this->filename;
+ }
+
+ /**
+ * Return the filename of this file as it is/will be stored on the
+ * filestore, which may be different to the filename.
+ *
+ * @return string
+ */
+ public function getFilenameOnFilestore() {
+ return $this->filestore->getFilenameOnFilestore($this);
+ }
+
+ /**
+ * Return the size of the filestore associated with this file
+ *
+ * @param string $prefix Storage prefix
+ * @param int $container_guid The container GUID of the checked filestore
+ *
+ * @return int
+ */
+ public function getFilestoreSize($prefix = '', $container_guid = 0) {
+ if (!$container_guid) {
+ $container_guid = $this->container_guid;
+ }
+ $fs = $this->getFilestore();
+ // @todo add getSize() to ElggFilestore
+ return $fs->getSize($prefix, $container_guid);
+ }
+
+ /**
+ * Get the mime type of the file.
+ *
+ * @return string
+ */
+ public function getMimeType() {
+ if ($this->mimetype) {
+ return $this->mimetype;
+ }
+
+ // @todo Guess mimetype if not here
+ }
+
+ /**
+ * Set the mime type of the file.
+ *
+ * @param string $mimetype The mimetype
+ *
+ * @return bool
+ */
+ public function setMimeType($mimetype) {
+ return $this->mimetype = $mimetype;
+ }
+
+ /**
+ * Detects mime types based on filename or actual file.
+ *
+ * @param mixed $file The full path of the file to check. For uploaded files, use tmp_name.
+ * @param mixed $default A default. Useful to pass what the browser thinks it is.
+ * @since 1.7.12
+ *
+ * @note If $file is provided, this may be called statically
+ *
+ * @return mixed Detected type on success, false on failure.
+ */
+ public function detectMimeType($file = null, $default = null) {
+ if (!$file) {
+ if (isset($this) && $this->filename) {
+ $file = $this->filename;
+ } else {
+ return false;
+ }
+ }
+
+ $mime = false;
+
+ // for PHP5 folks.
+ if (function_exists('finfo_file') && defined('FILEINFO_MIME_TYPE')) {
+ $resource = finfo_open(FILEINFO_MIME_TYPE);
+ if ($resource) {
+ $mime = finfo_file($resource, $file);
+ }
+ }
+
+ // for everyone else.
+ if (!$mime && function_exists('mime_content_type')) {
+ $mime = mime_content_type($file);
+ }
+
+ // default
+ if (!$mime) {
+ return $default;
+ }
+
+ return $mime;
+ }
+
+ /**
+ * Set the optional file description.
+ *
+ * @param string $description The description.
+ *
+ * @return bool
+ */
+ public function setDescription($description) {
+ $this->description = $description;
+ }
+
+ /**
+ * Open the file with the given mode
+ *
+ * @param string $mode Either read/write/append
+ *
+ * @return resource File handler
+ *
+ * @throws IOException|InvalidParameterException
+ */
+ public function open($mode) {
+ if (!$this->getFilename()) {
+ throw new IOException(elgg_echo('IOException:MissingFileName'));
+ }
+
+ // See if file has already been saved
+ // seek on datastore, parameters and name?
+
+ // Sanity check
+ if (
+ ($mode != "read") &&
+ ($mode != "write") &&
+ ($mode != "append")
+ ) {
+ $msg = elgg_echo('InvalidParameterException:UnrecognisedFileMode', array($mode));
+ throw new InvalidParameterException($msg);
+ }
+
+ // Get the filestore
+ $fs = $this->getFilestore();
+
+ // Ensure that we save the file details to object store
+ //$this->save();
+
+ // Open the file handle
+ $this->handle = $fs->open($this, $mode);
+
+ return $this->handle;
+ }
+
+ /**
+ * Write data.
+ *
+ * @param string $data The data
+ *
+ * @return bool
+ */
+ public function write($data) {
+ $fs = $this->getFilestore();
+
+ return $fs->write($this->handle, $data);
+ }
+
+ /**
+ * Read data.
+ *
+ * @param int $length Amount to read.
+ * @param int $offset The offset to start from.
+ *
+ * @return mixed Data or false
+ */
+ public function read($length, $offset = 0) {
+ $fs = $this->getFilestore();
+
+ return $fs->read($this->handle, $length, $offset);
+ }
+
+ /**
+ * Gets the full contents of this file.
+ *
+ * @return mixed The file contents.
+ */
+ public function grabFile() {
+ $fs = $this->getFilestore();
+ return $fs->grabFile($this);
+ }
+
+ /**
+ * Close the file and commit changes
+ *
+ * @return bool
+ */
+ public function close() {
+ $fs = $this->getFilestore();
+
+ if ($fs->close($this->handle)) {
+ $this->handle = NULL;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Delete this file.
+ *
+ * @return bool
+ */
+ public function delete() {
+ $fs = $this->getFilestore();
+
+ $result = $fs->delete($this);
+
+ if ($this->getGUID() && $result) {
+ $result = parent::delete();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Seek a position in the file.
+ *
+ * @param int $position Position in bytes
+ *
+ * @return bool
+ */
+ public function seek($position) {
+ $fs = $this->getFilestore();
+
+ // @todo add seek() to ElggFilestore
+ return $fs->seek($this->handle, $position);
+ }
+
+ /**
+ * Return the current position of the file.
+ *
+ * @return int The file position
+ */
+ public function tell() {
+ $fs = $this->getFilestore();
+
+ return $fs->tell($this->handle);
+ }
+
+ /**
+ * Return the size of the file in bytes.
+ *
+ * @return int
+ */
+ public function size() {
+ return $this->filestore->getFileSize($this);
+ }
+
+ /**
+ * Return a boolean value whether the file handle is at the end of the file
+ *
+ * @return bool
+ */
+ public function eof() {
+ $fs = $this->getFilestore();
+
+ return $fs->eof($this->handle);
+ }
+
+ /**
+ * Returns if the file exists
+ *
+ * @return bool
+ */
+ public function exists() {
+ $fs = $this->getFilestore();
+
+ return $fs->exists($this);
+ }
+
+ /**
+ * Set a filestore.
+ *
+ * @param ElggFilestore $filestore The file store.
+ *
+ * @return void
+ */
+ public function setFilestore(ElggFilestore $filestore) {
+ $this->filestore = $filestore;
+ }
+
+ /**
+ * Return a filestore suitable for saving this file.
+ * This filestore is either a pre-registered filestore,
+ * a filestore as recorded in metadata or the system default.
+ *
+ * @return ElggFilestore
+ *
+ * @throws ClassNotFoundException
+ */
+ protected function getFilestore() {
+ // Short circuit if already set.
+ if ($this->filestore) {
+ return $this->filestore;
+ }
+
+ // ask for entity specific filestore
+ // saved as filestore::className in metadata.
+ // need to get all filestore::* metadata because the rest are "parameters" that
+ // get passed to filestore::setParameters()
+ if ($this->guid) {
+ $options = array(
+ 'guid' => $this->guid,
+ 'where' => array("n.string LIKE 'filestore::%'"),
+ );
+
+ $mds = elgg_get_metadata($options);
+
+ $parameters = array();
+ foreach ($mds as $md) {
+ list($foo, $name) = explode("::", $md->name);
+ if ($name == 'filestore') {
+ $filestore = $md->value;
+ }
+ $parameters[$name] = $md->value;
+ }
+ }
+
+ // need to check if filestore is set because this entity is loaded in save()
+ // before the filestore metadata is saved.
+ if (isset($filestore)) {
+ if (!class_exists($filestore)) {
+ $msg = elgg_echo('ClassNotFoundException:NotFoundNotSavedWithFile',
+ array($filestore, $this->guid));
+ throw new ClassNotFoundException($msg);
+ }
+
+ $this->filestore = new $filestore();
+ $this->filestore->setParameters($parameters);
+ // @todo explain why $parameters will always be set here (PhpStorm complains)
+ }
+
+ // this means the entity hasn't been saved so fallback to default
+ if (!$this->filestore) {
+ $this->filestore = get_default_filestore();
+ }
+
+ return $this->filestore;
+ }
+
+ /**
+ * Save the file
+ *
+ * Write the file's data to the filestore and save
+ * the corresponding entity.
+ *
+ * @see ElggObject::save()
+ *
+ * @return bool
+ */
+ public function save() {
+ if (!parent::save()) {
+ return false;
+ }
+
+ // Save datastore metadata
+ $params = $this->filestore->getParameters();
+ foreach ($params as $k => $v) {
+ $this->setMetaData("filestore::$k", $v);
+ }
+
+ // Now make a note of the filestore class
+ $this->setMetaData("filestore::filestore", get_class($this->filestore));
+
+ return true;
+ }
+}
diff --git a/engine/classes/ElggFileCache.php b/engine/classes/ElggFileCache.php
new file mode 100644
index 000000000..94143f777
--- /dev/null
+++ b/engine/classes/ElggFileCache.php
@@ -0,0 +1,230 @@
+<?php
+/**
+ * ElggFileCache
+ * Store cached data in a file store.
+ *
+ * @package Elgg.Core
+ * @subpackage Caches
+ */
+class ElggFileCache extends ElggCache {
+ /**
+ * Set the Elgg cache.
+ *
+ * @param string $cache_path The cache path.
+ * @param int $max_age Maximum age in seconds, 0 if no limit.
+ * @param int $max_size Maximum size of cache in seconds, 0 if no limit.
+ *
+ * @throws ConfigurationException
+ */
+ function __construct($cache_path, $max_age = 0, $max_size = 0) {
+ $this->setVariable("cache_path", $cache_path);
+ $this->setVariable("max_age", $max_age);
+ $this->setVariable("max_size", $max_size);
+
+ if ($cache_path == "") {
+ throw new ConfigurationException(elgg_echo('ConfigurationException:NoCachePath'));
+ }
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Create and return a handle to a file.
+ *
+ * @deprecated 1.8 Use ElggFileCache::createFile()
+ *
+ * @param string $filename Filename to save as
+ * @param string $rw Write mode
+ *
+ * @return mixed
+ */
+ protected function create_file($filename, $rw = "rb") {
+ elgg_deprecated_notice('ElggFileCache::create_file() is deprecated by ::createFile()', 1.8);
+
+ return $this->createFile($filename, $rw);
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * Create and return a handle to a file.
+ *
+ * @param string $filename Filename to save as
+ * @param string $rw Write mode
+ *
+ * @return mixed
+ */
+ protected function createFile($filename, $rw = "rb") {
+ // Create a filename matrix
+ $matrix = "";
+ $depth = strlen($filename);
+ if ($depth > 5) {
+ $depth = 5;
+ }
+
+ // Create full path
+ $path = $this->getVariable("cache_path") . $matrix;
+ if (!is_dir($path)) {
+ mkdir($path, 0700, true);
+ }
+
+ // Open the file
+ if ((!file_exists($path . $filename)) && ($rw == "rb")) {
+ return false;
+ }
+
+ return fopen($path . $filename, $rw);
+ }
+
+ // @codingStandardsIgnoreStart
+ /**
+ * Create a sanitised filename for the file.
+ *
+ * @deprecated 1.8 Use ElggFileCache::sanitizeFilename()
+ *
+ * @param string $filename The filename
+ *
+ * @return string
+ */
+ protected function sanitise_filename($filename) {
+ // @todo : Writeme
+
+ return $filename;
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * Create a sanitised filename for the file.
+ *
+ * @param string $filename The filename
+ *
+ * @return string
+ */
+ protected function sanitizeFilename($filename) {
+ // @todo : Writeme
+
+ return $filename;
+ }
+
+ /**
+ * Save a key
+ *
+ * @param string $key Name
+ * @param string $data Value
+ *
+ * @return boolean
+ */
+ public function save($key, $data) {
+ $f = $this->createFile($this->sanitizeFilename($key), "wb");
+ if ($f) {
+ $result = fwrite($f, $data);
+ fclose($f);
+
+ return $result;
+ }
+
+ return false;
+ }
+
+ /**
+ * Load a key
+ *
+ * @param string $key Name
+ * @param int $offset Offset
+ * @param int $limit Limit
+ *
+ * @return string
+ */
+ public function load($key, $offset = 0, $limit = null) {
+ $f = $this->createFile($this->sanitizeFilename($key));
+ if ($f) {
+ if (!$limit) {
+ $limit = -1;
+ }
+
+ $data = stream_get_contents($f, $limit, $offset);
+
+ fclose($f);
+
+ return $data;
+ }
+
+ return false;
+ }
+
+ /**
+ * Invalidate a given key.
+ *
+ * @param string $key Name
+ *
+ * @return bool
+ */
+ public function delete($key) {
+ $dir = $this->getVariable("cache_path");
+
+ if (file_exists($dir . $key)) {
+ return unlink($dir . $key);
+ }
+ return TRUE;
+ }
+
+ /**
+ * Delete all files in the directory of this file cache
+ *
+ * @return void
+ */
+ public function clear() {
+ $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);
+ }
+ }
+ }
+
+ /**
+ * Preform cleanup and invalidates cache upon object destruction
+ *
+ * @throws IOException
+ */
+ public function __destruct() {
+ // @todo Check size and age, clean up accordingly
+ $size = 0;
+ $dir = $this->getVariable("cache_path");
+
+ // Short circuit if both size and age are unlimited
+ if (($this->getVariable("max_age") == 0) && ($this->getVariable("max_size") == 0)) {
+ return;
+ }
+
+ $exclude = array(".", "..");
+
+ $files = scandir($dir);
+ if (!$files) {
+ throw new IOException(elgg_echo('IOException:NotDirectory', array($dir)));
+ }
+
+ // Perform cleanup
+ foreach ($files as $f) {
+ if (!in_array($f, $exclude)) {
+ $stat = stat($dir . $f);
+
+ // Add size
+ $size .= $stat['size'];
+
+ // Is this older than my maximum date?
+ if (($this->getVariable("max_age") > 0) && (time() - $stat['mtime'] > $this->getVariable("max_age"))) {
+ unlink($dir . $f);
+ }
+
+ // @todo Size
+ }
+ }
+ }
+}
diff --git a/engine/classes/ElggFilestore.php b/engine/classes/ElggFilestore.php
new file mode 100644
index 000000000..16430feac
--- /dev/null
+++ b/engine/classes/ElggFilestore.php
@@ -0,0 +1,139 @@
+<?php
+/**
+ * This class defines the interface for all elgg data repositories.
+ *
+ * @package Elgg.Core
+ * @subpackage DataStorage
+ * @class ElggFilestore
+ */
+abstract class ElggFilestore {
+ /**
+ * Attempt to open the file $file for storage or writing.
+ *
+ * @param ElggFile $file A file
+ * @param string $mode "read", "write", "append"
+ *
+ * @return mixed A handle to the opened file or false on error.
+ */
+ abstract public function open(ElggFile $file, $mode);
+
+ /**
+ * Write data to a given file handle.
+ *
+ * @param mixed $f The file handle - exactly what this is depends on the file system
+ * @param string $data The binary string of data to write
+ *
+ * @return int Number of bytes written.
+ */
+ abstract public function write($f, $data);
+
+ /**
+ * Read data from a filestore.
+ *
+ * @param mixed $f The file handle
+ * @param int $length Length in bytes to read.
+ * @param int $offset The optional offset.
+ *
+ * @return mixed String of data or false on error.
+ */
+ abstract public function read($f, $length, $offset = 0);
+
+ /**
+ * Seek a given position within a file handle.
+ *
+ * @param mixed $f The file handle.
+ * @param int $position The position.
+ *
+ * @return void
+ */
+ abstract public function seek($f, $position);
+
+ /**
+ * Return a whether the end of a file has been reached.
+ *
+ * @param mixed $f The file handle.
+ *
+ * @return boolean
+ */
+ abstract public function eof($f);
+
+ /**
+ * Return the current position in an open file.
+ *
+ * @param mixed $f The file handle.
+ *
+ * @return int
+ */
+ abstract public function tell($f);
+
+ /**
+ * Close a given file handle.
+ *
+ * @param mixed $f The file handle
+ *
+ * @return bool
+ */
+ abstract public function close($f);
+
+ /**
+ * Delete the file associated with a given file handle.
+ *
+ * @param ElggFile $file The file
+ *
+ * @return bool
+ */
+ abstract public function delete(ElggFile $file);
+
+ /**
+ * Return the size in bytes for a given file.
+ *
+ * @param ElggFile $file The file
+ *
+ * @return int
+ */
+ abstract public function getFileSize(ElggFile $file);
+
+ /**
+ * Return the filename of a given file as stored on the filestore.
+ *
+ * @param ElggFile $file The file
+ *
+ * @return string
+ */
+ abstract public function getFilenameOnFilestore(ElggFile $file);
+
+ /**
+ * Get the filestore's creation parameters as an associative array.
+ * Used for serialisation and for storing the creation details along side a file object.
+ *
+ * @return array
+ */
+ abstract public function getParameters();
+
+ /**
+ * Set the parameters from the associative array produced by $this->getParameters().
+ *
+ * @param array $parameters A list of parameters
+ *
+ * @return bool
+ */
+ abstract public function setParameters(array $parameters);
+
+ /**
+ * Get the contents of the whole file.
+ *
+ * @param mixed $file The file handle.
+ *
+ * @return mixed The file contents.
+ */
+ abstract public function grabFile(ElggFile $file);
+
+ /**
+ * Return whether a file physically exists or not.
+ *
+ * @param ElggFile $file The file
+ *
+ * @return bool
+ */
+ abstract public function exists(ElggFile $file);
+}
diff --git a/engine/classes/ElggGroup.php b/engine/classes/ElggGroup.php
new file mode 100644
index 000000000..7e69b7a84
--- /dev/null
+++ b/engine/classes/ElggGroup.php
@@ -0,0 +1,393 @@
+<?php
+
+/**
+ * Class representing a container for other elgg entities.
+ *
+ * @package Elgg.Core
+ * @subpackage Groups
+ *
+ * @property string $name A short name that captures the purpose of the group
+ * @property string $description A longer body of content that gives more details about the group
+ */
+class ElggGroup extends ElggEntity
+ implements Friendable {
+
+ /**
+ * Sets the type to group.
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['type'] = "group";
+ $this->attributes['name'] = NULL;
+ $this->attributes['description'] = NULL;
+ $this->attributes['tables_split'] = 2;
+ }
+
+ /**
+ * Construct a new group entity, optionally from a given guid value.
+ *
+ * @param mixed $guid If an int, load that GUID.
+ * If an entity table db row, then will load the rest of the data.
+ *
+ * @throws IOException|InvalidParameterException if there was a problem creating the group.
+ */
+ function __construct($guid = null) {
+ $this->initializeAttributes();
+
+ // compatibility for 1.7 api.
+ $this->initialise_attributes(false);
+
+ if (!empty($guid)) {
+ // Is $guid is a entity table DB row
+ if ($guid instanceof stdClass) {
+ // Load the rest
+ if (!$this->load($guid)) {
+ $msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
+ throw new IOException($msg);
+ }
+ } else if ($guid instanceof ElggGroup) {
+ // $guid is an ElggGroup so this is a copy constructor
+ elgg_deprecated_notice('This type of usage of the ElggGroup constructor was deprecated. Please use the clone method.', 1.7);
+
+ foreach ($guid->attributes as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+ } else if ($guid instanceof ElggEntity) {
+ // @todo why separate from else
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggGroup'));
+ } else if (is_numeric($guid)) {
+ // $guid is a GUID so load entity
+ if (!$this->load($guid)) {
+ throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
+ }
+ } else {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
+ }
+ }
+ }
+
+ /**
+ * Add an ElggObject to this group.
+ *
+ * @param ElggObject $object The object.
+ *
+ * @return bool
+ */
+ public function addObjectToGroup(ElggObject $object) {
+ return add_object_to_group($this->getGUID(), $object->getGUID());
+ }
+
+ /**
+ * Remove an object from the containing group.
+ *
+ * @param int $guid The guid of the object.
+ *
+ * @return bool
+ */
+ public function removeObjectFromGroup($guid) {
+ return remove_object_from_group($this->getGUID(), $guid);
+ }
+
+ /**
+ * Returns an attribute or metadata.
+ *
+ * @see ElggEntity::get()
+ *
+ * @param string $name Name
+ *
+ * @return mixed
+ */
+ public function get($name) {
+ if ($name == 'username') {
+ return 'group:' . $this->getGUID();
+ }
+ return parent::get($name);
+ }
+
+ /**
+ * Start friendable compatibility block:
+ *
+ * public function addFriend($friend_guid);
+ public function removeFriend($friend_guid);
+ public function isFriend();
+ public function isFriendsWith($user_guid);
+ public function isFriendOf($user_guid);
+ public function getFriends($subtype = "", $limit = 10, $offset = 0);
+ public function getFriendsOf($subtype = "", $limit = 10, $offset = 0);
+ public function getObjects($subtype="", $limit = 10, $offset = 0);
+ public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0);
+ public function countObjects($subtype = "");
+ */
+
+ /**
+ * For compatibility with Friendable.
+ *
+ * Join a group when you friend ElggGroup.
+ *
+ * @param int $friend_guid The GUID of the user joining the group.
+ *
+ * @return bool
+ */
+ public function addFriend($friend_guid) {
+ return $this->join(get_entity($friend_guid));
+ }
+
+ /**
+ * For compatibility with Friendable
+ *
+ * Leave group when you unfriend ElggGroup.
+ *
+ * @param int $friend_guid The GUID of the user leaving.
+ *
+ * @return bool
+ */
+ public function removeFriend($friend_guid) {
+ return $this->leave(get_entity($friend_guid));
+ }
+
+ /**
+ * For compatibility with Friendable
+ *
+ * Friending a group adds you as a member
+ *
+ * @return bool
+ */
+ public function isFriend() {
+ return $this->isMember();
+ }
+
+ /**
+ * For compatibility with Friendable
+ *
+ * @param int $user_guid The GUID of a user to check.
+ *
+ * @return bool
+ */
+ public function isFriendsWith($user_guid) {
+ return $this->isMember($user_guid);
+ }
+
+ /**
+ * For compatibility with Friendable
+ *
+ * @param int $user_guid The GUID of a user to check.
+ *
+ * @return bool
+ */
+ public function isFriendOf($user_guid) {
+ return $this->isMember($user_guid);
+ }
+
+ /**
+ * For compatibility with Friendable
+ *
+ * @param string $subtype The GUID of a user to check.
+ * @param int $limit Limit
+ * @param int $offset Offset
+ *
+ * @return bool
+ */
+ public function getFriends($subtype = "", $limit = 10, $offset = 0) {
+ return get_group_members($this->getGUID(), $limit, $offset);
+ }
+
+ /**
+ * For compatibility with Friendable
+ *
+ * @param string $subtype The GUID of a user to check.
+ * @param int $limit Limit
+ * @param int $offset Offset
+ *
+ * @return bool
+ */
+ public function getFriendsOf($subtype = "", $limit = 10, $offset = 0) {
+ return get_group_members($this->getGUID(), $limit, $offset);
+ }
+
+ /**
+ * Get objects contained in this group.
+ *
+ * @param string $subtype Entity subtype
+ * @param int $limit Limit
+ * @param int $offset Offset
+ *
+ * @return array|false
+ */
+ public function getObjects($subtype = "", $limit = 10, $offset = 0) {
+ // @todo are we deprecating this method, too?
+ return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);
+ }
+
+ /**
+ * For compatibility with Friendable
+ *
+ * @param string $subtype Entity subtype
+ * @param int $limit Limit
+ * @param int $offset Offset
+ *
+ * @return array|false
+ */
+ public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) {
+ // @todo are we deprecating this method, too?
+ return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);
+ }
+
+ /**
+ * For compatibility with Friendable
+ *
+ * @param string $subtype Subtype of entities
+ *
+ * @return array|false
+ */
+ public function countObjects($subtype = "") {
+ // @todo are we deprecating this method, too?
+ return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", 10, 0, true);
+ }
+
+ /**
+ * End friendable compatibility block
+ */
+
+ /**
+ * Get a list of group members.
+ *
+ * @param int $limit Limit
+ * @param int $offset Offset
+ * @param bool $count Count
+ *
+ * @return mixed
+ */
+ public function getMembers($limit = 10, $offset = 0, $count = false) {
+ return get_group_members($this->getGUID(), $limit, $offset, 0, $count);
+ }
+
+ /**
+ * Returns whether the current group is public membership or not.
+ *
+ * @return bool
+ */
+ public function isPublicMembership() {
+ if ($this->membership == ACCESS_PUBLIC) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return whether a given user is a member of this group or not.
+ *
+ * @param ElggUser $user The user
+ *
+ * @return bool
+ */
+ public function isMember($user = null) {
+ if (!($user instanceof ElggUser)) {
+ $user = elgg_get_logged_in_user_entity();
+ }
+ if (!($user instanceof ElggUser)) {
+ return false;
+ }
+ return is_group_member($this->getGUID(), $user->getGUID());
+ }
+
+ /**
+ * Join an elgg user to this group.
+ *
+ * @param ElggUser $user User
+ *
+ * @return bool
+ */
+ public function join(ElggUser $user) {
+ return join_group($this->getGUID(), $user->getGUID());
+ }
+
+ /**
+ * Remove a user from the group.
+ *
+ * @param ElggUser $user User
+ *
+ * @return bool
+ */
+ public function leave(ElggUser $user) {
+ return leave_group($this->getGUID(), $user->getGUID());
+ }
+
+ /**
+ * Load the ElggGroup data from the database
+ *
+ * @param mixed $guid GUID of an ElggGroup entity or database row from entity table
+ *
+ * @return bool
+ */
+ protected function load($guid) {
+ $attr_loader = new ElggAttributeLoader(get_class(), 'group', $this->attributes);
+ $attr_loader->requires_access_control = !($this instanceof ElggPlugin);
+ $attr_loader->secondary_loader = 'get_group_entity_as_row';
+
+ $attrs = $attr_loader->getRequiredAttributes($guid);
+ if (!$attrs) {
+ return false;
+ }
+
+ $this->attributes = $attrs;
+ $this->attributes['tables_loaded'] = 2;
+ _elgg_cache_entity($this);
+
+ return true;
+ }
+
+ /**
+ * Override the save function.
+ *
+ * @return bool
+ */
+ public function save() {
+ // Save generic stuff
+ if (!parent::save()) {
+ return false;
+ }
+
+ // Now save specific stuff
+
+ _elgg_disable_caching_for_entity($this->guid);
+ $ret = create_group_entity($this->get('guid'), $this->get('name'), $this->get('description'));
+ _elgg_enable_caching_for_entity($this->guid);
+
+ return $ret;
+ }
+
+ // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
+
+ /**
+ * Return an array of fields which can be exported.
+ *
+ * @return array
+ */
+ public function getExportableValues() {
+ return array_merge(parent::getExportableValues(), array(
+ 'name',
+ 'description',
+ ));
+ }
+
+ /**
+ * Can a user comment on this group?
+ *
+ * @see ElggEntity::canComment()
+ *
+ * @param int $user_guid User guid (default is logged in user)
+ * @return bool
+ * @since 1.8.0
+ */
+ public function canComment($user_guid = 0) {
+ $result = parent::canComment($user_guid);
+ if ($result !== null) {
+ return $result;
+ }
+ return false;
+ }
+}
diff --git a/engine/classes/ElggGroupItemVisibility.php b/engine/classes/ElggGroupItemVisibility.php
new file mode 100644
index 000000000..2c7e2abb4
--- /dev/null
+++ b/engine/classes/ElggGroupItemVisibility.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * Determines if otherwise visible items should be hidden from a user due to group
+ * policy or visibility.
+ *
+ * @class ElggGroupItemVisibility
+ * @package Elgg.Core
+ * @subpackage Groups
+ *
+ * @access private
+ */
+class ElggGroupItemVisibility {
+
+ const REASON_MEMBERSHIP = 'membershiprequired';
+ const REASON_LOGGEDOUT = 'loggedinrequired';
+ const REASON_NOACCESS = 'noaccess';
+
+ /**
+ * @var bool
+ */
+ public $shouldHideItems = false;
+
+ /**
+ * @var string
+ */
+ public $reasonHidden = '';
+
+ /**
+ * Determine visibility of items within a container for the current user
+ *
+ * @param int $container_guid GUID of a container (may/may not be a group)
+ *
+ * @return ElggGroupItemVisibility
+ *
+ * @todo Make this faster, considering it must run for every river item.
+ */
+ static public function factory($container_guid) {
+ // cache because this may be called repeatedly during river display, and
+ // due to need to check group visibility, cache will be disabled for some
+ // get_entity() calls
+ static $cache = array();
+
+ $ret = new ElggGroupItemVisibility();
+
+ if (!$container_guid) {
+ return $ret;
+ }
+
+ $user = elgg_get_logged_in_user_entity();
+ $user_guid = $user ? $user->guid : 0;
+
+ $container_guid = (int) $container_guid;
+
+ $cache_key = "$container_guid|$user_guid";
+ if (empty($cache[$cache_key])) {
+ // compute
+
+ $container = get_entity($container_guid);
+ $is_visible = (bool) $container;
+
+ if (!$is_visible) {
+ // see if it *really* exists...
+ $prev_access = elgg_set_ignore_access();
+ $container = get_entity($container_guid);
+ elgg_set_ignore_access($prev_access);
+ }
+
+ if ($container && $container instanceof ElggGroup) {
+ /* @var ElggGroup $container */
+
+ if ($is_visible) {
+ if (!$container->isPublicMembership()) {
+ if ($user) {
+ if (!$container->isMember($user) && !$user->isAdmin()) {
+ $ret->shouldHideItems = true;
+ $ret->reasonHidden = self::REASON_MEMBERSHIP;
+ }
+ } else {
+ $ret->shouldHideItems = true;
+ $ret->reasonHidden = self::REASON_LOGGEDOUT;
+ }
+ }
+ } else {
+ $ret->shouldHideItems = true;
+ $ret->reasonHidden = self::REASON_NOACCESS;
+ }
+ }
+ $cache[$cache_key] = $ret;
+ }
+ return $cache[$cache_key];
+ }
+}
diff --git a/engine/classes/ElggHMACCache.php b/engine/classes/ElggHMACCache.php
new file mode 100644
index 000000000..c2f468815
--- /dev/null
+++ b/engine/classes/ElggHMACCache.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * ElggHMACCache
+ * Store cached data in a temporary database, only used by the HMAC stuff.
+ *
+ * @package Elgg.Core
+ * @subpackage HMAC
+ */
+class ElggHMACCache extends ElggCache {
+ /**
+ * Set the Elgg cache.
+ *
+ * @param int $max_age Maximum age in seconds, 0 if no limit.
+ */
+ function __construct($max_age = 0) {
+ $this->setVariable("max_age", $max_age);
+ }
+
+ /**
+ * Save a key
+ *
+ * @param string $key Name
+ * @param string $data Value
+ *
+ * @return boolean
+ */
+ public function save($key, $data) {
+ global $CONFIG;
+
+ $key = sanitise_string($key);
+ $time = time();
+
+ $query = "INSERT into {$CONFIG->dbprefix}hmac_cache (hmac, ts) VALUES ('$key', '$time')";
+ return insert_data($query);
+ }
+
+ /**
+ * Load a key
+ *
+ * @param string $key Name
+ * @param int $offset Offset
+ * @param int $limit Limit
+ *
+ * @return string
+ */
+ public function load($key, $offset = 0, $limit = null) {
+ global $CONFIG;
+
+ $key = sanitise_string($key);
+
+ $row = get_data_row("SELECT * from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
+ if ($row) {
+ return $row->hmac;
+ }
+
+ return false;
+ }
+
+ /**
+ * Invalidate a given key.
+ *
+ * @param string $key Name
+ *
+ * @return bool
+ */
+ public function delete($key) {
+ global $CONFIG;
+
+ $key = sanitise_string($key);
+
+ return delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
+ }
+
+ /**
+ * Clear out all the contents of the cache.
+ *
+ * Not currently implemented in this cache type.
+ *
+ * @return true
+ */
+ public function clear() {
+ return true;
+ }
+
+ /**
+ * Clean out old stuff.
+ *
+ */
+ public function __destruct() {
+ global $CONFIG;
+
+ $time = time();
+ $age = (int)$this->getVariable("max_age");
+
+ $expires = $time - $age;
+
+ delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where ts<$expires");
+ }
+}
diff --git a/engine/classes/ElggLRUCache.php b/engine/classes/ElggLRUCache.php
new file mode 100644
index 000000000..f51af2ed7
--- /dev/null
+++ b/engine/classes/ElggLRUCache.php
@@ -0,0 +1,181 @@
+<?php
+
+/**
+ * Least Recently Used Cache
+ *
+ * A fixed sized cache that removes the element used last when it reaches its
+ * size limit.
+ *
+ * Based on https://github.com/cash/LRUCache
+ *
+ * @access private
+ *
+ * @package Elgg.Core
+ * @subpackage Cache
+ */
+class ElggLRUCache implements ArrayAccess {
+ /** @var int */
+ protected $maximumSize;
+
+ /**
+ * The front of the array contains the LRU element
+ *
+ * @var array
+ */
+ protected $data = array();
+
+ /**
+ * Create a LRU Cache
+ *
+ * @param int $size The size of the cache
+ * @throws InvalidArgumentException
+ */
+ public function __construct($size) {
+ if (!is_int($size) || $size <= 0) {
+ throw new InvalidArgumentException();
+ }
+ $this->maximumSize = $size;
+ }
+
+ /**
+ * Get the value cached with this key
+ *
+ * @param int|string $key The key. Strings that are ints are cast to ints.
+ * @param mixed $default The value to be returned if key not found. (Optional)
+ * @return mixed
+ */
+ public function get($key, $default = null) {
+ if (isset($this->data[$key])) {
+ $this->recordAccess($key);
+ return $this->data[$key];
+ } else {
+ return $default;
+ }
+ }
+
+ /**
+ * Add something to the cache
+ *
+ * @param int|string $key The key. Strings that are ints are cast to ints.
+ * @param mixed $value The value to cache
+ * @return void
+ */
+ public function set($key, $value) {
+ if (isset($this->data[$key])) {
+ $this->data[$key] = $value;
+ $this->recordAccess($key);
+ } else {
+ $this->data[$key] = $value;
+ if ($this->size() > $this->maximumSize) {
+ // remove least recently used element (front of array)
+ reset($this->data);
+ unset($this->data[key($this->data)]);
+ }
+ }
+ }
+
+ /**
+ * Get the number of elements in the cache
+ *
+ * @return int
+ */
+ public function size() {
+ return count($this->data);
+ }
+
+ /**
+ * Does the cache contain an element with this key
+ *
+ * @param int|string $key The key
+ * @return boolean
+ */
+ public function containsKey($key) {
+ return isset($this->data[$key]);
+ }
+
+ /**
+ * Remove the element with this key.
+ *
+ * @param int|string $key The key
+ * @return mixed Value or null if not set
+ */
+ public function remove($key) {
+ if (isset($this->data[$key])) {
+ $value = $this->data[$key];
+ unset($this->data[$key]);
+ return $value;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Clear the cache
+ *
+ * @return void
+ */
+ public function clear() {
+ $this->data = array();
+ }
+
+ /**
+ * Moves the element from current position to end of array
+ *
+ * @param int|string $key The key
+ * @return void
+ */
+ protected function recordAccess($key) {
+ $value = $this->data[$key];
+ unset($this->data[$key]);
+ $this->data[$key] = $value;
+ }
+
+ /**
+ * Assigns a value for the specified key
+ *
+ * @see ArrayAccess::offsetSet()
+ *
+ * @param int|string $key The key to assign the value to.
+ * @param mixed $value The value to set.
+ * @return void
+ */
+ public function offsetSet($key, $value) {
+ $this->set($key, $value);
+ }
+
+ /**
+ * Get the value for specified key
+ *
+ * @see ArrayAccess::offsetGet()
+ *
+ * @param int|string $key The key to retrieve.
+ * @return mixed
+ */
+ public function offsetGet($key) {
+ return $this->get($key);
+ }
+
+ /**
+ * Unsets a key.
+ *
+ * @see ArrayAccess::offsetUnset()
+ *
+ * @param int|string $key The key to unset.
+ * @return void
+ */
+ public function offsetUnset($key) {
+ $this->remove($key);
+ }
+
+ /**
+ * Does key exist?
+ *
+ * @see ArrayAccess::offsetExists()
+ *
+ * @param int|string $key A key to check for.
+ * @return boolean
+ */
+ public function offsetExists($key) {
+ return $this->containsKey($key);
+ }
+}
diff --git a/engine/classes/ElggMemcache.php b/engine/classes/ElggMemcache.php
new file mode 100644
index 000000000..91d50ab89
--- /dev/null
+++ b/engine/classes/ElggMemcache.php
@@ -0,0 +1,203 @@
+<?php
+/**
+ * Memcache wrapper class.
+ *
+ * @package Elgg.Core
+ * @subpackage Memcache
+ */
+class ElggMemcache extends ElggSharedMemoryCache {
+ /**
+ * Minimum version of memcached needed to run
+ *
+ */
+ private static $MINSERVERVERSION = '1.1.12';
+
+ /**
+ * Memcache object
+ */
+ private $memcache;
+
+ /**
+ * Expiry of saved items (default timeout after a day to prevent anything getting too stale)
+ */
+ private $expires = 86400;
+
+ /**
+ * The version of memcache running
+ */
+ private $version = 0;
+
+ /**
+ * Connect to memcache.
+ *
+ * @param string $namespace The namespace for this cache to write to -
+ * note, namespaces of the same name are shared!
+ *
+ * @throws ConfigurationException
+ */
+ function __construct($namespace = 'default') {
+ global $CONFIG;
+
+ $this->setNamespace($namespace);
+
+ // Do we have memcache?
+ if (!class_exists('Memcache')) {
+ throw new ConfigurationException('PHP memcache module not installed, you must install php5-memcache');
+ }
+
+ // Create memcache object
+ $this->memcache = new Memcache;
+
+ // Now add servers
+ if (!$CONFIG->memcache_servers) {
+ throw new ConfigurationException('No memcache servers defined, please populate the $CONFIG->memcache_servers variable');
+ }
+
+ if (is_callable(array($this->memcache, 'addServer'))) {
+ foreach ($CONFIG->memcache_servers as $server) {
+ if (is_array($server)) {
+ $this->memcache->addServer(
+ $server[0],
+ isset($server[1]) ? $server[1] : 11211,
+ isset($server[2]) ? $server[2] : FALSE,
+ isset($server[3]) ? $server[3] : 1,
+ isset($server[4]) ? $server[4] : 1,
+ isset($server[5]) ? $server[5] : 15,
+ isset($server[6]) ? $server[6] : TRUE
+ );
+
+ } else {
+ $this->memcache->addServer($server, 11211);
+ }
+ }
+ } else {
+ // don't use elgg_echo() here because most of the config hasn't been loaded yet
+ // and it caches the language, which is hard coded in $CONFIG->language as en.
+ // overriding it with real values later has no effect because it's already cached.
+ elgg_log("This version of the PHP memcache API doesn't support multiple servers.", 'ERROR');
+
+ $server = $CONFIG->memcache_servers[0];
+ if (is_array($server)) {
+ $this->memcache->connect($server[0], $server[1]);
+ } else {
+ $this->memcache->addServer($server, 11211);
+ }
+ }
+
+ // Get version
+ $this->version = $this->memcache->getVersion();
+ if (version_compare($this->version, ElggMemcache::$MINSERVERVERSION, '<')) {
+ $msg = vsprintf('Memcache needs at least version %s to run, you are running %s',
+ array(ElggMemcache::$MINSERVERVERSION,
+ $this->version
+ ));
+
+ throw new ConfigurationException($msg);
+ }
+
+ // Set some defaults
+ if (isset($CONFIG->memcache_expires)) {
+ $this->expires = $CONFIG->memcache_expires;
+ }
+ }
+
+ /**
+ * Set the default expiry.
+ *
+ * @param int $expires The lifetime as a unix timestamp or time from now. Defaults forever.
+ *
+ * @return void
+ */
+ public function setDefaultExpiry($expires = 0) {
+ $this->expires = $expires;
+ }
+
+ /**
+ * Combine a key with the namespace.
+ * Memcache can only accept <250 char key. If the given key is too long it is shortened.
+ *
+ * @param string $key The key
+ *
+ * @return string The new key.
+ */
+ private function makeMemcacheKey($key) {
+ $prefix = $this->getNamespace() . ":";
+
+ if (strlen($prefix . $key) > 250) {
+ $key = md5($key);
+ }
+
+ return $prefix . $key;
+ }
+
+ /**
+ * Saves a name and value to the cache
+ *
+ * @param string $key Name
+ * @param string $data Value
+ * @param integer $expires Expires (in seconds)
+ *
+ * @return bool
+ */
+ public function save($key, $data, $expires = null) {
+ $key = $this->makeMemcacheKey($key);
+
+ if ($expires === null) {
+ $expires = $this->expires;
+ }
+
+ $result = $this->memcache->set($key, $data, null, $expires);
+ if ($result === false) {
+ elgg_log("MEMCACHE: FAILED TO SAVE $key", 'ERROR');
+ }
+
+ return $result;
+ }
+
+ /**
+ * Retrieves data.
+ *
+ * @param string $key Name of data to retrieve
+ * @param int $offset Offset
+ * @param int $limit Limit
+ *
+ * @return mixed
+ */
+ public function load($key, $offset = 0, $limit = null) {
+ $key = $this->makeMemcacheKey($key);
+
+ $result = $this->memcache->get($key);
+ if ($result === false) {
+ elgg_log("MEMCACHE: FAILED TO LOAD $key", 'ERROR');
+ }
+
+ return $result;
+ }
+
+ /**
+ * Delete data
+ *
+ * @param string $key Name of data
+ *
+ * @return bool
+ */
+ public function delete($key) {
+ $key = $this->makeMemcacheKey($key);
+
+ return $this->memcache->delete($key, 0);
+ }
+
+ /**
+ * Clears the entire cache?
+ *
+ * @todo write or remove.
+ *
+ * @return true
+ */
+ public function clear() {
+ // DISABLE clearing for now - you must use delete on a specific key.
+ return true;
+
+ // @todo Namespaces as in #532
+ }
+}
diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php
new file mode 100644
index 000000000..b463143d8
--- /dev/null
+++ b/engine/classes/ElggMenuBuilder.php
@@ -0,0 +1,291 @@
+<?php
+/**
+ * Elgg Menu Builder
+ *
+ * @package Elgg.Core
+ * @subpackage Navigation
+ * @since 1.8.0
+ */
+class ElggMenuBuilder {
+
+ /**
+ * @var ElggMenuItem[]
+ */
+ protected $menu = array();
+
+ protected $selected = null;
+
+ /**
+ * ElggMenuBuilder constructor
+ *
+ * @param ElggMenuItem[] $menu Array of ElggMenuItem objects
+ */
+ public function __construct(array $menu) {
+ $this->menu = $menu;
+ }
+
+ /**
+ * Get a prepared menu array
+ *
+ * @param mixed $sort_by Method to sort the menu by. @see ElggMenuBuilder::sort()
+ * @return array
+ */
+ public function getMenu($sort_by = 'text') {
+
+ $this->selectFromContext();
+
+ $this->selected = $this->findSelected();
+
+ $this->setupSections();
+
+ $this->setupTrees();
+
+ $this->sort($sort_by);
+
+ return $this->menu;
+ }
+
+ /**
+ * Get the selected menu item
+ *
+ * @return ElggMenuItem
+ */
+ public function getSelected() {
+ return $this->selected;
+ }
+
+ /**
+ * Select menu items for the current context
+ *
+ * @return void
+ */
+ protected function selectFromContext() {
+ if (!isset($this->menu)) {
+ $this->menu = array();
+ return;
+ }
+
+ // get menu items for this context
+ $selected_menu = array();
+ foreach ($this->menu as $menu_item) {
+ if (!is_object($menu_item)) {
+ elgg_log("A non-object was passed to ElggMenuBuilder", "ERROR");
+ continue;
+ }
+ if ($menu_item->inContext()) {
+ $selected_menu[] = $menu_item;
+ }
+ }
+
+ $this->menu = $selected_menu;
+ }
+
+ /**
+ * Group the menu items into sections
+ *
+ * @return void
+ */
+ protected function setupSections() {
+ $sectioned_menu = array();
+ foreach ($this->menu as $menu_item) {
+ if (!isset($sectioned_menu[$menu_item->getSection()])) {
+ $sectioned_menu[$menu_item->getSection()] = array();
+ }
+ $sectioned_menu[$menu_item->getSection()][] = $menu_item;
+ }
+ $this->menu = $sectioned_menu;
+ }
+
+ /**
+ * Create trees for each menu section
+ *
+ * @internal The tree is doubly linked (parent and children links)
+ * @return void
+ */
+ protected function setupTrees() {
+ $menu_tree = array();
+
+ foreach ($this->menu as $key => $section) {
+ $parents = array();
+ $children = array();
+ // divide base nodes from children
+ foreach ($section as $menu_item) {
+ /* @var ElggMenuItem $menu_item */
+ $parent_name = $menu_item->getParentName();
+ if (!$parent_name) {
+ $parents[$menu_item->getName()] = $menu_item;
+ } else {
+ $children[] = $menu_item;
+ }
+ }
+
+ // attach children to parents
+ $iteration = 0;
+ $current_gen = $parents;
+ $next_gen = null;
+ while (count($children) && $iteration < 5) {
+ foreach ($children as $index => $menu_item) {
+ $parent_name = $menu_item->getParentName();
+ if (array_key_exists($parent_name, $current_gen)) {
+ $next_gen[$menu_item->getName()] = $menu_item;
+ if (!in_array($menu_item, $current_gen[$parent_name]->getData('children'))) {
+ $current_gen[$parent_name]->addChild($menu_item);
+ $menu_item->setParent($current_gen[$parent_name]);
+ }
+ unset($children[$index]);
+ }
+ }
+ $current_gen = $next_gen;
+ $iteration += 1;
+ }
+
+ // convert keys to indexes for first level of tree
+ $parents = array_values($parents);
+
+ $menu_tree[$key] = $parents;
+ }
+
+ $this->menu = $menu_tree;
+ }
+
+ /**
+ * Find the menu item that is currently selected
+ *
+ * @return ElggMenuItem
+ */
+ protected function findSelected() {
+
+ // do we have a selected menu item already
+ foreach ($this->menu as $menu_item) {
+ if ($menu_item->getSelected()) {
+ return $menu_item;
+ }
+ }
+
+ // scan looking for a selected item
+ foreach ($this->menu as $menu_item) {
+ if ($menu_item->getHref()) {
+ if (elgg_http_url_is_identical(current_page_url(), $menu_item->getHref())) {
+ $menu_item->setSelected(true);
+ return $menu_item;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Sort the menu sections and trees
+ *
+ * @param mixed $sort_by Sort type as string or php callback
+ * @return void
+ */
+ protected function sort($sort_by) {
+
+ // sort sections
+ ksort($this->menu);
+
+ switch ($sort_by) {
+ case 'text':
+ $sort_callback = array('ElggMenuBuilder', 'compareByText');
+ break;
+ case 'name':
+ $sort_callback = array('ElggMenuBuilder', 'compareByName');
+ break;
+ case 'priority':
+ $sort_callback = array('ElggMenuBuilder', 'compareByWeight');
+ break;
+ case 'register':
+ // use registration order - usort breaks this
+ return;
+ break;
+ default:
+ if (is_callable($sort_by)) {
+ $sort_callback = $sort_by;
+ } else {
+ return;
+ }
+ break;
+ }
+
+ // sort each section
+ foreach ($this->menu as $index => $section) {
+ foreach ($section as $key => $node) {
+ $section[$key]->setData('original_order', $key);
+ }
+ usort($section, $sort_callback);
+ $this->menu[$index] = $section;
+
+ // depth first traversal of tree
+ foreach ($section as $root) {
+ $stack = array();
+ array_push($stack, $root);
+ while (!empty($stack)) {
+ $node = array_pop($stack);
+ /* @var ElggMenuItem $node */
+ $node->sortChildren($sort_callback);
+ $children = $node->getChildren();
+ if ($children) {
+ $stack = array_merge($stack, $children);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Compare two menu items by their display text
+ *
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
+ * @return bool
+ */
+ public static function compareByText($a, $b) {
+ $at = $a->getText();
+ $bt = $b->getText();
+
+ $result = strnatcmp($at, $bt);
+ if ($result === 0) {
+ return $a->getData('original_order') - $b->getData('original_order');
+ }
+ return $result;
+ }
+
+ /**
+ * Compare two menu items by their identifiers
+ *
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
+ * @return bool
+ */
+ public static function compareByName($a, $b) {
+ $an = $a->getName();
+ $bn = $b->getName();
+
+ $result = strcmp($an, $bn);
+ if ($result === 0) {
+ return $a->getData('original_order') - $b->getData('original_order');
+ }
+ return $result;
+ }
+
+ /**
+ * Compare two menu items by their priority
+ *
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
+ * @return bool
+ *
+ * @todo change name to compareByPriority
+ */
+ public static function compareByWeight($a, $b) {
+ $aw = $a->getWeight();
+ $bw = $b->getWeight();
+
+ if ($aw == $bw) {
+ return $a->getData('original_order') - $b->getData('original_order');
+ }
+ return $aw - $bw;
+ }
+}
diff --git a/engine/classes/ElggMenuItem.php b/engine/classes/ElggMenuItem.php
new file mode 100644
index 000000000..81ce6c099
--- /dev/null
+++ b/engine/classes/ElggMenuItem.php
@@ -0,0 +1,590 @@
+<?php
+/**
+ * Elgg Menu Item
+ *
+ * To create a menu item that is not a link, pass false for $href.
+ *
+ * @package Elgg.Core
+ * @subpackage Navigation
+ * @since 1.8.0
+ */
+class ElggMenuItem {
+
+ /**
+ * @var array Non-rendered data about the menu item
+ */
+ protected $data = array(
+ // string Identifier of the menu
+ 'name' => '',
+
+ // array Page contexts this menu item should appear on
+ 'contexts' => array('all'),
+
+ // string Menu section identifier
+ 'section' => 'default',
+
+ // int Smaller priorities float to the top
+ 'priority' => 100,
+
+ // bool Is this the currently selected menu item
+ 'selected' => false,
+
+ // string Identifier of this item's parent
+ 'parent_name' => '',
+
+ // ElggMenuItem The parent object or null
+ 'parent' => null,
+
+ // array Array of children objects or empty array
+ 'children' => array(),
+
+ // array Classes to apply to the li tag
+ 'itemClass' => array(),
+
+ // array Classes to apply to the anchor tag
+ 'linkClass' => array(),
+ );
+
+ /**
+ * @var string The menu display string
+ */
+ protected $text;
+
+ /**
+ * @var string The menu url
+ */
+ protected $href = null;
+
+ /**
+ * @var string Tooltip
+ */
+ protected $title = false;
+
+ /**
+ * @var string The string to display if link is clicked
+ */
+ protected $confirm = '';
+
+
+ /**
+ * ElggMenuItem constructor
+ *
+ * @param string $name Identifier of the menu item
+ * @param string $text Display text of the menu item
+ * @param string $href URL of the menu item (false if not a link)
+ */
+ public function __construct($name, $text, $href) {
+ //$this->name = $name;
+ $this->text = $text;
+ if ($href) {
+ $this->href = elgg_normalize_url($href);
+ } else {
+ $this->href = $href;
+ }
+
+ $this->data['name'] = $name;
+ }
+
+ /**
+ * ElggMenuItem factory method
+ *
+ * This static method creates an ElggMenuItem from an associative array.
+ * Required keys are name, text, and href.
+ *
+ * @param array $options Option array of key value pairs
+ *
+ * @return ElggMenuItem or NULL on error
+ */
+ public static function factory($options) {
+ if (!isset($options['name']) || !isset($options['text'])) {
+ return NULL;
+ }
+ if (!isset($options['href'])) {
+ $options['href'] = '';
+ }
+
+ $item = new ElggMenuItem($options['name'], $options['text'], $options['href']);
+ unset($options['name']);
+ unset($options['text']);
+ unset($options['href']);
+
+ // special catch in case someone uses context rather than contexts
+ if (isset($options['context'])) {
+ $options['contexts'] = $options['context'];
+ unset($options['context']);
+ }
+
+ // make sure contexts is set correctly
+ if (isset($options['contexts'])) {
+ $item->setContext($options['contexts']);
+ unset($options['contexts']);
+ }
+
+ if (isset($options['link_class'])) {
+ $item->setLinkClass($options['link_class']);
+ unset($options['link_class']);
+ }
+
+ if (isset($options['item_class'])) {
+ $item->setItemClass($options['item_class']);
+ unset($options['item_class']);
+ }
+
+ if (isset($options['data']) && is_array($options['data'])) {
+ $item->setData($options['data']);
+ unset($options['data']);
+ }
+
+ foreach ($options as $key => $value) {
+ if (isset($item->data[$key])) {
+ $item->data[$key] = $value;
+ } else {
+ $item->$key = $value;
+ }
+ }
+
+ return $item;
+ }
+
+ /**
+ * Set a data key/value pair or a set of key/value pairs
+ *
+ * This method allows storage of arbitrary data with this menu item. The
+ * data can be used for sorting, custom rendering, or any other use.
+ *
+ * @param mixed $key String key or an associative array of key/value pairs
+ * @param mixed $value The value if $key is a string
+ * @return void
+ */
+ public function setData($key, $value = null) {
+ if (is_array($key)) {
+ $this->data += $key;
+ } else {
+ $this->data[$key] = $value;
+ }
+ }
+
+ /**
+ * Get stored data
+ *
+ * @param string $key The key for the requested key/value pair
+ * @return mixed
+ */
+ public function getData($key) {
+ if (isset($this->data[$key])) {
+ return $this->data[$key];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set the identifier of the menu item
+ *
+ * @param string $name Unique identifier
+ * @return void
+ */
+ public function setName($name) {
+ $this->data['name'] = $name;
+ }
+
+ /**
+ * Get the identifier of the menu item
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->data['name'];
+ }
+
+ /**
+ * Set the display text of the menu item
+ *
+ * @param string $text The display text
+ * @return void
+ */
+ public function setText($text) {
+ $this->text = $text;
+ }
+
+ /**
+ * Get the display text of the menu item
+ *
+ * @return string
+ */
+ public function getText() {
+ return $this->text;
+ }
+
+ /**
+ * Set the URL of the menu item
+ *
+ * @param string $href URL or false if not a link
+ * @return void
+ */
+ public function setHref($href) {
+ $this->href = $href;
+ }
+
+ /**
+ * Get the URL of the menu item
+ *
+ * @return string
+ */
+ public function getHref() {
+ return $this->href;
+ }
+
+ /**
+ * Set the contexts that this menu item is available for
+ *
+ * @param array $contexts An array of context strings
+ * @return void
+ */
+ public function setContext($contexts) {
+ if (is_string($contexts)) {
+ $contexts = array($contexts);
+ }
+ $this->data['contexts'] = $contexts;
+ }
+
+ /**
+ * Get an array of context strings
+ *
+ * @return array
+ */
+ public function getContext() {
+ return $this->data['contexts'];
+ }
+
+ /**
+ * Should this menu item be used given the current context
+ *
+ * @param string $context A context string (default is empty string for
+ * current context stack).
+ * @return bool
+ */
+ public function inContext($context = '') {
+ if ($context) {
+ return in_array($context, $this->data['contexts']);
+ }
+
+ if (in_array('all', $this->data['contexts'])) {
+ return true;
+ }
+
+ foreach ($this->data['contexts'] as $context) {
+ if (elgg_in_context($context)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Set the selected flag
+ *
+ * @param bool $state Selected state (default is true)
+ * @return void
+ */
+ public function setSelected($state = true) {
+ $this->data['selected'] = $state;
+ }
+
+ /**
+ * Get selected state
+ *
+ * @return bool
+ */
+ public function getSelected() {
+ return $this->data['selected'];
+ }
+
+ /**
+ * Set the tool tip text
+ *
+ * @param string $text The text of the tool tip
+ * @return void
+ */
+ public function setTooltip($text) {
+ $this->title = $text;
+ }
+
+ /**
+ * Get the tool tip text
+ *
+ * @return string
+ */
+ public function getTooltip() {
+ return $this->title;
+ }
+
+ /**
+ * Set the confirm text shown when link is clicked
+ *
+ * @param string $text The text to show
+ * @return void
+ */
+ public function setConfirmText($text) {
+ $this->confirm = $text;
+ }
+
+ /**
+ * Get the confirm text
+ *
+ * @return string
+ */
+ public function getConfirmText() {
+ return $this->confirm;
+ }
+
+ /**
+ * Set the anchor class
+ *
+ * @param mixed $class An array of class names, or a single string class name.
+ * @return void
+ */
+ public function setLinkClass($class) {
+ if (!is_array($class)) {
+ $this->data['linkClass'] = array($class);
+ } else {
+ $this->data['linkClass'] = $class;
+ }
+ }
+
+ /**
+ * Get the anchor classes as text
+ *
+ * @return string
+ */
+ public function getLinkClass() {
+ return implode(' ', $this->data['linkClass']);
+ }
+
+ /**
+ * Add a link class
+ *
+ * @param mixed $class An array of class names, or a single string class name.
+ * @return void
+ */
+ public function addLinkClass($class) {
+ if (!is_array($class)) {
+ $this->data['linkClass'][] = $class;
+ } else {
+ $this->data['linkClass'] += $class;
+ }
+ }
+
+ /**
+ * Set the li classes
+ *
+ * @param mixed $class An array of class names, or a single string class name.
+ * @return void
+ */
+ public function setItemClass($class) {
+ if (!is_array($class)) {
+ $this->data['itemClass'] = array($class);
+ } else {
+ $this->data['itemClass'] = $class;
+ }
+ }
+
+ /**
+ * Get the li classes as text
+ *
+ * @return string
+ */
+ public function getItemClass() {
+ // allow people to specify name with underscores and colons
+ $name = strtolower($this->getName());
+ $name = str_replace('_', '-', $name);
+ $name = str_replace(':', '-', $name);
+ $name = str_replace(' ', '-', $name);
+
+ $class = implode(' ', $this->data['itemClass']);
+ if ($class) {
+ return "elgg-menu-item-$name $class";
+ } else {
+ return "elgg-menu-item-$name";
+ }
+ }
+
+ /**
+ * Set the priority of the menu item
+ *
+ * @param int $priority The smaller numbers mean higher priority (1 before 100)
+ * @return void
+ * @deprecated
+ */
+ public function setWeight($priority) {
+ $this->data['priority'] = $priority;
+ }
+
+ /**
+ * Get the priority of the menu item
+ *
+ * @return int
+ * @deprecated
+ */
+ public function getWeight() {
+ return $this->data['priority'];
+ }
+
+ /**
+ * Set the priority of the menu item
+ *
+ * @param int $priority The smaller numbers mean higher priority (1 before 100)
+ * @return void
+ */
+ public function setPriority($priority) {
+ $this->data['priority'] = $priority;
+ }
+
+ /**
+ * Get the priority of the menu item
+ *
+ * @return int
+ */
+ public function getPriority() {
+ return $this->data['priority'];
+ }
+
+ /**
+ * Set the section identifier
+ *
+ * @param string $section The identifier of the section
+ * @return void
+ */
+ public function setSection($section) {
+ $this->data['section'] = $section;
+ }
+
+ /**
+ * Get the section identifier
+ *
+ * @return string
+ */
+ public function getSection() {
+ return $this->data['section'];
+ }
+
+ /**
+ * Set the parent identifier
+ *
+ * @param string $name The identifier of the parent ElggMenuItem
+ * @return void
+ */
+ public function setParentName($name) {
+ $this->data['parent_name'] = $name;
+ }
+
+ /**
+ * Get the parent identifier
+ *
+ * @return string
+ */
+ public function getParentName() {
+ return $this->data['parent_name'];
+ }
+
+ /**
+ * Set the parent menu item
+ *
+ * @param ElggMenuItem $parent The parent of this menu item
+ * @return void
+ */
+ public function setParent($parent) {
+ $this->data['parent'] = $parent;
+ }
+
+ /**
+ * Get the parent menu item
+ *
+ * @return ElggMenuItem or null
+ */
+ public function getParent() {
+ return $this->data['parent'];
+ }
+
+ /**
+ * Add a child menu item
+ *
+ * @param ElggMenuItem $item A child menu item
+ * @return void
+ */
+ public function addChild($item) {
+ $this->data['children'][] = $item;
+ }
+
+ /**
+ * Set the menu item's children
+ *
+ * @param array $children Array of ElggMenuItems
+ * @return void
+ */
+ public function setChildren($children) {
+ $this->data['children'] = $children;
+ }
+
+ /**
+ * Get the children menu items
+ *
+ * @return array
+ */
+ public function getChildren() {
+ return $this->data['children'];
+ }
+
+ /**
+ * Sort the children
+ *
+ * @param string $sortFunction A function that is passed to usort()
+ * @return void
+ */
+ public function sortChildren($sortFunction) {
+ foreach ($this->data['children'] as $key => $node) {
+ $this->data['children'][$key]->data['original_order'] = $key;
+ }
+ usort($this->data['children'], $sortFunction);
+ }
+
+ /**
+ * Get the menu item content (usually a link)
+ *
+ * @param array $vars Options to pass to output/url if a link
+ * @return string
+ * @todo View code in a model. How do we feel about that?
+ */
+ public function getContent(array $vars = array()) {
+
+ if ($this->href === false) {
+ return $this->text;
+ }
+
+ $defaults = get_object_vars($this);
+ unset($defaults['data']);
+
+ $vars += $defaults;
+
+ if ($this->data['linkClass']) {
+ if (isset($vars['class'])) {
+ $vars['class'] = $vars['class'] . ' ' . $this->getLinkClass();
+ } else {
+ $vars['class'] = $this->getLinkClass();
+ }
+ }
+
+ if (!isset($vars['rel']) && !isset($vars['is_trusted'])) {
+ $vars['is_trusted'] = true;
+ }
+
+ if ($this->confirm) {
+ $vars['confirm'] = $this->confirm;
+ return elgg_view('output/confirmlink', $vars);
+ } else {
+ unset($vars['confirm']);
+ }
+
+ return elgg_view('output/url', $vars);
+ }
+}
diff --git a/engine/classes/ElggMetadata.php b/engine/classes/ElggMetadata.php
new file mode 100644
index 000000000..3a8e2d817
--- /dev/null
+++ b/engine/classes/ElggMetadata.php
@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * ElggMetadata
+ * This class describes metadata that can be attached to ElggEntities.
+ *
+ * @package Elgg.Core
+ * @subpackage Metadata
+ *
+ * @property string $value_type
+ * @property int $owner_guid
+ * @property string $enabled
+ */
+class ElggMetadata extends ElggExtender {
+
+ /**
+ * (non-PHPdoc)
+ *
+ * @see ElggData::initializeAttributes()
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['type'] = "metadata";
+ }
+
+ /**
+ * Construct a metadata object
+ *
+ * @param mixed $id ID of metadata or a database row as stdClass object
+ */
+ function __construct($id = null) {
+ $this->initializeAttributes();
+
+ if (!empty($id)) {
+ // Create from db row
+ if ($id instanceof stdClass) {
+ $metadata = $id;
+
+ $objarray = (array) $metadata;
+ foreach ($objarray as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+ } else {
+ // get an ElggMetadata object and copy its attributes
+ $metadata = elgg_get_metadata_from_id($id);
+ $this->attributes = $metadata->attributes;
+ }
+ }
+ }
+
+ /**
+ * Determines whether or not the user can edit this piece of metadata
+ *
+ * @param int $user_guid The GUID of the user (defaults to currently logged in user)
+ *
+ * @return bool Depending on permissions
+ */
+ function canEdit($user_guid = 0) {
+ if ($entity = get_entity($this->get('entity_guid'))) {
+ return $entity->canEditMetadata($this, $user_guid);
+ }
+ return false;
+ }
+
+ /**
+ * Save metadata object
+ *
+ * @return int|bool the metadata object id or true if updated
+ *
+ * @throws IOException
+ */
+ function save() {
+ if ($this->id > 0) {
+ return update_metadata($this->id, $this->name, $this->value,
+ $this->value_type, $this->owner_guid, $this->access_id);
+ } else {
+ $this->id = create_metadata($this->entity_guid, $this->name, $this->value,
+ $this->value_type, $this->owner_guid, $this->access_id);
+
+ if (!$this->id) {
+ throw new IOException(elgg_echo('IOException:UnableToSaveNew', array(get_class())));
+ }
+ return $this->id;
+ }
+ }
+
+ /**
+ * Delete the metadata
+ *
+ * @return bool
+ */
+ function delete() {
+ $success = elgg_delete_metastring_based_object_by_id($this->id, 'metadata');
+ if ($success) {
+ // we mark unknown here because this deletes only one value
+ // under this name, and there may be others remaining.
+ elgg_get_metadata_cache()->markUnknown($this->entity_guid, $this->name);
+ }
+ return $success;
+ }
+
+ /**
+ * Disable the metadata
+ *
+ * @return bool
+ * @since 1.8
+ */
+ function disable() {
+ $success = elgg_set_metastring_based_object_enabled_by_id($this->id, 'no', 'metadata');
+ if ($success) {
+ // we mark unknown here because this disables only one value
+ // under this name, and there may be others remaining.
+ elgg_get_metadata_cache()->markUnknown($this->entity_guid, $this->name);
+ }
+ return $success;
+ }
+
+ /**
+ * Enable the metadata
+ *
+ * @return bool
+ * @since 1.8
+ */
+ function enable() {
+ $success = elgg_set_metastring_based_object_enabled_by_id($this->id, 'yes', 'metadata');
+ if ($success) {
+ elgg_get_metadata_cache()->markUnknown($this->entity_guid, $this->name);
+ }
+ return $success;
+ }
+
+ /**
+ * Get a url for this item of metadata.
+ *
+ * @return string
+ */
+ public function getURL() {
+ return get_metadata_url($this->id);
+ }
+
+ // SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
+
+ /**
+ * For a given ID, return the object associated with it.
+ * This is used by the river functionality primarily.
+ * This is useful for checking access permissions etc on objects.
+ *
+ * @param int $id Metadata ID
+ *
+ * @return ElggMetadata
+ */
+ public function getObjectFromID($id) {
+ return elgg_get_metadata_from_id($id);
+ }
+}
diff --git a/engine/classes/ElggObject.php b/engine/classes/ElggObject.php
new file mode 100644
index 000000000..aeaa3ba5c
--- /dev/null
+++ b/engine/classes/ElggObject.php
@@ -0,0 +1,216 @@
+<?php
+/**
+ * Elgg Object
+ *
+ * Elgg objects are the most common means of storing information in the database.
+ * They are a child class of ElggEntity, so receive all the benefits of the Entities,
+ * but also include a title and description field.
+ *
+ * An ElggObject represents a row from the objects_entity table, as well
+ * as the related row in the entities table as represented by the parent
+ * ElggEntity object.
+ *
+ * @internal Title and description are stored in the objects_entity table.
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel.Object
+ *
+ * @property string $title The title, name, or summary of this object
+ * @property string $description The body, description, or content of the object
+ * @property array $tags Array of tags that describe the object
+ */
+class ElggObject extends ElggEntity {
+
+ /**
+ * Initialise the attributes array to include the type,
+ * title, and description.
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['type'] = "object";
+ $this->attributes['title'] = NULL;
+ $this->attributes['description'] = NULL;
+ $this->attributes['tables_split'] = 2;
+ }
+
+ /**
+ * Load or create a new ElggObject.
+ *
+ * If no arguments are passed, create a new entity.
+ *
+ * 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 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
+ * load the rest of the data.
+ *
+ * @throws IOException If passed an incorrect guid
+ * @throws InvalidParameterException If passed an Elgg* Entity that isn't an ElggObject
+ */
+ function __construct($guid = null) {
+ $this->initializeAttributes();
+
+ // compatibility for 1.7 api.
+ $this->initialise_attributes(false);
+
+ if (!empty($guid)) {
+ // Is $guid is a DB row from the entity table
+ if ($guid instanceof stdClass) {
+ // Load the rest
+ if (!$this->load($guid)) {
+ $msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
+ throw new IOException($msg);
+ }
+ } else if ($guid instanceof ElggObject) {
+ // $guid is an ElggObject so this is a copy constructor
+ elgg_deprecated_notice('This type of usage of the ElggObject constructor was deprecated. Please use the clone method.', 1.7);
+
+ foreach ($guid->attributes as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+ } else if ($guid instanceof ElggEntity) {
+ // @todo remove - do not need separate exception
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggObject'));
+ } else if (is_numeric($guid)) {
+ // $guid is a GUID so load
+ if (!$this->load($guid)) {
+ throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
+ }
+ } else {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
+ }
+ }
+ }
+
+ /**
+ * Loads the full ElggObject when given a guid.
+ *
+ * @param mixed $guid GUID of an ElggObject or the stdClass object from entities table
+ *
+ * @return bool
+ * @throws InvalidClassException
+ */
+ protected function load($guid) {
+ $attr_loader = new ElggAttributeLoader(get_class(), 'object', $this->attributes);
+ $attr_loader->requires_access_control = !($this instanceof ElggPlugin);
+ $attr_loader->secondary_loader = 'get_object_entity_as_row';
+
+ $attrs = $attr_loader->getRequiredAttributes($guid);
+ if (!$attrs) {
+ return false;
+ }
+
+ $this->attributes = $attrs;
+ $this->attributes['tables_loaded'] = 2;
+ _elgg_cache_entity($this);
+
+ return true;
+ }
+
+ /**
+ * Saves object-specific attributes.
+ *
+ * @internal Object attributes are saved in the objects_entity table.
+ *
+ * @return bool
+ */
+ public function save() {
+ // Save ElggEntity attributes
+ if (!parent::save()) {
+ return false;
+ }
+
+ // Save ElggObject-specific attributes
+
+ _elgg_disable_caching_for_entity($this->guid);
+ $ret = create_object_entity($this->get('guid'), $this->get('title'), $this->get('description'));
+ _elgg_enable_caching_for_entity($this->guid);
+
+ return $ret;
+ }
+
+ /**
+ * Return sites that this object is a member of
+ *
+ * Site membership is determined by relationships and not site_guid.d
+ *
+ * @todo This should be moved to ElggEntity
+ * @todo Unimplemented
+ *
+ * @param string $subtype Optionally, the subtype of result we want to limit to
+ * @param int $limit The number of results to return
+ * @param int $offset Any indexing offset
+ *
+ * @return array|false
+ */
+ function getSites($subtype = "", $limit = 10, $offset = 0) {
+ return get_site_objects($this->getGUID(), $subtype, $limit, $offset);
+ }
+
+ /**
+ * Add this object to a site
+ *
+ * @param int $site_guid The guid of the site to add it to
+ *
+ * @return bool
+ */
+ function addToSite($site_guid) {
+ return add_site_object($this->getGUID(), $site_guid);
+ }
+
+ /*
+ * EXPORTABLE INTERFACE
+ */
+
+ /**
+ * Return an array of fields which can be exported.
+ *
+ * @return array
+ */
+ public function getExportableValues() {
+ return array_merge(parent::getExportableValues(), array(
+ 'title',
+ 'description',
+ ));
+ }
+
+ /**
+ * Can a user comment on this object?
+ *
+ * @see ElggEntity::canComment()
+ *
+ * @param int $user_guid User guid (default is logged in user)
+ * @return bool
+ * @since 1.8.0
+ */
+ public function canComment($user_guid = 0) {
+ $result = parent::canComment($user_guid);
+ if ($result !== null) {
+ return $result;
+ }
+
+ if ($user_guid == 0) {
+ $user_guid = elgg_get_logged_in_user_guid();
+ }
+
+ // must be logged in to comment
+ if (!$user_guid) {
+ return false;
+ }
+
+ // must be member of group
+ if (elgg_instanceof($this->getContainerEntity(), 'group')) {
+ if (!$this->getContainerEntity()->canWriteToContainer($user_guid)) {
+ return false;
+ }
+ }
+
+ // no checks on read access since a user cannot see entities outside his access
+ return true;
+ }
+}
diff --git a/engine/classes/ElggPAM.php b/engine/classes/ElggPAM.php
new file mode 100644
index 000000000..f07095fc1
--- /dev/null
+++ b/engine/classes/ElggPAM.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * ElggPAM Pluggable Authentication Module
+ *
+ * @package Elgg.Core
+ * @subpackage Authentication
+ */
+class ElggPAM {
+ /**
+ * @var string PAM policy type: user, api or plugin-defined policies
+ */
+ protected $policy;
+
+ /**
+ * @var array Failure mesages
+ */
+ protected $messages;
+
+ /**
+ * ElggPAM constructor
+ *
+ * @param string $policy PAM policy type: user, api, or plugin-defined policies
+ */
+ public function __construct($policy) {
+ $this->policy = $policy;
+ $this->messages = array('sufficient' => array(), 'required' => array());
+ }
+
+ /**
+ * Authenticate a set of credentials against a policy
+ * This function will process all registered PAM handlers or stop when the first
+ * handler fails. A handler fails by either returning false or throwing an
+ * exception. The advantage of throwing an exception is that it returns a message
+ * that can be passed to the user. The processing order of the handlers is
+ * determined by the order that they were registered.
+ *
+ * If $credentials are provided, the PAM handler should authenticate using the
+ * provided credentials. If not, then credentials should be prompted for or
+ * otherwise retrieved (eg from the HTTP header or $_SESSION).
+ *
+ * @param array $credentials Credentials array dependant on policy type
+ * @return bool
+ */
+ public function authenticate($credentials = array()) {
+ global $_PAM_HANDLERS;
+
+ if (!isset($_PAM_HANDLERS[$this->policy]) ||
+ !is_array($_PAM_HANDLERS[$this->policy])) {
+ return false;
+ }
+
+ $authenticated = false;
+
+ foreach ($_PAM_HANDLERS[$this->policy] as $k => $v) {
+ $handler = $v->handler;
+ if (!is_callable($handler)) {
+ continue;
+ }
+ /* @var callable $handler */
+
+ $importance = $v->importance;
+
+ try {
+ // Execute the handler
+ // @todo don't assume $handler is a global function
+ $result = call_user_func($handler, $credentials);
+ if ($result) {
+ $authenticated = true;
+ } elseif ($result === false) {
+ if ($importance == 'required') {
+ $this->messages['required'][] = "$handler:failed";
+ return false;
+ } else {
+ $this->messages['sufficient'][] = "$handler:failed";
+ }
+ }
+ } catch (Exception $e) {
+ if ($importance == 'required') {
+ $this->messages['required'][] = $e->getMessage();
+ return false;
+ } else {
+ $this->messages['sufficient'][] = $e->getMessage();
+ }
+ }
+ }
+
+ return $authenticated;
+ }
+
+ /**
+ * Get a failure message to display to user
+ *
+ * @return string
+ */
+ public function getFailureMessage() {
+ $message = elgg_echo('auth:nopams');
+ if (!empty($this->messages['required'])) {
+ $message = $this->messages['required'][0];
+ } elseif (!empty($this->messages['sufficient'])) {
+ $message = $this->messages['sufficient'][0];
+ }
+
+ return elgg_trigger_plugin_hook('fail', 'auth', $this->messages, $message);
+ }
+}
diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php
new file mode 100644
index 000000000..545b9a53c
--- /dev/null
+++ b/engine/classes/ElggPlugin.php
@@ -0,0 +1,1006 @@
+<?php
+/**
+ * Stores site-side plugin settings as private data.
+ *
+ * This class is currently a stub, allowing a plugin to
+ * save settings in an object's private settings for each site.
+ *
+ * @package Elgg.Core
+ * @subpackage Plugins.Settings
+ */
+class ElggPlugin extends ElggObject {
+ private $package;
+ private $manifest;
+
+ private $path;
+ private $pluginID;
+ private $errorMsg = '';
+
+ /**
+ * Set subtype to 'plugin'
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['subtype'] = "plugin";
+
+ // plugins must be public.
+ $this->access_id = ACCESS_PUBLIC;
+ }
+
+ /**
+ * Loads the plugin by GUID or path.
+ *
+ * @warning Unlike other ElggEntity objects, you cannot null instantiate
+ * ElggPlugin. You must point it to an actual plugin GUID or location.
+ *
+ * @param mixed $plugin The GUID of the ElggPlugin object or the path of the plugin to load.
+ *
+ * @throws PluginException
+ */
+ public function __construct($plugin) {
+ if (!$plugin) {
+ throw new PluginException(elgg_echo('PluginException:NullInstantiated'));
+ }
+
+ // ElggEntity can be instantiated with a guid or an object.
+ // @todo plugins w/id 12345
+ if (is_numeric($plugin) || is_object($plugin)) {
+ parent::__construct($plugin);
+ $this->path = elgg_get_plugins_path() . $this->getID();
+ } else {
+ $plugin_path = elgg_get_plugins_path();
+
+ // not a full path, so assume an id
+ // use the default path
+ if (strpos($plugin, $plugin_path) !== 0) {
+ $plugin = $plugin_path . $plugin;
+ }
+
+ // path checking is done in the package
+ $plugin = sanitise_filepath($plugin);
+ $this->path = $plugin;
+ $path_parts = explode('/', rtrim($plugin, '/'));
+ $plugin_id = array_pop($path_parts);
+ $this->pluginID = $plugin_id;
+
+ // check if we're loading an existing plugin
+ $existing_plugin = elgg_get_plugin_from_id($this->pluginID);
+ $existing_guid = null;
+
+ if ($existing_plugin) {
+ $existing_guid = $existing_plugin->guid;
+ }
+
+ // load the rest of the plugin
+ parent::__construct($existing_guid);
+ }
+
+ _elgg_cache_plugin_by_id($this);
+ }
+
+ /**
+ * Save the plugin object. Make sure required values exist.
+ *
+ * @see ElggObject::save()
+ * @return bool
+ */
+ public function save() {
+ // own by the current site so users can be deleted without affecting plugins
+ $site = get_config('site');
+ $this->attributes['site_guid'] = $site->guid;
+ $this->attributes['owner_guid'] = $site->guid;
+ $this->attributes['container_guid'] = $site->guid;
+ $this->attributes['title'] = $this->pluginID;
+
+ if (parent::save()) {
+ // make sure we have a priority
+ $priority = $this->getPriority();
+ if ($priority === FALSE || $priority === NULL) {
+ return $this->setPriority('last');
+ }
+ } else {
+ return false;
+ }
+ }
+
+
+ // Plugin ID and path
+
+ /**
+ * Returns the ID (dir name) of this plugin
+ *
+ * @return string
+ */
+ public function getID() {
+ return $this->title;
+ }
+
+ /**
+ * Returns the manifest's name if available, otherwise the ID.
+ *
+ * @return string
+ * @since 1.8.1
+ */
+ public function getFriendlyName() {
+ $manifest = $this->getManifest();
+ if ($manifest) {
+ return $manifest->getName();
+ }
+
+ return $this->getID();
+ }
+
+ /**
+ * Returns the plugin's full path with trailing slash.
+ *
+ * @return string
+ */
+ public function getPath() {
+ return sanitise_filepath($this->path);
+ }
+
+ /**
+ * Sets the location of this plugin.
+ *
+ * @param string $id The path to the plugin's dir.
+ * @return bool
+ */
+ public function setID($id) {
+ return $this->attributes['title'] = $id;
+ }
+
+ /**
+ * Returns an array of available markdown files for this plugin
+ *
+ * @return array
+ */
+ public function getAvailableTextFiles() {
+ $filenames = $this->getPackage()->getTextFilenames();
+
+ $files = array();
+ foreach ($filenames as $filename) {
+ if ($this->canReadFile($filename)) {
+ $files[$filename] = "$this->path/$filename";
+ }
+ }
+
+ return $files;
+ }
+
+ // Load Priority
+
+ /**
+ * Gets the plugin's load priority.
+ *
+ * @return int
+ */
+ public function getPriority() {
+ $name = elgg_namespace_plugin_private_setting('internal', 'priority');
+ return $this->$name;
+ }
+
+ /**
+ * Sets the priority of the plugin
+ *
+ * @param mixed $priority The priority to set. One of +1, -1, first, last, or a number.
+ * If given a number, this will displace all plugins at that number
+ * and set their priorities +1
+ * @param mixed $site_guid Optional site GUID.
+ * @return bool
+ */
+ public function setPriority($priority, $site_guid = null) {
+ if (!$this->guid) {
+ return false;
+ }
+
+ $db_prefix = get_config('dbprefix');
+ $name = elgg_namespace_plugin_private_setting('internal', 'priority');
+ // if no priority assume a priority of 1
+ $old_priority = (int) $this->getPriority();
+ $old_priority = (!$old_priority) ? 1 : $old_priority;
+ $max_priority = elgg_get_max_plugin_priority();
+
+ // can't use switch here because it's not strict and
+ // php evaluates +1 == 1
+ if ($priority === '+1') {
+ $priority = $old_priority + 1;
+ } elseif ($priority === '-1') {
+ $priority = $old_priority - 1;
+ } elseif ($priority === 'first') {
+ $priority = 1;
+ } elseif ($priority === 'last') {
+ $priority = $max_priority;
+ }
+
+ // should be a number by now
+ if ($priority > 0) {
+ if (!is_numeric($priority)) {
+ return false;
+ }
+
+ // there's nothing above the max.
+ if ($priority > $max_priority) {
+ $priority = $max_priority;
+ }
+
+ // there's nothing below 1.
+ if ($priority < 1) {
+ $priority = 1;
+ }
+
+ if ($priority > $old_priority) {
+ $op = '-';
+ $where = "CAST(value as unsigned) BETWEEN $old_priority AND $priority";
+ } else {
+ $op = '+';
+ $where = "CAST(value as unsigned) BETWEEN $priority AND $old_priority";
+ }
+
+ // displace the ones affected by this change
+ $q = "UPDATE {$db_prefix}private_settings
+ SET value = CAST(value as unsigned) $op 1
+ WHERE entity_guid != $this->guid
+ AND name = '$name'
+ AND $where";
+
+ if (!update_data($q)) {
+ return false;
+ }
+
+ // set this priority
+ if ($this->set($name, $priority)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+
+ // Plugin settings
+
+ /**
+ * Returns a plugin setting
+ *
+ * @param string $name The setting name
+ * @return mixed
+ */
+ public function getSetting($name) {
+ return $this->$name;
+ }
+
+ /**
+ * Returns an array of all settings saved for this plugin.
+ *
+ * @note Unlike user settings, plugin settings are not namespaced.
+ *
+ * @return array An array of key/value pairs.
+ */
+ public function getAllSettings() {
+ if (!$this->guid) {
+ return false;
+ }
+
+ $db_prefix = elgg_get_config('dbprefix');
+ // need to remove all namespaced private settings.
+ $us_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
+ $is_prefix = elgg_namespace_plugin_private_setting('internal', '', $this->getID());
+
+ // Get private settings for user
+ $q = "SELECT * FROM {$db_prefix}private_settings
+ WHERE entity_guid = $this->guid
+ AND name NOT LIKE '$us_prefix%'
+ AND name NOT LIKE '$is_prefix%'";
+
+ $private_settings = get_data($q);
+
+ $return = array();
+
+ if ($private_settings) {
+ foreach ($private_settings as $setting) {
+ $return[$setting->name] = $setting->value;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Set a plugin setting for the plugin
+ *
+ * @todo This will only work once the plugin has a GUID.
+ *
+ * @param string $name The name to set
+ * @param string $value The value to set
+ *
+ * @return bool
+ */
+ public function setSetting($name, $value) {
+ if (!$this->guid) {
+ return false;
+ }
+
+ return $this->set($name, $value);
+ }
+
+ /**
+ * Removes a plugin setting name and value.
+ *
+ * @param string $name The setting name to remove
+ *
+ * @return bool
+ */
+ public function unsetSetting($name) {
+ return remove_private_setting($this->guid, $name);
+ }
+
+ /**
+ * Removes all settings for this plugin.
+ *
+ * @todo Should be a better way to do this without dropping to raw SQL.
+ * @todo If we could namespace the plugin settings this would be cleaner.
+ * @return bool
+ */
+ public function unsetAllSettings() {
+ $db_prefix = get_config('dbprefix');
+
+ $us_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
+ $is_prefix = elgg_namespace_plugin_private_setting('internal', '', $this->getID());
+
+ $q = "DELETE FROM {$db_prefix}private_settings
+ WHERE entity_guid = $this->guid
+ AND name NOT LIKE '$us_prefix%'
+ AND name NOT LIKE '$is_prefix%'";
+
+ return delete_data($q);
+ }
+
+
+ // User settings
+
+ /**
+ * Returns a user's setting for this plugin
+ *
+ * @param string $name The setting name
+ * @param int $user_guid The user GUID
+ *
+ * @return mixed The setting string value or false
+ */
+ public function getUserSetting($name, $user_guid = null) {
+ $user_guid = (int)$user_guid;
+
+ if ($user_guid) {
+ $user = get_entity($user_guid);
+ } else {
+ $user = elgg_get_logged_in_user_entity();
+ }
+
+ if (!($user instanceof ElggUser)) {
+ return false;
+ }
+
+ $name = elgg_namespace_plugin_private_setting('user_setting', $name, $this->getID());
+ return get_private_setting($user->guid, $name);
+ }
+
+ /**
+ * Returns an array of all user settings saved for this plugin for the user.
+ *
+ * @note Plugin settings are saved with a prefix. This removes that prefix.
+ *
+ * @param int $user_guid The user GUID. Defaults to logged in.
+ * @return array An array of key/value pairs.
+ */
+ public function getAllUserSettings($user_guid = null) {
+ $user_guid = (int)$user_guid;
+
+ if ($user_guid) {
+ $user = get_entity($user_guid);
+ } else {
+ $user = elgg_get_logged_in_user_entity();
+ }
+
+ if (!($user instanceof ElggUser)) {
+ return false;
+ }
+
+ $db_prefix = elgg_get_config('dbprefix');
+ // send an empty name so we just get the first part of the namespace
+ $ps_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
+ $ps_prefix_len = strlen($ps_prefix);
+
+ // Get private settings for user
+ $q = "SELECT * FROM {$db_prefix}private_settings
+ WHERE entity_guid = {$user->guid}
+ AND name LIKE '$ps_prefix%'";
+
+ $private_settings = get_data($q);
+
+ $return = array();
+
+ if ($private_settings) {
+ foreach ($private_settings as $setting) {
+ $name = substr($setting->name, $ps_prefix_len);
+ $value = $setting->value;
+
+ $return[$name] = $value;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Sets a user setting for a plugin
+ *
+ * @param string $name The setting name
+ * @param string $value The setting value
+ * @param int $user_guid The user GUID
+ *
+ * @return mixed The new setting ID or false
+ */
+ public function setUserSetting($name, $value, $user_guid = null) {
+ $user_guid = (int)$user_guid;
+
+ if ($user_guid) {
+ $user = get_entity($user_guid);
+ } else {
+ $user = elgg_get_logged_in_user_entity();
+ }
+
+ if (!($user instanceof ElggUser)) {
+ return false;
+ }
+
+ // Hook to validate setting
+ // note: this doesn't pass the namespaced name
+ $value = elgg_trigger_plugin_hook('usersetting', 'plugin', array(
+ 'user' => $user,
+ 'plugin' => $this,
+ 'plugin_id' => $this->getID(),
+ 'name' => $name,
+ 'value' => $value
+ ), $value);
+
+ // set the namespaced name.
+ $name = elgg_namespace_plugin_private_setting('user_setting', $name, $this->getID());
+
+ return set_private_setting($user->guid, $name, $value);
+ }
+
+
+ /**
+ * Removes a user setting name and value.
+ *
+ * @param string $name The user setting name
+ * @param int $user_guid The user GUID
+ * @return bool
+ */
+ public function unsetUserSetting($name, $user_guid = null) {
+ $user_guid = (int)$user_guid;
+
+ if ($user_guid) {
+ $user = get_entity($user_guid);
+ } else {
+ $user = elgg_get_logged_in_user_entity();
+ }
+
+ if (!($user instanceof ElggUser)) {
+ return false;
+ }
+
+ // set the namespaced name.
+ $name = elgg_namespace_plugin_private_setting('user_setting', $name, $this->getID());
+
+ return remove_private_setting($user->guid, $name);
+ }
+
+ /**
+ * Removes all User Settings for this plugin
+ *
+ * Use {@link removeAllUsersSettings()} to remove all user
+ * settings for all users. (Note the plural 'Users'.)
+ *
+ * @param int $user_guid The user GUID to remove user settings.
+ * @return bool
+ */
+ public function unsetAllUserSettings($user_guid) {
+ $db_prefix = get_config('dbprefix');
+ $ps_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
+
+ $q = "DELETE FROM {$db_prefix}private_settings
+ WHERE entity_guid = $user_guid
+ AND name LIKE '$ps_prefix%'";
+
+ return delete_data($q);
+ }
+
+ /**
+ * Removes this plugin's user settings for all users.
+ *
+ * Use {@link removeAllUserSettings()} if you just want to remove
+ * settings for a single user.
+ *
+ * @return bool
+ */
+ public function unsetAllUsersSettings() {
+ $db_prefix = get_config('dbprefix');
+ $ps_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
+
+ $q = "DELETE FROM {$db_prefix}private_settings
+ WHERE name LIKE '$ps_prefix%'";
+
+ return delete_data($q);
+ }
+
+
+ // validation
+
+ /**
+ * Returns if the plugin is complete, meaning has all required files
+ * and Elgg can read them and they make sense.
+ *
+ * @todo bad name? This could be confused with isValid() from ElggPluginPackage.
+ *
+ * @return bool
+ */
+ public function isValid() {
+ if (!$this->getID()) {
+ $this->errorMsg = elgg_echo('ElggPlugin:NoId', array($this->guid));
+ return false;
+ }
+
+ if (!$this->getPackage() instanceof ElggPluginPackage) {
+ $this->errorMsg = elgg_echo('ElggPlugin:NoPluginPackagePackage', array($this->getID(), $this->guid));
+ return false;
+ }
+
+ if (!$this->getPackage()->isValid()) {
+ $this->errorMsg = $this->getPackage()->getError();
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Is this plugin active?
+ *
+ * @param int $site_guid Optional site guid.
+ * @return bool
+ */
+ public function isActive($site_guid = null) {
+ if (!$this->guid) {
+ return false;
+ }
+
+ if ($site_guid) {
+ $site = get_entity($site_guid);
+ } else {
+ $site = get_config('site');
+ }
+
+ if (!($site instanceof ElggSite)) {
+ return false;
+ }
+
+ return check_entity_relationship($this->guid, 'active_plugin', $site->guid);
+ }
+
+ /**
+ * Checks if this plugin can be activated on the current
+ * Elgg installation.
+ *
+ * @todo remove $site_guid param or implement it
+ *
+ * @param mixed $site_guid Optional site guid
+ * @return bool
+ */
+ public function canActivate($site_guid = null) {
+ if ($this->getPackage()) {
+ $result = $this->getPackage()->isValid() && $this->getPackage()->checkDependencies();
+ if (!$result) {
+ $this->errorMsg = $this->getPackage()->getError();
+ }
+
+ return $result;
+ }
+
+ return false;
+ }
+
+
+ // activating and deactivating
+
+ /**
+ * Actives the plugin for the current site.
+ *
+ * @param mixed $site_guid Optional site GUID.
+ * @return bool
+ */
+ public function activate($site_guid = null) {
+ if ($this->isActive($site_guid)) {
+ return false;
+ }
+
+ if (!$this->canActivate()) {
+ return false;
+ }
+
+ // set in the db, now perform tasks and emit events
+ if ($this->setStatus(true, $site_guid)) {
+ // emit an event. returning false will make this not be activated.
+ // we need to do this after it's been fully activated
+ // or the deactivate will be confused.
+ $params = array(
+ 'plugin_id' => $this->pluginID,
+ 'plugin_entity' => $this
+ );
+
+ $return = elgg_trigger_event('activate', 'plugin', $params);
+
+ // if there are any on_enable functions, start the plugin now and run them
+ // Note: this will not run re-run the init hooks!
+ if ($return) {
+ if ($this->canReadFile('activate.php')) {
+ $flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_CLASSES |
+ ELGG_PLUGIN_REGISTER_LANGUAGES | ELGG_PLUGIN_REGISTER_VIEWS;
+
+ $this->start($flags);
+
+ $return = $this->includeFile('activate.php');
+ }
+ }
+
+ if ($return === false) {
+ $this->deactivate($site_guid);
+ }
+
+ return $return;
+ }
+
+ return false;
+ }
+
+ /**
+ * Deactivates the plugin.
+ *
+ * @param mixed $site_guid Optional site GUID.
+ * @return bool
+ */
+ public function deactivate($site_guid = null) {
+ if (!$this->isActive($site_guid)) {
+ return false;
+ }
+
+ // emit an event. returning false will cause this to not be deactivated.
+ $params = array(
+ 'plugin_id' => $this->pluginID,
+ 'plugin_entity' => $this
+ );
+
+ $return = elgg_trigger_event('deactivate', 'plugin', $params);
+
+ // run any deactivate code
+ if ($return) {
+ if ($this->canReadFile('deactivate.php')) {
+ $return = $this->includeFile('deactivate.php');
+ }
+ }
+
+ if ($return === false) {
+ return false;
+ } else {
+ return $this->setStatus(false, $site_guid);
+ }
+ }
+
+ /**
+ * Start the plugin.
+ *
+ * @param int $flags Start flags for the plugin. See the constants in lib/plugins.php for details.
+ * @return true
+ * @throws PluginException
+ */
+ public function start($flags) {
+ //if (!$this->canActivate()) {
+ // return false;
+ //}
+
+ // include classes
+ if ($flags & ELGG_PLUGIN_REGISTER_CLASSES) {
+ $this->registerClasses();
+ }
+
+ // include start file
+ if ($flags & ELGG_PLUGIN_INCLUDE_START) {
+ $this->includeFile('start.php');
+ }
+
+ // include views
+ if ($flags & ELGG_PLUGIN_REGISTER_VIEWS) {
+ $this->registerViews();
+ }
+
+ // include languages
+ if ($flags & ELGG_PLUGIN_REGISTER_LANGUAGES) {
+ $this->registerLanguages();
+ }
+
+ return true;
+ }
+
+
+ // start helpers
+
+ /**
+ * Includes one of the plugins files
+ *
+ * @param string $filename The name of the file
+ *
+ * @throws PluginException
+ * @return mixed The return value of the included file (or 1 if there is none)
+ */
+ protected function includeFile($filename) {
+ // This needs to be here to be backwards compatible for 1.0-1.7.
+ // They expect the global config object to be available in start.php.
+ if ($filename == 'start.php') {
+ global $CONFIG;
+ }
+
+ $filepath = "$this->path/$filename";
+
+ if (!$this->canReadFile($filename)) {
+ $msg = elgg_echo('ElggPlugin:Exception:CannotIncludeFile',
+ array($filename, $this->getID(), $this->guid, $this->path));
+ throw new PluginException($msg);
+ }
+
+ return include $filepath;
+ }
+
+ /**
+ * Checks whether a plugin file with the given name exists
+ *
+ * @param string $filename The name of the file
+ * @return bool
+ */
+ protected function canReadFile($filename) {
+ return is_readable($this->path . '/' . $filename);
+ }
+
+ /**
+ * Registers the plugin's views
+ *
+ * @throws PluginException
+ * @return true
+ */
+ protected function registerViews() {
+ $view_dir = "$this->path/views/";
+
+ // plugins don't have to have views.
+ if (!is_dir($view_dir)) {
+ return true;
+ }
+
+ // but if they do, they have to be readable
+ $handle = opendir($view_dir);
+ if (!$handle) {
+ $msg = elgg_echo('ElggPlugin:Exception:CannotRegisterViews',
+ array($this->getID(), $this->guid, $view_dir));
+ throw new PluginException($msg);
+ }
+
+ while (FALSE !== ($view_type = readdir($handle))) {
+ $view_type_dir = $view_dir . $view_type;
+
+ if ('.' !== substr($view_type, 0, 1) && is_dir($view_type_dir)) {
+ if (autoregister_views('', $view_type_dir, $view_dir, $view_type)) {
+ elgg_register_viewtype($view_type);
+ } else {
+ $msg = elgg_echo('ElggPlugin:Exception:CannotRegisterViews',
+ array($this->getID(), $view_type_dir));
+ throw new PluginException($msg);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Registers the plugin's languages
+ *
+ * @throws PluginException
+ * @return true
+ */
+ protected function registerLanguages() {
+ $languages_path = "$this->path/languages";
+
+ // don't need to have classes
+ if (!is_dir($languages_path)) {
+ return true;
+ }
+
+ // but need to have working ones.
+ if (!register_translations($languages_path)) {
+ $msg = elgg_echo('ElggPlugin:Exception:CannotRegisterLanguages',
+ array($this->getID(), $this->guid, $languages_path));
+ throw new PluginException($msg);
+ }
+
+ return true;
+ }
+
+ /**
+ * Registers the plugin's classes
+ *
+ * @throws PluginException
+ * @return true
+ */
+ protected function registerClasses() {
+ $classes_path = "$this->path/classes";
+
+ // don't need to have classes
+ if (!is_dir($classes_path)) {
+ return true;
+ }
+
+ elgg_register_classes($classes_path);
+
+ return true;
+ }
+
+
+ // generic helpers and overrides
+
+ /**
+ * Get a value from private settings.
+ *
+ * @param string $name Name
+ *
+ * @return mixed
+ */
+ public function get($name) {
+ // rewrite for old and inaccurate plugin:setting
+ if (strstr($name, 'plugin:setting:')) {
+ $msg = 'Direct access of user settings is deprecated. Use ElggPlugin->getUserSetting()';
+ elgg_deprecated_notice($msg, 1.8);
+ $name = str_replace('plugin:setting:', '', $name);
+ $name = elgg_namespace_plugin_private_setting('user_setting', $name);
+ }
+
+ // See if its in our base attribute
+ if (array_key_exists($name, $this->attributes)) {
+ return $this->attributes[$name];
+ }
+
+ // No, so see if its in the private data store.
+ // get_private_setting() returns false if it doesn't exist
+ $meta = $this->getPrivateSetting($name);
+
+ if ($meta === false) {
+ // Can't find it, so return null
+ return NULL;
+ }
+
+ return $meta;
+ }
+
+ /**
+ * Save a value as private setting or attribute.
+ *
+ * Attributes include title and description.
+ *
+ * @param string $name Name
+ * @param mixed $value Value
+ *
+ * @return bool
+ */
+ public function set($name, $value) {
+ if (array_key_exists($name, $this->attributes)) {
+ // Check that we're not trying to change the guid!
+ if ((array_key_exists('guid', $this->attributes)) && ($name == 'guid')) {
+ return false;
+ }
+
+ $this->attributes[$name] = $value;
+
+ return true;
+ } else {
+ // Hook to validate setting
+ $value = elgg_trigger_plugin_hook('setting', 'plugin', array(
+ 'plugin_id' => $this->pluginID,
+ 'plugin' => $this,
+ 'name' => $name,
+ 'value' => $value
+ ), $value);
+
+ return $this->setPrivateSetting($name, $value);
+ }
+ }
+
+ /**
+ * Sets the plugin to active or inactive for $site_guid.
+ *
+ * @param bool $active Set to active or inactive
+ * @param mixed $site_guid Int for specific site, null for current site.
+ *
+ * @return bool
+ */
+ private function setStatus($active, $site_guid = null) {
+ if (!$this->guid) {
+ return false;
+ }
+
+ if ($site_guid) {
+ $site = get_entity($site_guid);
+
+ if (!($site instanceof ElggSite)) {
+ return false;
+ }
+ } else {
+ $site = get_config('site');
+ }
+
+ if ($active) {
+ return add_entity_relationship($this->guid, 'active_plugin', $site->guid);
+ } else {
+ return remove_entity_relationship($this->guid, 'active_plugin', $site->guid);
+ }
+ }
+
+ /**
+ * Returns the last error message registered.
+ *
+ * @return string|null
+ */
+ public function getError() {
+ return $this->errorMsg;
+ }
+
+ /**
+ * Returns this plugin's ElggPluginManifest object
+ *
+ * @return ElggPluginManifest
+ */
+ public function getManifest() {
+ if ($this->manifest instanceof ElggPluginManifest) {
+ return $this->manifest;
+ }
+
+ try {
+ $this->manifest = $this->getPackage()->getManifest();
+ } catch (Exception $e) {
+ elgg_log("Failed to load manifest for plugin $this->guid. " . $e->getMessage(), 'WARNING');
+ $this->errorMsg = $e->getmessage();
+ }
+
+ return $this->manifest;
+ }
+
+ /**
+ * Returns this plugin's ElggPluginPackage object
+ *
+ * @return ElggPluginPackage
+ */
+ public function getPackage() {
+ if ($this->package instanceof ElggPluginPackage) {
+ return $this->package;
+ }
+
+ try {
+ $this->package = new ElggPluginPackage($this->path, false);
+ } catch (Exception $e) {
+ elgg_log("Failed to load package for $this->guid. " . $e->getMessage(), 'WARNING');
+ $this->errorMsg = $e->getmessage();
+ }
+
+ return $this->package;
+ }
+}
diff --git a/engine/classes/ElggPluginManifest.php b/engine/classes/ElggPluginManifest.php
new file mode 100644
index 000000000..6912c2b08
--- /dev/null
+++ b/engine/classes/ElggPluginManifest.php
@@ -0,0 +1,656 @@
+<?php
+/**
+ * Parses Elgg manifest.xml files.
+ *
+ * Normalizes the values from the ElggManifestParser object.
+ *
+ * This requires an ElggPluginManifestParser class implementation
+ * as $this->parser.
+ *
+ * To add new parser versions, name them ElggPluginManifestParserXX
+ * where XX is the version specified in the top-level <plugin_manifest>
+ * tag's XML namespace.
+ *
+ * @package Elgg.Core
+ * @subpackage Plugins
+ * @since 1.8
+ */
+class ElggPluginManifest {
+
+ /**
+ * The parser object
+ */
+ protected $parser;
+
+ /**
+ * The root for plugin manifest namespaces.
+ * This is in the format http://www.elgg.org/plugin_manifest/<version>
+ */
+ protected $namespace_root = 'http://www.elgg.org/plugin_manifest/';
+
+ /**
+ * The expected structure of a plugins requires element
+ */
+ private $depsStructPlugin = array(
+ 'type' => '',
+ 'name' => '',
+ 'version' => '',
+ 'comparison' => 'ge'
+ );
+
+ /**
+ * The expected structure of a priority element
+ */
+ private $depsStructPriority = array(
+ 'type' => '',
+ 'priority' => '',
+ 'plugin' => ''
+ );
+
+ /*
+ * The expected structure of elgg_version and elgg_release requires element
+ */
+ private $depsStructElgg = array(
+ 'type' => '',
+ 'version' => '',
+ 'comparison' => 'ge'
+ );
+
+ /**
+ * The expected structure of a requires php_ini dependency element
+ */
+ private $depsStructPhpIni = array(
+ 'type' => '',
+ 'name' => '',
+ 'value' => '',
+ 'comparison' => '='
+ );
+
+ /**
+ * The expected structure of a requires php_extension dependency element
+ */
+ private $depsStructPhpExtension = array(
+ 'type' => '',
+ 'name' => '',
+ 'version' => '',
+ 'comparison' => '='
+ );
+
+ /**
+ * The expected structure of a conflicts depedency element
+ */
+ private $depsConflictsStruct = array(
+ 'type' => '',
+ 'name' => '',
+ 'version' => '',
+ 'comparison' => '='
+ );
+
+ /**
+ * The expected structure of a provides dependency element.
+ */
+ private $depsProvidesStruct = array(
+ 'type' => '',
+ 'name' => '',
+ 'version' => ''
+ );
+
+ /**
+ * The expected structure of a screenshot element
+ */
+ private $screenshotStruct = array(
+ 'description' => '',
+ 'path' => ''
+ );
+
+ /**
+ * The API version of the manifest.
+ *
+ * @var int
+ */
+ protected $apiVersion;
+
+ /**
+ * The optional plugin id this manifest belongs to.
+ *
+ * @var string
+ */
+ protected $pluginID;
+
+ /**
+ * Load a manifest file, XmlElement or path to manifest.xml file
+ *
+ * @param mixed $manifest A string, XmlElement, or path of a manifest file.
+ * @param string $plugin_id Optional ID of the owning plugin. Used to
+ * fill in some values automatically.
+ */
+ public function __construct($manifest, $plugin_id = null) {
+ if ($plugin_id) {
+ $this->pluginID = $plugin_id;
+ }
+
+ // see if we need to construct the xml object.
+ if ($manifest instanceof ElggXMLElement) {
+ $manifest_obj = $manifest;
+ } else {
+ if (substr(trim($manifest), 0, 1) == '<') {
+ // this is a string
+ $raw_xml = $manifest;
+ } elseif (is_file($manifest)) {
+ // this is a file
+ $raw_xml = file_get_contents($manifest);
+ }
+
+ $manifest_obj = xml_to_object($raw_xml);
+ }
+
+ if (!$manifest_obj) {
+ throw new PluginException(elgg_echo('PluginException:InvalidManifest',
+ array($this->getPluginID())));
+ }
+
+ // set manifest api version
+ if (isset($manifest_obj->attributes['xmlns'])) {
+ $namespace = $manifest_obj->attributes['xmlns'];
+ $version = str_replace($this->namespace_root, '', $namespace);
+ } else {
+ $version = 1.7;
+ }
+
+ $this->apiVersion = $version;
+
+ $parser_class_name = 'ElggPluginManifestParser' . str_replace('.', '', $this->apiVersion);
+
+ // @todo currently the autoloader freaks out if a class doesn't exist.
+ try {
+ $class_exists = class_exists($parser_class_name);
+ } catch (Exception $e) {
+ $class_exists = false;
+ }
+
+ if ($class_exists) {
+ $this->parser = new $parser_class_name($manifest_obj, $this);
+ } else {
+ throw new PluginException(elgg_echo('PluginException:NoAvailableParser',
+ array($this->apiVersion, $this->getPluginID())));
+ }
+
+ if (!$this->parser->parse()) {
+ throw new PluginException(elgg_echo('PluginException:ParserError',
+ array($this->apiVersion, $this->getPluginID())));
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the API version in use.
+ *
+ * @return int
+ */
+ public function getApiVersion() {
+ return $this->apiVersion;
+ }
+
+ /**
+ * Returns the plugin ID.
+ *
+ * @return string
+ */
+ public function getPluginID() {
+ if ($this->pluginID) {
+ return $this->pluginID;
+ } else {
+ return elgg_echo('unknown');
+ }
+ }
+
+ /**
+ * Returns the manifest array.
+ *
+ * Used for backward compatibility. Specific
+ * methods should be called instead.
+ *
+ * @return array
+ */
+ public function getManifest() {
+ return $this->parser->getManifest();
+ }
+
+ /***************************************
+ * Parsed and Normalized Manifest Data *
+ ***************************************/
+
+ /**
+ * Returns the plugin name
+ *
+ * @return string
+ */
+ public function getName() {
+ $name = $this->parser->getAttribute('name');
+
+ if (!$name && $this->pluginID) {
+ $name = ucwords(str_replace('_', ' ', $this->pluginID));
+ }
+
+ return $name;
+ }
+
+
+ /**
+ * Return the description
+ *
+ * @return string
+ */
+ public function getDescription() {
+ return $this->parser->getAttribute('description');
+ }
+
+ /**
+ * Return the short description
+ *
+ * @return string
+ */
+ public function getBlurb() {
+ $blurb = $this->parser->getAttribute('blurb');
+
+ if (!$blurb) {
+ $blurb = elgg_get_excerpt($this->getDescription());
+ }
+
+ return $blurb;
+ }
+
+ /**
+ * Returns the license
+ *
+ * @return string
+ */
+ public function getLicense() {
+ // license vs licence. Use license.
+ $en_us = $this->parser->getAttribute('license');
+ if ($en_us) {
+ return $en_us;
+ } else {
+ return $this->parser->getAttribute('licence');
+ }
+ }
+
+ /**
+ * Returns the repository url
+ *
+ * @return string
+ */
+ public function getRepositoryURL() {
+ return $this->parser->getAttribute('repository');
+ }
+
+ /**
+ * Returns the bug tracker page
+ *
+ * @return string
+ */
+ public function getBugTrackerURL() {
+ return $this->parser->getAttribute('bugtracker');
+ }
+
+ /**
+ * Returns the donations page
+ *
+ * @return string
+ */
+ public function getDonationsPageURL() {
+ return $this->parser->getAttribute('donations');
+ }
+
+ /**
+ * Returns the version of the plugin.
+ *
+ * @return float
+ */
+ public function getVersion() {
+ return $this->parser->getAttribute('version');
+ }
+
+ /**
+ * Returns the plugin author.
+ *
+ * @return string
+ */
+ public function getAuthor() {
+ return $this->parser->getAttribute('author');
+ }
+
+ /**
+ * Return the copyright
+ *
+ * @return string
+ */
+ public function getCopyright() {
+ return $this->parser->getAttribute('copyright');
+ }
+
+ /**
+ * Return the website
+ *
+ * @return string
+ */
+ public function getWebsite() {
+ return $this->parser->getAttribute('website');
+ }
+
+ /**
+ * Return the categories listed for this plugin
+ *
+ * @return array
+ */
+ public function getCategories() {
+ $bundled_plugins = array('blog', 'bookmarks', 'categories',
+ 'custom_index', 'dashboard', 'developers', 'diagnostics',
+ 'embed', 'externalpages', 'file', 'garbagecollector',
+ 'groups', 'htmlawed', 'invitefriends', 'likes',
+ 'logbrowser', 'logrotate', 'members', 'messageboard',
+ 'messages', 'notifications', 'oauth_api', 'pages', 'profile',
+ 'reportedcontent', 'search', 'tagcloud', 'thewire', 'tinymce',
+ 'twitter', 'twitter_api', 'uservalidationbyemail', 'zaudio',
+ );
+
+ $cats = $this->parser->getAttribute('category');
+
+ if (!$cats) {
+ $cats = array();
+ }
+
+ if (in_array('bundled', $cats) && !in_array($this->getPluginID(), $bundled_plugins)) {
+ unset($cats[array_search('bundled', $cats)]);
+ }
+
+ return $cats;
+ }
+
+ /**
+ * Return the screenshots listed.
+ *
+ * @return array
+ */
+ public function getScreenshots() {
+ $ss = $this->parser->getAttribute('screenshot');
+
+ if (!$ss) {
+ $ss = array();
+ }
+
+ $normalized = array();
+ foreach ($ss as $s) {
+ $normalized[] = $this->buildStruct($this->screenshotStruct, $s);
+ }
+
+ return $normalized;
+ }
+
+ /**
+ * Return the list of provides by this plugin.
+ *
+ * @return array
+ */
+ public function getProvides() {
+ // normalize for 1.7
+ if ($this->getApiVersion() < 1.8) {
+ $provides = array();
+ } else {
+ $provides = $this->parser->getAttribute('provides');
+ }
+
+ if (!$provides) {
+ $provides = array();
+ }
+
+ // always provide ourself if we can
+ if ($this->pluginID) {
+ $provides[] = array(
+ 'type' => 'plugin',
+ 'name' => $this->getPluginID(),
+ 'version' => $this->getVersion()
+ );
+ }
+
+ $normalized = array();
+ foreach ($provides as $provide) {
+ $normalized[] = $this->buildStruct($this->depsProvidesStruct, $provide);
+ }
+
+ return $normalized;
+ }
+
+ /**
+ * Returns the dependencies listed.
+ *
+ * @return array
+ */
+ public function getRequires() {
+ // rewrite the 1.7 style elgg_version as a real requires.
+ if ($this->apiVersion < 1.8) {
+ $elgg_version = $this->parser->getAttribute('elgg_version');
+ if ($elgg_version) {
+ $reqs = array(
+ array(
+ 'type' => 'elgg_version',
+ 'version' => $elgg_version,
+ 'comparison' => 'ge'
+ )
+ );
+ } else {
+ $reqs = array();
+ }
+ } else {
+ $reqs = $this->parser->getAttribute('requires');
+ }
+
+ if (!$reqs) {
+ $reqs = array();
+ }
+
+ $normalized = array();
+ foreach ($reqs as $req) {
+ $normalized[] = $this->normalizeDep($req);
+ }
+
+ return $normalized;
+ }
+
+ /**
+ * Returns the suggests elements.
+ *
+ * @return array
+ */
+ public function getSuggests() {
+ $suggests = $this->parser->getAttribute('suggests');
+
+ if (!$suggests) {
+ $suggests = array();
+ }
+
+ $normalized = array();
+ foreach ($suggests as $suggest) {
+ $normalized[] = $this->normalizeDep($suggest);
+ }
+
+ return $normalized;
+ }
+
+ /**
+ * Normalizes a dependency array using the defined structs.
+ * Can be used with either requires or suggests.
+ *
+ * @param array $dep A dependency array.
+ * @return array The normalized deps array.
+ */
+ private function normalizeDep($dep) {
+ switch ($dep['type']) {
+ case 'elgg_version':
+ case 'elgg_release':
+ $struct = $this->depsStructElgg;
+ break;
+
+ case 'plugin':
+ $struct = $this->depsStructPlugin;
+ break;
+
+ case 'priority':
+ $struct = $this->depsStructPriority;
+ break;
+
+ case 'php_extension':
+ $struct = $this->depsStructPhpExtension;
+ break;
+
+ case 'php_ini':
+ $struct = $this->depsStructPhpIni;
+
+ // also normalize boolean values
+ if (isset($dep['value'])) {
+ switch (strtolower($dep['value'])) {
+ case 'yes':
+ case 'true':
+ case 'on':
+ case 1:
+ $dep['value'] = 1;
+ break;
+
+ case 'no':
+ case 'false':
+ case 'off':
+ case 0:
+ case '':
+ $dep['value'] = 0;
+ break;
+ }
+ }
+ break;
+ default:
+ // unrecognized so we just return the raw dependency
+ return $dep;
+ }
+
+ $normalized_dep = $this->buildStruct($struct, $dep);
+
+ // normalize comparison operators
+ if (isset($normalized_dep['comparison'])) {
+ switch ($normalized_dep['comparison']) {
+ case '<':
+ $normalized_dep['comparison'] = 'lt';
+ break;
+
+ case '<=':
+ $normalized_dep['comparison'] = 'le';
+ break;
+
+ case '>':
+ $normalized_dep['comparison'] = 'gt';
+ break;
+
+ case '>=':
+ $normalized_dep['comparison'] = 'ge';
+ break;
+
+ case '==':
+ case 'eq':
+ $normalized_dep['comparison'] = '=';
+ break;
+
+ case '<>':
+ case 'ne':
+ $normalized_dep['comparison'] = '!=';
+ break;
+ }
+ }
+
+ return $normalized_dep;
+ }
+
+ /**
+ * Returns the conflicts listed
+ *
+ * @return array
+ */
+ public function getConflicts() {
+ // normalize for 1.7
+ if ($this->getApiVersion() < 1.8) {
+ $conflicts = array();
+ } else {
+ $conflicts = $this->parser->getAttribute('conflicts');
+ }
+
+ if (!$conflicts) {
+ $conflicts = array();
+ }
+
+ $normalized = array();
+
+ foreach ($conflicts as $conflict) {
+ $normalized[] = $this->buildStruct($this->depsConflictsStruct, $conflict);
+ }
+
+ return $normalized;
+ }
+
+ /**
+ * Should this plugin be activated when Elgg is installed
+ *
+ * @return bool
+ */
+ public function getActivateOnInstall() {
+ $activate = $this->parser->getAttribute('activate_on_install');
+ switch (strtolower($activate)) {
+ case 'yes':
+ case 'true':
+ case 'on':
+ case 1:
+ return true;
+
+ case 'no':
+ case 'false':
+ case 'off':
+ case 0:
+ case '':
+ return false;
+ }
+ }
+
+ /**
+ * Normalizes an array into the structure specified
+ *
+ * @param array $struct The struct to normalize $element to.
+ * @param array $array The array
+ *
+ * @return array
+ */
+ protected function buildStruct(array $struct, array $array) {
+ $return = array();
+
+ foreach ($struct as $index => $default) {
+ $return[$index] = elgg_extract($index, $array, $default);
+ }
+
+ 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/ElggPluginManifestParser.php b/engine/classes/ElggPluginManifestParser.php
new file mode 100644
index 000000000..af152b561
--- /dev/null
+++ b/engine/classes/ElggPluginManifestParser.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Parent class for manifest parsers.
+ *
+ * Converts manifest.xml files or strings to an array.
+ *
+ * This should be extended by a class that does the actual work
+ * to convert based on the manifest.xml version.
+ *
+ * This class only parses XML to an XmlEntity object and
+ * an array. The array should be used primarily to extract
+ * information since it is quicker to parse once and store
+ * values from the XmlElement object than to parse the object
+ * each time.
+ *
+ * The array should be an exact representation of the manifest.xml
+ * file or string. Any normalization needs to be done in the
+ * calling class / function.
+ *
+ * @package Elgg.Core
+ * @subpackage Plugins
+ * @since 1.8
+ */
+abstract class ElggPluginManifestParser {
+ /**
+ * The XmlElement object
+ *
+ * @var XmlElement
+ */
+ protected $manifestObject;
+
+ /**
+ * The manifest array
+ *
+ * @var array
+ */
+ protected $manifest;
+
+ /**
+ * All valid manifest attributes with default values.
+ *
+ * @var array
+ */
+ protected $validAttributes;
+
+ /**
+ * The object we're doing parsing for.
+ *
+ * @var object
+ */
+ protected $caller;
+
+ /**
+ * Loads the manifest XML to be parsed.
+ *
+ * @param ElggXmlElement $xml The Manifest XML object to be parsed
+ * @param object $caller The object calling this parser.
+ */
+ public function __construct(ElggXMLElement $xml, $caller) {
+ $this->manifestObject = $xml;
+ $this->caller = $caller;
+ }
+
+ /**
+ * Returns the manifest XML object
+ *
+ * @return XmlElement
+ */
+ public function getManifestObject() {
+ return $this->manifestObject;
+ }
+
+ /**
+ * Return the parsed manifest array
+ *
+ * @return array
+ */
+ public function getManifest() {
+ return $this->manifest;
+ }
+
+ /**
+ * Return an attribute in the manifest.
+ *
+ * @param string $name Attribute name
+ * @return mixed
+ */
+ public function getAttribute($name) {
+ if (in_array($name, $this->validAttributes) && isset($this->manifest[$name])) {
+ return $this->manifest[$name];
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse the XML object into an array
+ *
+ * @return bool
+ */
+ abstract public function parse();
+}
diff --git a/engine/classes/ElggPluginManifestParser17.php b/engine/classes/ElggPluginManifestParser17.php
new file mode 100644
index 000000000..5658ee804
--- /dev/null
+++ b/engine/classes/ElggPluginManifestParser17.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Plugin manifest.xml parser for Elgg 1.7 and lower.
+ *
+ * @package Elgg.Core
+ * @subpackage Plugins
+ * @since 1.8
+ */
+class ElggPluginManifestParser17 extends ElggPluginManifestParser {
+ /**
+ * The valid top level attributes and defaults for a 1.7 manifest
+ */
+ protected $validAttributes = array(
+ 'author', 'version', 'description', 'website',
+ 'copyright', 'license', 'licence', 'elgg_version',
+
+ // were never really used and not enforced in code.
+ 'requires', 'recommends', 'conflicts',
+
+ // not a 1.7 field, but we need it
+ 'name',
+ );
+
+ /**
+ * Parse a manifest object from 1.7 or earlier.
+ *
+ * @return void
+ */
+ public function parse() {
+ if (!isset($this->manifestObject->children)) {
+ return false;
+ }
+
+ $elements = array();
+
+ foreach ($this->manifestObject->children as $element) {
+ $key = $element->attributes['key'];
+ $value = $element->attributes['value'];
+
+ // create arrays if multiple fields are set
+ if (array_key_exists($key, $elements)) {
+ if (!is_array($elements[$key])) {
+ $orig = $elements[$key];
+ $elements[$key] = array($orig);
+ }
+
+ $elements[$key][] = $value;
+ } else {
+ $elements[$key] = $value;
+ }
+ }
+
+ if ($elements && !array_key_exists('name', $elements)) {
+ $elements['name'] = $this->caller->getName();
+ }
+
+ $this->manifest = $elements;
+
+ if (!$this->manifest) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Return an attribute in the manifest.
+ *
+ * Overrides ElggPluginManifestParser::getAttribute() because before 1.8
+ * there were no rules...weeeeeeeee!
+ *
+ * @param string $name Attribute name
+ * @return mixed
+ */
+ public function getAttribute($name) {
+ if (isset($this->manifest[$name])) {
+ return $this->manifest[$name];
+ }
+
+ return false;
+ }
+}
diff --git a/engine/classes/ElggPluginManifestParser18.php b/engine/classes/ElggPluginManifestParser18.php
new file mode 100644
index 000000000..3b753f17b
--- /dev/null
+++ b/engine/classes/ElggPluginManifestParser18.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Plugin manifest.xml parser for Elgg 1.8 and above.
+ *
+ * @package Elgg.Core
+ * @subpackage Plugins
+ * @since 1.8
+ */
+class ElggPluginManifestParser18 extends ElggPluginManifestParser {
+ /**
+ * The valid top level attributes and defaults for a 1.8 manifest array.
+ *
+ * @var array
+ */
+ protected $validAttributes = array(
+ 'name', 'author', 'version', 'blurb', 'description','website',
+ 'repository', 'bugtracker', 'donations', 'copyright', 'license',
+ 'requires', 'suggests', 'conflicts', 'provides',
+ 'screenshot', 'category', 'activate_on_install'
+ );
+
+ /**
+ * Required attributes for a valid 1.8 manifest
+ *
+ * @var array
+ */
+ protected $requiredAttributes = array(
+ 'name', 'author', 'version', 'description', 'requires'
+ );
+
+ /**
+ * Parse a manifest object from 1.8 and later
+ *
+ * @return void
+ */
+ public function parse() {
+ $parsed = array();
+ foreach ($this->manifestObject->children as $element) {
+ switch ($element->name) {
+ // single elements
+ case 'blurb':
+ case 'description':
+ case 'name':
+ case 'author':
+ case 'version':
+ case 'website':
+ case 'copyright':
+ case 'license':
+ case 'repository':
+ case 'bugtracker':
+ case 'donations':
+ case 'activate_on_install':
+ $parsed[$element->name] = $element->content;
+ break;
+
+ // arrays
+ case 'category':
+ $parsed[$element->name][] = $element->content;
+ break;
+
+ // 3d arrays
+ case 'screenshot':
+ case 'provides':
+ case 'conflicts':
+ case 'requires':
+ case 'suggests':
+ if (!isset($element->children)) {
+ return false;
+ }
+
+ $info = array();
+ foreach ($element->children as $child_element) {
+ $info[$child_element->name] = $child_element->content;
+ }
+
+ $parsed[$element->name][] = $info;
+ break;
+ }
+ }
+
+ // check we have all the required fields
+ foreach ($this->requiredAttributes as $attr) {
+ if (!array_key_exists($attr, $parsed)) {
+ throw new PluginException(elgg_echo('PluginException:ParserErrorMissingRequiredAttribute',
+ array($attr, $this->caller->getPluginID())));
+ }
+ }
+
+ $this->manifest = $parsed;
+
+ if (!$this->manifest) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/engine/classes/ElggPluginPackage.php b/engine/classes/ElggPluginPackage.php
new file mode 100644
index 000000000..37eb4bf4d
--- /dev/null
+++ b/engine/classes/ElggPluginPackage.php
@@ -0,0 +1,640 @@
+<?php
+/**
+ * Manages plugin packages under mod.
+ *
+ * @todo This should eventually be merged into ElggPlugin.
+ * Currently ElggPlugin objects are only used to get and save
+ * plugin settings and user settings, so not every plugin
+ * has an ElggPlugin object. It's not implemented in ElggPlugin
+ * right now because of conflicts with at least the constructor,
+ * enable(), disable(), and private settings.
+ *
+ * Around 1.9 or so we should each plugin over to using
+ * ElggPlugin and merge ElggPluginPackage and ElggPlugin.
+ *
+ * @package Elgg.Core
+ * @subpackage Plugins
+ * @since 1.8
+ */
+class ElggPluginPackage {
+
+ /**
+ * The required files in the package
+ *
+ * @var array
+ */
+ private $requiredFiles = array(
+ 'start.php', 'manifest.xml'
+ );
+
+ /**
+ * The optional files that can be read and served through the markdown page handler
+ * @var array
+ */
+ private $textFiles = array(
+ 'README.txt', 'CHANGES.txt',
+ 'INSTALL.txt', 'COPYRIGHT.txt', 'LICENSE.txt',
+
+ 'README', 'README.md', 'README.markdown'
+ );
+
+ /**
+ * Valid types for provides.
+ *
+ * @var array
+ */
+ private $providesSupportedTypes = array(
+ 'plugin', 'php_extension'
+ );
+
+ /**
+ * The type of requires/conflicts supported
+ *
+ * @var array
+ */
+ private $depsSupportedTypes = array(
+ 'elgg_version', 'elgg_release', 'php_extension', 'php_ini', 'plugin', 'priority',
+ );
+
+ /**
+ * An invalid plugin error.
+ */
+ private $errorMsg = '';
+
+ /**
+ * Any dependencies messages
+ */
+ private $depsMsgs = array();
+
+ /**
+ * The plugin's manifest object
+ *
+ * @var ElggPluginManifest
+ */
+ protected $manifest;
+
+ /**
+ * The plugin's full path
+ *
+ * @var string
+ */
+ protected $path;
+
+ /**
+ * Is the plugin valid?
+ *
+ * @var mixed Bool after validation check, null before.
+ */
+ protected $valid = null;
+
+ /**
+ * The plugin ID (dir name)
+ *
+ * @var string
+ */
+ protected $id;
+
+ /**
+ * Load a plugin package from mod/$id or by full path.
+ *
+ * @param string $plugin The ID (directory name) or full path of the plugin.
+ * @param bool $validate Automatically run isValid()?
+ *
+ * @throws PluginException
+ */
+ public function __construct($plugin, $validate = true) {
+ $plugin_path = elgg_get_plugins_path();
+ // @todo wanted to avoid another is_dir() call here.
+ // should do some profiling to see how much it affects
+ if (strpos($plugin, $plugin_path) === 0 || is_dir($plugin)) {
+ // this is a path
+ $path = sanitise_filepath($plugin);
+
+ // the id is the last element of the array
+ $path_array = explode('/', trim($path, '/'));
+ $id = array_pop($path_array);
+ } else {
+ // this is a plugin id
+ // strict plugin names
+ if (preg_match('/[^a-z0-9\.\-_]/i', $plugin)) {
+ throw new PluginException(elgg_echo('PluginException:InvalidID', array($plugin)));
+ }
+
+ $path = "{$plugin_path}$plugin/";
+ $id = $plugin;
+ }
+
+ if (!is_dir($path)) {
+ throw new PluginException(elgg_echo('PluginException:InvalidPath', array($path)));
+ }
+
+ $this->path = $path;
+ $this->id = $id;
+
+ if ($validate && !$this->isValid()) {
+ if ($this->errorMsg) {
+ throw new PluginException(elgg_echo('PluginException:InvalidPlugin:Details',
+ array($plugin, $this->errorMsg)));
+ } else {
+ throw new PluginException(elgg_echo('PluginException:InvalidPlugin', array($plugin)));
+ }
+ }
+
+ return true;
+ }
+
+ /********************************
+ * Validation and sanity checks *
+ ********************************/
+
+ /**
+ * Checks if this is a valid Elgg plugin.
+ *
+ * Checks for requires files as defined at the start of this
+ * class. Will check require manifest fields via ElggPluginManifest
+ * for Elgg 1.8 plugins.
+ *
+ * @note This doesn't check dependencies or conflicts.
+ * Use {@link ElggPluginPackage::canActivate()} or
+ * {@link ElggPluginPackage::checkDependencies()} for that.
+ *
+ * @return bool
+ */
+ public function isValid() {
+ if (isset($this->valid)) {
+ return $this->valid;
+ }
+
+ // check required files.
+ $have_req_files = true;
+ foreach ($this->requiredFiles as $file) {
+ if (!is_readable($this->path . $file)) {
+ $have_req_files = false;
+ $this->errorMsg =
+ elgg_echo('ElggPluginPackage:InvalidPlugin:MissingFile', array($file));
+ break;
+ }
+ }
+
+ // check required files
+ if (!$have_req_files) {
+ return $this->valid = false;
+ }
+
+ // check for valid manifest.
+ if (!$this->loadManifest()) {
+ return $this->valid = false;
+ }
+
+ // can't require or conflict with yourself or something you provide.
+ // make sure provides are all valid.
+ if (!$this->isSaneDeps()) {
+ return $this->valid = false;
+ }
+
+ return $this->valid = true;
+ }
+
+ /**
+ * Check the plugin doesn't require or conflict with itself
+ * or something provides. Also check that it only list
+ * valid provides. Deps are checked in checkDependencies()
+ *
+ * @note Plugins always provide themselves.
+ *
+ * @todo Don't let them require and conflict the same thing
+ *
+ * @return bool
+ */
+ private function isSaneDeps() {
+ // protection against plugins with no manifest file
+ if (!$this->getManifest()) {
+ return false;
+ }
+
+ // Note: $conflicts and $requires are not unused. They're called dynamically
+ $conflicts = $this->getManifest()->getConflicts();
+ $requires = $this->getManifest()->getRequires();
+ $provides = $this->getManifest()->getProvides();
+
+ foreach ($provides as $provide) {
+ // only valid provide types
+ if (!in_array($provide['type'], $this->providesSupportedTypes)) {
+ $this->errorMsg =
+ elgg_echo('ElggPluginPackage:InvalidPlugin:InvalidProvides', array($provide['type']));
+ return false;
+ }
+
+ // doesn't conflict or require any of its provides
+ $name = $provide['name'];
+ foreach (array('conflicts', 'requires') as $dep_type) {
+ foreach (${$dep_type} as $dep) {
+ if (!in_array($dep['type'], $this->depsSupportedTypes)) {
+ $this->errorMsg =
+ elgg_echo('ElggPluginPackage:InvalidPlugin:InvalidDependency', array($dep['type']));
+ return false;
+ }
+
+ // make sure nothing is providing something it conflicts or requires.
+ if (isset($dep['name']) && $dep['name'] == $name) {
+ $version_compare = version_compare($provide['version'], $dep['version'], $dep['comparison']);
+
+ if ($version_compare) {
+ $this->errorMsg =
+ elgg_echo('ElggPluginPackage:InvalidPlugin:CircularDep',
+ array($dep['type'], $dep['name'], $this->id));
+
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+ /************
+ * Manifest *
+ ************/
+
+ /**
+ * Returns a parsed manifest file.
+ *
+ * @return ElggPluginManifest
+ */
+ public function getManifest() {
+ if (!$this->manifest) {
+ if (!$this->loadManifest()) {
+ return false;
+ }
+ }
+
+ return $this->manifest;
+ }
+
+ /**
+ * Loads the manifest into this->manifest as an
+ * ElggPluginManifest object.
+ *
+ * @return bool
+ */
+ private function loadManifest() {
+ $file = $this->path . 'manifest.xml';
+
+ try {
+ $this->manifest = new ElggPluginManifest($file, $this->id);
+ } catch (Exception $e) {
+ $this->errorMsg = $e->getMessage();
+ return false;
+ }
+
+ if ($this->manifest instanceof ElggPluginManifest) {
+ return true;
+ }
+
+ $this->errorMsg = elgg_echo('unknown_error');
+ return false;
+ }
+
+ /****************
+ * Readme Files *
+ ***************/
+
+ /**
+ * Returns an array of present and readable text files
+ *
+ * @return array
+ */
+ public function getTextFilenames() {
+ return $this->textFiles;
+ }
+
+ /***********************
+ * Dependencies system *
+ ***********************/
+
+ /**
+ * Returns if the Elgg system meets the plugin's dependency
+ * requirements. This includes both requires and conflicts.
+ *
+ * Full reports can be requested. The results are returned
+ * as an array of arrays in the form array(
+ * 'type' => requires|conflicts,
+ * 'dep' => array( dependency array ),
+ * 'status' => bool if depedency is met,
+ * 'comment' => optional comment to display to the user.
+ * )
+ *
+ * @param bool $full_report Return a full report.
+ * @return bool|array
+ */
+ public function checkDependencies($full_report = false) {
+ // Note: $conflicts and $requires are not unused. They're called dynamically
+ $requires = $this->getManifest()->getRequires();
+ $conflicts = $this->getManifest()->getConflicts();
+
+ $enabled_plugins = elgg_get_plugins('active');
+ $this_id = $this->getID();
+ $report = array();
+
+ // first, check if any active plugin conflicts with us.
+ foreach ($enabled_plugins as $plugin) {
+ $temp_conflicts = array();
+ $temp_manifest = $plugin->getManifest();
+ if ($temp_manifest instanceof ElggPluginManifest) {
+ $temp_conflicts = $plugin->getManifest()->getConflicts();
+ }
+ foreach ($temp_conflicts as $conflict) {
+ if ($conflict['type'] == 'plugin' && $conflict['name'] == $this_id) {
+ $result = $this->checkDepPlugin($conflict, $enabled_plugins, false);
+
+ // rewrite the conflict to show the originating plugin
+ $conflict['name'] = $plugin->getManifest()->getName();
+
+ if (!$full_report && !$result['status']) {
+ $this->errorMsg = "Conflicts with plugin \"{$plugin->getManifest()->getName()}\".";
+ return $result['status'];
+ } else {
+ $report[] = array(
+ 'type' => 'conflicted',
+ 'dep' => $conflict,
+ 'status' => $result['status'],
+ 'value' => $this->getManifest()->getVersion()
+ );
+ }
+ }
+ }
+ }
+
+ $check_types = array('requires', 'conflicts');
+
+ if ($full_report) {
+ // Note: $suggests is not unused. It's called dynamically
+ $suggests = $this->getManifest()->getSuggests();
+ $check_types[] = 'suggests';
+ }
+
+ foreach ($check_types as $dep_type) {
+ $inverse = ($dep_type == 'conflicts') ? true : false;
+
+ foreach (${$dep_type} as $dep) {
+ switch ($dep['type']) {
+ case 'elgg_version':
+ $result = $this->checkDepElgg($dep, get_version(), $inverse);
+ break;
+
+ case 'elgg_release':
+ $result = $this->checkDepElgg($dep, get_version(true), $inverse);
+ break;
+
+ case 'plugin':
+ $result = $this->checkDepPlugin($dep, $enabled_plugins, $inverse);
+ break;
+
+ case 'priority':
+ $result = $this->checkDepPriority($dep, $enabled_plugins, $inverse);
+ break;
+
+ case 'php_extension':
+ $result = $this->checkDepPhpExtension($dep, $inverse);
+ break;
+
+ case 'php_ini':
+ $result = $this->checkDepPhpIni($dep, $inverse);
+ break;
+ }
+
+ // unless we're doing a full report, break as soon as we fail.
+ if (!$full_report && !$result['status']) {
+ $this->errorMsg = "Missing dependencies.";
+ return $result['status'];
+ } else {
+ // build report element and comment
+ $report[] = array(
+ 'type' => $dep_type,
+ 'dep' => $dep,
+ 'status' => $result['status'],
+ 'value' => $result['value']
+ );
+ }
+ }
+ }
+
+ if ($full_report) {
+ // add provides to full report
+ $provides = $this->getManifest()->getProvides();
+
+ foreach ($provides as $provide) {
+ $report[] = array(
+ 'type' => 'provides',
+ 'dep' => $provide,
+ 'status' => true,
+ 'value' => ''
+ );
+ }
+
+ return $report;
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks if $plugins meets the requirement by $dep.
+ *
+ * @param array $dep An Elgg manifest.xml deps array
+ * @param array $plugins A list of plugins as returned by elgg_get_plugins();
+ * @param bool $inverse Inverse the results to use as a conflicts.
+ * @return bool
+ */
+ private function checkDepPlugin(array $dep, array $plugins, $inverse = false) {
+ $r = elgg_check_plugins_provides('plugin', $dep['name'], $dep['version'], $dep['comparison']);
+
+ if ($inverse) {
+ $r['status'] = !$r['status'];
+ }
+
+ return $r;
+ }
+
+ /**
+ * Checks if $plugins meets the requirement by $dep.
+ *
+ * @param array $dep An Elgg manifest.xml deps array
+ * @param array $plugins A list of plugins as returned by elgg_get_plugins();
+ * @param bool $inverse Inverse the results to use as a conflicts.
+ * @return bool
+ */
+ private function checkDepPriority(array $dep, array $plugins, $inverse = false) {
+ // grab the ElggPlugin using this package.
+ $plugin_package = elgg_get_plugin_from_id($this->getID());
+ $plugin_priority = $plugin_package->getPriority();
+ $test_plugin = elgg_get_plugin_from_id($dep['plugin']);
+
+ // If this isn't a plugin or the plugin isn't installed or active
+ // priority doesn't matter. Use requires to check if a plugin is active.
+ if (!$plugin_package || !$test_plugin || !$test_plugin->isActive()) {
+ return array(
+ 'status' => true,
+ 'value' => 'uninstalled'
+ );
+ }
+
+ $test_plugin_priority = $test_plugin->getPriority();
+
+ switch ($dep['priority']) {
+ case 'before':
+ $status = $plugin_priority < $test_plugin_priority;
+ break;
+
+ case 'after':
+ $status = $plugin_priority > $test_plugin_priority;
+ break;
+
+ default;
+ $status = false;
+ }
+
+ // get the current value
+ if ($plugin_priority < $test_plugin_priority) {
+ $value = 'before';
+ } else {
+ $value = 'after';
+ }
+
+ if ($inverse) {
+ $status = !$status;
+ }
+
+ return array(
+ 'status' => $status,
+ 'value' => $value
+ );
+ }
+
+ /**
+ * Checks if $elgg_version meets the requirement by $dep.
+ *
+ * @param array $dep An Elgg manifest.xml deps array
+ * @param array $elgg_version An Elgg version (either YYYYMMDDXX or X.Y.Z)
+ * @param bool $inverse Inverse the result to use as a conflicts.
+ * @return bool
+ */
+ private function checkDepElgg(array $dep, $elgg_version, $inverse = false) {
+ $status = version_compare($elgg_version, $dep['version'], $dep['comparison']);
+
+ if ($inverse) {
+ $status = !$status;
+ }
+
+ return array(
+ 'status' => $status,
+ 'value' => $elgg_version
+ );
+ }
+
+ /**
+ * Checks if the PHP extension in $dep is loaded.
+ *
+ * @todo Can this be merged with the plugin checker?
+ *
+ * @param array $dep An Elgg manifest.xml deps array
+ * @param bool $inverse Inverse the result to use as a conflicts.
+ * @return array An array in the form array(
+ * 'status' => bool
+ * 'value' => string The version provided
+ * )
+ */
+ private function checkDepPhpExtension(array $dep, $inverse = false) {
+ $name = $dep['name'];
+ $version = $dep['version'];
+ $comparison = $dep['comparison'];
+
+ // not enabled.
+ $status = extension_loaded($name);
+
+ // enabled. check version.
+ $ext_version = phpversion($name);
+
+ if ($status) {
+ // some extensions (like gd) don't provide versions. neat.
+ // don't check version info and return a lie.
+ if ($ext_version && $version) {
+ $status = version_compare($ext_version, $version, $comparison);
+ }
+
+ if (!$ext_version) {
+ $ext_version = '???';
+ }
+ }
+
+ // some php extensions can be emulated, so check provides.
+ if ($status == false) {
+ $provides = elgg_check_plugins_provides('php_extension', $name, $version, $comparison);
+ $status = $provides['status'];
+ $ext_version = $provides['value'];
+ }
+
+ if ($inverse) {
+ $status = !$status;
+ }
+
+ return array(
+ 'status' => $status,
+ 'value' => $ext_version
+ );
+ }
+
+ /**
+ * Check if the PHP ini setting satisfies $dep.
+ *
+ * @param array $dep An Elgg manifest.xml deps array
+ * @param bool $inverse Inverse the result to use as a conflicts.
+ * @return bool
+ */
+ private function checkDepPhpIni($dep, $inverse = false) {
+ $name = $dep['name'];
+ $value = $dep['value'];
+ $comparison = $dep['comparison'];
+
+ // ini_get() normalizes truthy values to 1 but falsey values to 0 or ''.
+ // version_compare() considers '' < 0, so normalize '' to 0.
+ // ElggPluginManifest normalizes all bool values and '' to 1 or 0.
+ $setting = ini_get($name);
+
+ if ($setting === '') {
+ $setting = 0;
+ }
+
+ $status = version_compare($setting, $value, $comparison);
+
+ if ($inverse) {
+ $status = !$status;
+ }
+
+ return array(
+ 'status' => $status,
+ 'value' => $setting
+ );
+ }
+
+ /**
+ * Returns the Plugin ID
+ *
+ * @return string
+ */
+ public function getID() {
+ return $this->id;
+ }
+
+ /**
+ * Returns the last error message.
+ *
+ * @return string
+ */
+ public function getError() {
+ return $this->errorMsg;
+ }
+}
diff --git a/engine/classes/ElggPriorityList.php b/engine/classes/ElggPriorityList.php
new file mode 100644
index 000000000..416df885c
--- /dev/null
+++ b/engine/classes/ElggPriorityList.php
@@ -0,0 +1,366 @@
+<?php
+/**
+ * Iterate over elements in a specific priority.
+ *
+ * $pl = new ElggPriorityList();
+ * $pl->add('Element 0');
+ * $pl->add('Element 10', 10);
+ * $pl->add('Element -10', -10);
+ *
+ * foreach ($pl as $priority => $element) {
+ * var_dump("$priority => $element");
+ * }
+ *
+ * Yields:
+ * -10 => Element -10
+ * 0 => Element 0
+ * 10 => Element 10
+ *
+ * Collisions on priority are handled by inserting the element at or as close to the
+ * requested priority as possible:
+ *
+ * $pl = new ElggPriorityList();
+ * $pl->add('Element 5', 5);
+ * $pl->add('Colliding element 5', 5);
+ * $pl->add('Another colliding element 5', 5);
+ *
+ * foreach ($pl as $priority => $element) {
+ * var_dump("$priority => $element");
+ * }
+ *
+ * Yields:
+ * 5 => 'Element 5',
+ * 6 => 'Colliding element 5',
+ * 7 => 'Another colliding element 5'
+ *
+ * You can do priority lookups by element:
+ *
+ * $pl = new ElggPriorityList();
+ * $pl->add('Element 0');
+ * $pl->add('Element -5', -5);
+ * $pl->add('Element 10', 10);
+ * $pl->add('Element -10', -10);
+ *
+ * $priority = $pl->getPriority('Element -5');
+ *
+ * Or element lookups by priority.
+ * $element = $pl->getElement(-5);
+ *
+ * To remove elements, pass the element.
+ * $pl->remove('Element -10');
+ *
+ * To check if an element exists:
+ * $pl->contains('Element -5');
+ *
+ * To move an element:
+ * $pl->move('Element -5', -3);
+ *
+ * ElggPriorityList only tracks priority. No checking is done in ElggPriorityList for duplicates or
+ * updating. If you need to track this use objects and an external map:
+ *
+ * function elgg_register_something($id, $display_name, $location, $priority = 500) {
+ * // $id => $element.
+ * static $map = array();
+ * static $list;
+ *
+ * if (!$list) {
+ * $list = new ElggPriorityList();
+ * }
+ *
+ * // update if already registered.
+ * if (isset($map[$id])) {
+ * $element = $map[$id];
+ * // move it first because we have to pass the original element.
+ * if (!$list->move($element, $priority)) {
+ * return false;
+ * }
+ * $element->display_name = $display_name;
+ * $element->location = $location;
+ * } else {
+ * $element = new stdClass();
+ * $element->display_name = $display_name;
+ * $element->location = $location;
+ * if (!$list->add($element, $priority)) {
+ * return false;
+ * }
+ * $map[$id] = $element;
+ * }
+ *
+ * return true;
+ * }
+ *
+ * @package Elgg.Core
+ * @subpackage Helpers
+ */
+class ElggPriorityList
+ implements Iterator, Countable {
+
+ /**
+ * The list of elements
+ *
+ * @var array
+ */
+ private $elements = array();
+
+ /**
+ * Create a new priority list.
+ *
+ * @param array $elements An optional array of priorities => element
+ */
+ public function __construct(array $elements = array()) {
+ if ($elements) {
+ foreach ($elements as $priority => $element) {
+ $this->add($element, $priority);
+ }
+ }
+ }
+
+ /**
+ * Adds an element to the list.
+ *
+ * @warning This returns the priority at which the element was added, which can be 0. Use
+ * !== false to check for success.
+ *
+ * @param mixed $element The element to add to the list.
+ * @param mixed $priority Priority to add the element. In priority collisions, the original element
+ * maintains its priority and the new element is to the next available
+ * slot, taking into consideration all previously registered elements.
+ * Negative elements are accepted.
+ * @param bool $exact unused
+ * @return int The priority of the added element.
+ * @todo remove $exact or implement it. Note we use variable name strict below.
+ */
+ public function add($element, $priority = null, $exact = false) {
+ if ($priority !== null && !is_numeric($priority)) {
+ return false;
+ } else {
+ $priority = $this->getNextPriority($priority);
+ }
+
+ $this->elements[$priority] = $element;
+ $this->sorted = false;
+ return $priority;
+ }
+
+ /**
+ * Removes an element from the list.
+ *
+ * @warning The element must have the same attributes / values. If using $strict, it must have
+ * the same types. array(10) will fail in strict against array('10') (str vs int).
+ *
+ * @param mixed $element The element to remove from the list
+ * @param bool $strict Whether to check the type of the element match
+ * @return bool
+ */
+ public function remove($element, $strict = false) {
+ $index = array_search($element, $this->elements, $strict);
+ if ($index !== false) {
+ unset($this->elements[$index]);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Move an existing element to a new priority.
+ *
+ * @param mixed $element The element to move
+ * @param int $new_priority The new priority for the element
+ * @param bool $strict Whether to check the type of the element match
+ * @return bool
+ */
+ public function move($element, $new_priority, $strict = false) {
+ $new_priority = (int) $new_priority;
+
+ $current_priority = $this->getPriority($element, $strict);
+ if ($current_priority === false) {
+ return false;
+ }
+
+ if ($current_priority == $new_priority) {
+ return true;
+ }
+
+ // move the actual element so strict operations still work
+ $element = $this->getElement($current_priority);
+ unset($this->elements[$current_priority]);
+ return $this->add($element, $new_priority);
+ }
+
+ /**
+ * Returns the elements
+ *
+ * @return array
+ */
+ public function getElements() {
+ $this->sortIfUnsorted();
+ return $this->elements;
+ }
+
+ /**
+ * Sort the elements optionally by a callback function.
+ *
+ * If no user function is provided the elements are sorted by priority registered.
+ *
+ * The callback function should accept the array of elements as the first
+ * argument and should return a sorted array.
+ *
+ * This function can be called multiple times.
+ *
+ * @param callback $callback The callback for sorting. Numeric sorting is the default.
+ * @return bool
+ */
+ public function sort($callback = null) {
+ if (!$callback) {
+ ksort($this->elements, SORT_NUMERIC);
+ } else {
+ $sorted = call_user_func($callback, $this->elements);
+
+ if (!$sorted) {
+ return false;
+ }
+
+ $this->elements = $sorted;
+ }
+
+ $this->sorted = true;
+ return true;
+ }
+
+ /**
+ * Sort the elements if they haven't been sorted yet.
+ *
+ * @return bool
+ */
+ private function sortIfUnsorted() {
+ if (!$this->sorted) {
+ return $this->sort();
+ }
+ }
+
+ /**
+ * Returns the next priority available.
+ *
+ * @param int $near Make the priority as close to $near as possible.
+ * @return int
+ */
+ public function getNextPriority($near = 0) {
+ $near = (int) $near;
+
+ while (array_key_exists($near, $this->elements)) {
+ $near++;
+ }
+
+ return $near;
+ }
+
+ /**
+ * Returns the priority of an element if it exists in the list.
+ *
+ * @warning This can return 0 if the element's priority is 0.
+ *
+ * @param mixed $element The element to check for.
+ * @param bool $strict Use strict checking?
+ * @return mixed False if the element doesn't exists, the priority if it does.
+ */
+ public function getPriority($element, $strict = false) {
+ return array_search($element, $this->elements, $strict);
+ }
+
+ /**
+ * Returns the element at $priority.
+ *
+ * @param int $priority The priority
+ * @return mixed The element or false on fail.
+ */
+ public function getElement($priority) {
+ return (isset($this->elements[$priority])) ? $this->elements[$priority] : false;
+ }
+
+ /**
+ * Returns if the list contains $element.
+ *
+ * @param mixed $element The element to check.
+ * @param bool $strict Use strict checking?
+ * @return bool
+ */
+ public function contains($element, $strict = false) {
+ return $this->getPriority($element, $strict) !== false;
+ }
+
+
+ /**********************
+ * Interface methods *
+ **********************/
+
+ /**
+ * Iterator
+ */
+
+ /**
+ * PHP Iterator Interface
+ *
+ * @see Iterator::rewind()
+ * @return void
+ */
+ public function rewind() {
+ $this->sortIfUnsorted();
+ return reset($this->elements);
+ }
+
+ /**
+ * PHP Iterator Interface
+ *
+ * @see Iterator::current()
+ * @return mixed
+ */
+ public function current() {
+ $this->sortIfUnsorted();
+ return current($this->elements);
+ }
+
+ /**
+ * PHP Iterator Interface
+ *
+ * @see Iterator::key()
+ * @return int
+ */
+ public function key() {
+ $this->sortIfUnsorted();
+ return key($this->elements);
+ }
+
+ /**
+ * PHP Iterator Interface
+ *
+ * @see Iterator::next()
+ * @return mixed
+ */
+ public function next() {
+ $this->sortIfUnsorted();
+ return next($this->elements);
+ }
+
+ /**
+ * PHP Iterator Interface
+ *
+ * @see Iterator::valid()
+ * @return bool
+ */
+ public function valid() {
+ $this->sortIfUnsorted();
+ $key = key($this->elements);
+ return ($key !== NULL && $key !== FALSE);
+ }
+
+ /**
+ * Countable interface
+ *
+ * @see Countable::count()
+ * @return int
+ */
+ public function count() {
+ return count($this->elements);
+ }
+} \ No newline at end of file
diff --git a/engine/classes/ElggRelationship.php b/engine/classes/ElggRelationship.php
new file mode 100644
index 000000000..d2e88882a
--- /dev/null
+++ b/engine/classes/ElggRelationship.php
@@ -0,0 +1,231 @@
+<?php
+/**
+ * Relationship class.
+ *
+ * @package Elgg.Core
+ * @subpackage Core
+ *
+ * @property int $id The unique identifier (read-only)
+ * @property int $guid_one The GUID of the subject of the relationship
+ * @property string $relationship The name of the relationship
+ * @property int $guid_two The GUID of the object of the relationship
+ * @property int $time_created A UNIX timestamp of when the relationship was created (read-only, set on first save)
+ */
+class ElggRelationship extends ElggData implements
+ Importable
+{
+
+ /**
+ * Create a relationship object, optionally from a given id value or row.
+ *
+ * @param mixed $id ElggRelationship id, database row, or null for new relationship
+ */
+ function __construct($id = null) {
+ $this->initializeAttributes();
+
+ if (!empty($id)) {
+ if ($id instanceof stdClass) {
+ $relationship = $id; // Create from db row
+ } else {
+ $relationship = get_relationship($id);
+ }
+
+ if ($relationship) {
+ $objarray = (array) $relationship;
+ foreach ($objarray as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+ }
+ }
+ }
+
+ /**
+ * Class member get overloading
+ *
+ * @param string $name Name
+ *
+ * @return mixed
+ */
+ function get($name) {
+ if (array_key_exists($name, $this->attributes)) {
+ return $this->attributes[$name];
+ }
+
+ return null;
+ }
+
+ /**
+ * Class member set overloading
+ *
+ * @param string $name Name
+ * @param mixed $value Value
+ *
+ * @return mixed
+ */
+ function set($name, $value) {
+ $this->attributes[$name] = $value;
+ return true;
+ }
+
+ /**
+ * Save the relationship
+ *
+ * @return int the relationship id
+ * @throws IOException
+ */
+ public function save() {
+ if ($this->id > 0) {
+ delete_relationship($this->id);
+ }
+
+ $this->id = add_entity_relationship($this->guid_one, $this->relationship, $this->guid_two);
+ if (!$this->id) {
+ throw new IOException(elgg_echo('IOException:UnableToSaveNew', array(get_class())));
+ }
+
+ return $this->id;
+ }
+
+ /**
+ * Delete a given relationship.
+ *
+ * @return bool
+ */
+ public function delete() {
+ return delete_relationship($this->id);
+ }
+
+ /**
+ * Get a URL for this relationship.
+ *
+ * @return string
+ */
+ public function getURL() {
+ return get_relationship_url($this->id);
+ }
+
+ // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
+
+ /**
+ * Return an array of fields which can be exported.
+ *
+ * @return array
+ */
+ public function getExportableValues() {
+ return array(
+ 'id',
+ 'guid_one',
+ 'relationship',
+ 'guid_two'
+ );
+ }
+
+ /**
+ * Export this relationship
+ *
+ * @return array
+ */
+ public function export() {
+ $uuid = get_uuid_from_object($this);
+ $relationship = new ODDRelationship(
+ guid_to_uuid($this->guid_one),
+ $this->relationship,
+ guid_to_uuid($this->guid_two)
+ );
+
+ $relationship->setAttribute('uuid', $uuid);
+
+ return $relationship;
+ }
+
+ // IMPORTABLE INTERFACE ////////////////////////////////////////////////////////////
+
+ /**
+ * Import a relationship
+ *
+ * @param ODD $data ODD data
+
+ * @return bool
+ * @throws ImportException|InvalidParameterException
+ */
+ public function import(ODD $data) {
+ if (!($data instanceof ODDRelationship)) {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnexpectedODDClass'));
+ }
+
+ $uuid_one = $data->getAttribute('uuid1');
+ $uuid_two = $data->getAttribute('uuid2');
+
+ // See if this entity has already been imported, if so then we need to link to it
+ $entity1 = get_entity_from_uuid($uuid_one);
+ $entity2 = get_entity_from_uuid($uuid_two);
+ if (($entity1) && ($entity2)) {
+ // Set the item ID
+ $this->attributes['guid_one'] = $entity1->getGUID();
+ $this->attributes['guid_two'] = $entity2->getGUID();
+
+ // Map verb to relationship
+ //$verb = $data->getAttribute('verb');
+ //$relationship = get_relationship_from_verb($verb);
+ $relationship = $data->getAttribute('type');
+
+ if ($relationship) {
+ $this->attributes['relationship'] = $relationship;
+ // save
+ $result = $this->save();
+ if (!$result) {
+ throw new ImportException(elgg_echo('ImportException:ProblemSaving', array(get_class())));
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
+
+ /**
+ * Return an identification for the object for storage in the system log.
+ * This id must be an integer.
+ *
+ * @return int
+ */
+ public function getSystemLogID() {
+ return $this->id;
+ }
+
+ /**
+ * For a given ID, return the object associated with it.
+ * This is used by the river functionality primarily.
+ * This is useful for checking access permissions etc on objects.
+ *
+ * @param int $id ID
+ *
+ * @return ElggRelationship
+ */
+ public function getObjectFromID($id) {
+ return get_relationship($id);
+ }
+
+ /**
+ * Return a type of the object - eg. object, group, user, relationship, metadata, annotation etc
+ *
+ * @return string 'relationship'
+ */
+ public function getType() {
+ return 'relationship';
+ }
+
+ /**
+ * Return a subtype. For metadata & annotations this is the 'name' and for relationship this
+ * is the relationship type.
+ *
+ * @return string
+ */
+ public function getSubtype() {
+ return $this->relationship;
+ }
+
+}
diff --git a/engine/classes/ElggRiverItem.php b/engine/classes/ElggRiverItem.php
new file mode 100644
index 000000000..d3d09cd91
--- /dev/null
+++ b/engine/classes/ElggRiverItem.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * River item class.
+ *
+ * @package Elgg.Core
+ * @subpackage Core
+ *
+ * @property int $id The unique identifier (read-only)
+ * @property int $subject_guid The GUID of the actor
+ * @property int $object_guid The GUID of the object
+ * @property int $annotation_id The ID of the annotation involved in the action
+ * @property string $type The type of one of the entities involved in the action
+ * @property string $subtype The subtype of one of the entities involved in the action
+ * @property string $action_type The name of the action
+ * @property string $view The view for displaying this river item
+ * @property int $access_id The visibility of the river item
+ * @property int $posted UNIX timestamp when the action occurred
+ */
+class ElggRiverItem {
+ public $id;
+ public $subject_guid;
+ public $object_guid;
+ public $annotation_id;
+ public $type;
+ public $subtype;
+ public $action_type;
+ public $access_id;
+ public $view;
+ public $posted;
+
+ /**
+ * Construct a river item object given a database row.
+ *
+ * @param stdClass $object Object obtained from database
+ */
+ function __construct($object) {
+ if (!($object instanceof stdClass)) {
+ // throw exception
+ }
+
+ // the casting is to support typed serialization like json
+ $int_types = array('id', 'subject_guid', 'object_guid', 'annotation_id', 'access_id', 'posted');
+ foreach ($object as $key => $value) {
+ if (in_array($key, $int_types)) {
+ $this->$key = (int)$value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+
+ /**
+ * Get the subject of this river item
+ *
+ * @return ElggEntity
+ */
+ public function getSubjectEntity() {
+ return get_entity($this->subject_guid);
+ }
+
+ /**
+ * Get the object of this river item
+ *
+ * @return ElggEntity
+ */
+ public function getObjectEntity() {
+ return get_entity($this->object_guid);
+ }
+
+ /**
+ * Get the Annotation for this river item
+ *
+ * @return ElggAnnotation
+ */
+ public function getAnnotation() {
+ return elgg_get_annotation_from_id($this->annotation_id);
+ }
+
+ /**
+ * Get the view used to display this river item
+ *
+ * @return string
+ */
+ public function getView() {
+ return $this->view;
+ }
+
+ /**
+ * Get the time this activity was posted
+ *
+ * @return int
+ */
+ public function getPostedTime() {
+ return (int)$this->posted;
+ }
+
+ /**
+ * Get the type of the object
+ *
+ * @return string 'river'
+ */
+ public function getType() {
+ return 'river';
+ }
+
+ /**
+ * Get the subtype of the object
+ *
+ * @return string 'item'
+ */
+ public function getSubtype() {
+ return 'item';
+ }
+
+}
diff --git a/engine/classes/ElggSession.php b/engine/classes/ElggSession.php
new file mode 100644
index 000000000..9750f063e
--- /dev/null
+++ b/engine/classes/ElggSession.php
@@ -0,0 +1,153 @@
+<?php
+/**
+ * Magic session class.
+ * This class is intended to extend the $_SESSION magic variable by providing an API hook
+ * to plug in other values.
+ *
+ * Primarily this is intended to provide a way of supplying "logged in user"
+ * details without touching the session (which can cause problems when
+ * accessed server side).
+ *
+ * If a value is present in the session then that value is returned, otherwise
+ * a plugin hook 'session:get', '$var' is called, where $var is the variable
+ * being requested.
+ *
+ * Setting values will store variables in the session in the normal way.
+ *
+ * LIMITATIONS: You can not access multidimensional arrays
+ *
+ * @package Elgg.Core
+ * @subpackage Sessions
+ */
+class ElggSession implements ArrayAccess {
+ /** Local cache of trigger retrieved variables */
+ private static $__localcache;
+
+ /**
+ * Test if property is set either as an attribute or metadata.
+ *
+ * @param string $key The name of the attribute or metadata.
+ *
+ * @return bool
+ */
+ function __isset($key) {
+ return $this->offsetExists($key);
+ }
+
+ /**
+ * Set a value, go straight to session.
+ *
+ * @param string $key Name
+ * @param mixed $value Value
+ *
+ * @return void
+ */
+ function offsetSet($key, $value) {
+ $_SESSION[$key] = $value;
+ }
+
+ /**
+ * Get a variable from either the session, or if its not in the session
+ * attempt to get it from an api call.
+ *
+ * @see ArrayAccess::offsetGet()
+ *
+ * @param mixed $key Name
+ *
+ * @return mixed
+ */
+ function offsetGet($key) {
+ if (!ElggSession::$__localcache) {
+ ElggSession::$__localcache = array();
+ }
+
+ if (isset($_SESSION[$key])) {
+ return $_SESSION[$key];
+ }
+
+ if (isset(ElggSession::$__localcache[$key])) {
+ return ElggSession::$__localcache[$key];
+ }
+
+ $value = NULL;
+ $value = elgg_trigger_plugin_hook('session:get', $key, NULL, $value);
+
+ ElggSession::$__localcache[$key] = $value;
+
+ return ElggSession::$__localcache[$key];
+ }
+
+ /**
+ * Unset a value from the cache and the session.
+ *
+ * @see ArrayAccess::offsetUnset()
+ *
+ * @param mixed $key Name
+ *
+ * @return void
+ */
+ function offsetUnset($key) {
+ unset(ElggSession::$__localcache[$key]);
+ unset($_SESSION[$key]);
+ }
+
+ /**
+ * Return whether the value is set in either the session or the cache.
+ *
+ * @see ArrayAccess::offsetExists()
+ *
+ * @param int $offset Offset
+ *
+ * @return bool
+ */
+ function offsetExists($offset) {
+ if (isset(ElggSession::$__localcache[$offset])) {
+ return true;
+ }
+
+ if (isset($_SESSION[$offset])) {
+ return true;
+ }
+
+ if ($this->offsetGet($offset)) {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Alias to ::offsetGet()
+ *
+ * @param string $key Name
+ *
+ * @return mixed
+ */
+ function get($key) {
+ return $this->offsetGet($key);
+ }
+
+ /**
+ * Alias to ::offsetSet()
+ *
+ * @param string $key Name
+ * @param mixed $value Value
+ *
+ * @return void
+ */
+ function set($key, $value) {
+ $this->offsetSet($key, $value);
+ }
+
+ /**
+ * Alias to offsetUnset()
+ *
+ * @param string $key Name
+ *
+ * @return void
+ */
+ function del($key) {
+ $this->offsetUnset($key);
+ }
+}
diff --git a/engine/classes/ElggSharedMemoryCache.php b/engine/classes/ElggSharedMemoryCache.php
new file mode 100644
index 000000000..f5f11d2c7
--- /dev/null
+++ b/engine/classes/ElggSharedMemoryCache.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Shared memory cache description.
+ * Extends ElggCache with functions useful to shared memory
+ * style caches (static variables, memcache etc)
+ *
+ * @package Elgg.Core
+ * @subpackage Cache
+ */
+abstract class ElggSharedMemoryCache extends ElggCache {
+ /**
+ * Namespace variable used to keep various bits of the cache
+ * separate.
+ *
+ * @var string
+ */
+ private $namespace;
+
+ /**
+ * Set the namespace of this cache.
+ * This is useful for cache types (like memcache or static variables) where there is one large
+ * flat area of memory shared across all instances of the cache.
+ *
+ * @param string $namespace Namespace for cache
+ *
+ * @return void
+ */
+ public function setNamespace($namespace = "default") {
+ $this->namespace = $namespace;
+ }
+
+ /**
+ * Get the namespace currently defined.
+ *
+ * @return string
+ */
+ public function getNamespace() {
+ return $this->namespace;
+ }
+}
diff --git a/engine/classes/ElggSite.php b/engine/classes/ElggSite.php
new file mode 100644
index 000000000..dd996fe98
--- /dev/null
+++ b/engine/classes/ElggSite.php
@@ -0,0 +1,455 @@
+<?php
+/**
+ * A Site entity.
+ *
+ * ElggSite represents a single site entity.
+ *
+ * An ElggSite object is an ElggEntity child class with the subtype
+ * of "site." It is created upon installation and hold all the
+ * information about a site:
+ * - name
+ * - description
+ * - url
+ *
+ * Every ElggEntity (except ElggSite) belongs to a site.
+ *
+ * @internal ElggSite represents a single row from the sites_entity
+ * table, as well as the corresponding ElggEntity row from the entities table.
+ *
+ * @warning Multiple site support isn't fully developed.
+ *
+ * @package Elgg.Core
+ * @subpackage DataMode.Site
+ * @link http://docs.elgg.org/DataModel/Sites
+ *
+ * @property string $name The name or title of the website
+ * @property string $description A motto, mission statement, or description of the website
+ * @property string $url The root web address for the site, including trailing slash
+ */
+class ElggSite extends ElggEntity {
+
+ /**
+ * Initialise the attributes array.
+ * This is vital to distinguish between metadata and base parameters.
+ *
+ * Place your base parameters here.
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['type'] = "site";
+ $this->attributes['name'] = NULL;
+ $this->attributes['description'] = NULL;
+ $this->attributes['url'] = NULL;
+ $this->attributes['tables_split'] = 2;
+ }
+
+ /**
+ * Load or create a new ElggSite.
+ *
+ * If no arguments are passed, create a new entity.
+ *
+ * If an argument is passed attempt to load a full Site entity. Arguments
+ * can be:
+ * - The GUID of a site entity.
+ * - 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
+ * 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
+ */
+ function __construct($guid = null) {
+ $this->initializeAttributes();
+
+ // compatibility for 1.7 api.
+ $this->initialise_attributes(false);
+
+ if (!empty($guid)) {
+ // Is $guid is a DB entity table row
+ if ($guid instanceof stdClass) {
+ // Load the rest
+ if (!$this->load($guid)) {
+ $msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
+ throw new IOException($msg);
+ }
+ } else if ($guid instanceof ElggSite) {
+ // $guid is an ElggSite so this is a copy constructor
+ elgg_deprecated_notice('This type of usage of the ElggSite constructor was deprecated. Please use the clone method.', 1.7);
+
+ foreach ($guid->attributes as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+ } else if ($guid instanceof ElggEntity) {
+ // @todo remove and just use else clause
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggSite'));
+ } else if (strpos($guid, "http") !== false) {
+ // url so retrieve by url
+ $guid = get_site_by_url($guid);
+ foreach ($guid->attributes as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+ } else if (is_numeric($guid)) {
+ // $guid is a GUID so load
+ if (!$this->load($guid)) {
+ throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
+ }
+ } else {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
+ }
+ }
+ }
+
+ /**
+ * Loads the full ElggSite when given a guid.
+ *
+ * @param mixed $guid GUID of ElggSite entity or database row object
+ *
+ * @return bool
+ * @throws InvalidClassException
+ */
+ protected function load($guid) {
+ $attr_loader = new ElggAttributeLoader(get_class(), 'site', $this->attributes);
+ $attr_loader->requires_access_control = !($this instanceof ElggPlugin);
+ $attr_loader->secondary_loader = 'get_site_entity_as_row';
+
+ $attrs = $attr_loader->getRequiredAttributes($guid);
+ if (!$attrs) {
+ return false;
+ }
+
+ $this->attributes = $attrs;
+ $this->attributes['tables_loaded'] = 2;
+ _elgg_cache_entity($this);
+
+ return true;
+ }
+
+ /**
+ * Saves site-specific attributes.
+ *
+ * @internal Site attributes are saved in the sites_entity table.
+ *
+ * @return bool
+ */
+ public function save() {
+ global $CONFIG;
+
+ // Save generic stuff
+ if (!parent::save()) {
+ return false;
+ }
+
+ // make sure the site guid is set (if not, set to self)
+ if (!$this->get('site_guid')) {
+ $guid = $this->get('guid');
+ update_data("UPDATE {$CONFIG->dbprefix}entities SET site_guid=$guid
+ WHERE guid=$guid");
+ }
+
+ // Now save specific stuff
+ return create_site_entity($this->get('guid'), $this->get('name'),
+ $this->get('description'), $this->get('url'));
+ }
+
+ /**
+ * Delete the site.
+ *
+ * @note You cannot delete the current site.
+ *
+ * @return bool
+ * @throws SecurityException
+ */
+ public function delete() {
+ global $CONFIG;
+ if ($CONFIG->site->getGUID() == $this->guid) {
+ throw new SecurityException('SecurityException:deletedisablecurrentsite');
+ }
+
+ return parent::delete();
+ }
+
+ /**
+ * Disable the site
+ *
+ * @note You cannot disable the current site.
+ *
+ * @param string $reason Optional reason for disabling
+ * @param bool $recursive Recursively disable all contained entities?
+ *
+ * @return bool
+ * @throws SecurityException
+ */
+ public function disable($reason = "", $recursive = true) {
+ global $CONFIG;
+
+ if ($CONFIG->site->getGUID() == $this->guid) {
+ throw new SecurityException('SecurityException:deletedisablecurrentsite');
+ }
+
+ return parent::disable($reason, $recursive);
+ }
+
+ /**
+ * Gets an array of ElggUser entities who are members of the site.
+ *
+ * @param array $options An associative array for key => value parameters
+ * accepted by elgg_get_entities(). Common parameters
+ * include 'limit', and 'offset'.
+ * Note: this was $limit before version 1.8
+ * @param int $offset Offset @deprecated parameter
+ *
+ * @todo remove $offset in 2.0
+ *
+ * @return array of ElggUsers
+ */
+ public function getMembers($options = array(), $offset = 0) {
+ if (!is_array($options)) {
+ elgg_deprecated_notice("ElggSite::getMembers uses different arguments!", 1.8);
+ $options = array(
+ 'limit' => $options,
+ 'offset' => $offset,
+ );
+ }
+
+ $defaults = array(
+ 'site_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'relationship' => 'member_of_site',
+ 'relationship_guid' => $this->getGUID(),
+ 'inverse_relationship' => TRUE,
+ 'type' => 'user',
+ );
+
+ $options = array_merge($defaults, $options);
+
+ return elgg_get_entities_from_relationship($options);
+ }
+
+ /**
+ * List the members of this site
+ *
+ * @param array $options An associative array for key => value parameters
+ * accepted by elgg_list_entities(). Common parameters
+ * include 'full_view', 'limit', and 'offset'.
+ *
+ * @return string
+ * @since 1.8.0
+ */
+ public function listMembers($options = array()) {
+ $defaults = array(
+ 'site_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'relationship' => 'member_of_site',
+ 'relationship_guid' => $this->getGUID(),
+ 'inverse_relationship' => TRUE,
+ 'type' => 'user',
+ );
+
+ $options = array_merge($defaults, $options);
+
+ return elgg_list_entities_from_relationship($options);
+ }
+
+ /**
+ * Adds a user to the site.
+ *
+ * @param int $user_guid GUID
+ *
+ * @return bool
+ */
+ public function addUser($user_guid) {
+ return add_site_user($this->getGUID(), $user_guid);
+ }
+
+ /**
+ * Removes a user from the site.
+ *
+ * @param int $user_guid GUID
+ *
+ * @return bool
+ */
+ public function removeUser($user_guid) {
+ return remove_site_user($this->getGUID(), $user_guid);
+ }
+
+ /**
+ * Returns an array of ElggObject entities that belong to the site.
+ *
+ * @warning This only returns objects that have been explicitly added to the
+ * site through addObject()
+ *
+ * @param string $subtype Entity subtype
+ * @param int $limit Limit
+ * @param int $offset Offset
+ *
+ * @return array
+ */
+ public function getObjects($subtype = "", $limit = 10, $offset = 0) {
+ return get_site_objects($this->getGUID(), $subtype, $limit, $offset);
+ }
+
+ /**
+ * Adds an object to the site.
+ *
+ * @param int $object_guid GUID
+ *
+ * @return bool
+ */
+ public function addObject($object_guid) {
+ return add_site_object($this->getGUID(), $object_guid);
+ }
+
+ /**
+ * Remvoes an object from the site.
+ *
+ * @param int $object_guid GUID
+ *
+ * @return bool
+ */
+ public function removeObject($object_guid) {
+ return remove_site_object($this->getGUID(), $object_guid);
+ }
+
+ /**
+ * Get the collections associated with a site.
+ *
+ * @param string $subtype Subtype
+ * @param int $limit Limit
+ * @param int $offset Offset
+ *
+ * @return unknown
+ * @deprecated 1.8 Was never implemented
+ */
+ public function getCollections($subtype = "", $limit = 10, $offset = 0) {
+ elgg_deprecated_notice("ElggSite::getCollections() is deprecated", 1.8);
+ get_site_collections($this->getGUID(), $subtype, $limit, $offset);
+ }
+
+ /*
+ * EXPORTABLE INTERFACE
+ */
+
+ /**
+ * Return an array of fields which can be exported.
+ *
+ * @return array
+ */
+ public function getExportableValues() {
+ return array_merge(parent::getExportableValues(), array(
+ 'name',
+ 'description',
+ 'url',
+ ));
+ }
+
+ /**
+ * Halts bootup and redirects to the site front page
+ * if site is in walled garden mode, no user is logged in,
+ * and the URL is not a public page.
+ *
+ * @link http://docs.elgg.org/Tutorials/WalledGarden
+ *
+ * @return void
+ * @since 1.8.0
+ */
+ public function checkWalledGarden() {
+ global $CONFIG;
+
+ // command line calls should not invoke the walled garden check
+ if (PHP_SAPI === 'cli') {
+ return;
+ }
+
+ if ($CONFIG->walled_garden) {
+ if ($CONFIG->default_access == ACCESS_PUBLIC) {
+ $CONFIG->default_access = ACCESS_LOGGED_IN;
+ }
+ elgg_register_plugin_hook_handler(
+ 'access:collections:write',
+ 'user',
+ '_elgg_walled_garden_remove_public_access');
+
+ if (!elgg_is_logged_in()) {
+ // hook into the index system call at the highest priority
+ elgg_register_plugin_hook_handler('index', 'system', 'elgg_walled_garden_index', 1);
+
+ if (!$this->isPublicPage()) {
+ if (!elgg_is_xhr()) {
+ $_SESSION['last_forward_from'] = current_page_url();
+ }
+ register_error(elgg_echo('loggedinrequired'));
+ forward();
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns if a URL is public for this site when in Walled Garden mode.
+ *
+ * Pages are registered to be public by {@elgg_plugin_hook public_pages walled_garden}.
+ *
+ * @param string $url Defaults to the current URL.
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+ public function isPublicPage($url = '') {
+ global $CONFIG;
+
+ if (empty($url)) {
+ $url = current_page_url();
+
+ // do not check against URL queries
+ if ($pos = strpos($url, '?')) {
+ $url = substr($url, 0, $pos);
+ }
+ }
+
+ // always allow index page
+ if ($url == elgg_get_site_url($this->guid)) {
+ return TRUE;
+ }
+
+ // default public pages
+ $defaults = array(
+ 'walled_garden/.*',
+ 'login',
+ 'action/login',
+ 'register',
+ 'action/register',
+ 'forgotpassword',
+ 'resetpassword',
+ 'action/user/requestnewpassword',
+ 'action/user/passwordreset',
+ '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
+ $plugins = elgg_trigger_plugin_hook('public_pages', 'walled_garden', NULL, array());
+
+ // allow public pages
+ foreach (array_merge($defaults, $plugins) as $public) {
+ $pattern = "`^{$CONFIG->url}$public/*$`i";
+ if (preg_match($pattern, $url)) {
+ return TRUE;
+ }
+ }
+
+ // non-public page
+ return FALSE;
+ }
+}
diff --git a/engine/classes/ElggStaticVariableCache.php b/engine/classes/ElggStaticVariableCache.php
new file mode 100644
index 000000000..9c14fdfba
--- /dev/null
+++ b/engine/classes/ElggStaticVariableCache.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * ElggStaticVariableCache
+ * Dummy cache which stores values in a static array. Using this makes future
+ * replacements to other caching back ends (eg memcache) much easier.
+ *
+ * @package Elgg.Core
+ * @subpackage Cache
+ */
+class ElggStaticVariableCache extends ElggSharedMemoryCache {
+ /**
+ * The cache.
+ *
+ * @var array
+ */
+ private static $__cache;
+
+ /**
+ * Create the variable cache.
+ *
+ * This function creates a variable cache in a static variable in
+ * memory, optionally with a given namespace (to avoid overlap).
+ *
+ * @param string $namespace The namespace for this cache to write to.
+ * @warning namespaces of the same name are shared!
+ */
+ function __construct($namespace = 'default') {
+ $this->setNamespace($namespace);
+ $this->clear();
+ }
+
+ /**
+ * Save a key
+ *
+ * @param string $key Name
+ * @param string $data Value
+ *
+ * @return boolean
+ */
+ public function save($key, $data) {
+ $namespace = $this->getNamespace();
+
+ ElggStaticVariableCache::$__cache[$namespace][$key] = $data;
+
+ return true;
+ }
+
+ /**
+ * Load a key
+ *
+ * @param string $key Name
+ * @param int $offset Offset
+ * @param int $limit Limit
+ *
+ * @return string
+ */
+ public function load($key, $offset = 0, $limit = null) {
+ $namespace = $this->getNamespace();
+
+ if (isset(ElggStaticVariableCache::$__cache[$namespace][$key])) {
+ return ElggStaticVariableCache::$__cache[$namespace][$key];
+ }
+
+ return false;
+ }
+
+ /**
+ * Invalidate a given key.
+ *
+ * @param string $key Name
+ *
+ * @return bool
+ */
+ public function delete($key) {
+ $namespace = $this->getNamespace();
+
+ unset(ElggStaticVariableCache::$__cache[$namespace][$key]);
+
+ return true;
+ }
+
+ /**
+ * Clears the cache for a particular namespace
+ *
+ * @return void
+ */
+ public function clear() {
+ $namespace = $this->getNamespace();
+
+ if (!isset(ElggStaticVariableCache::$__cache)) {
+ ElggStaticVariableCache::$__cache = array();
+ }
+
+ ElggStaticVariableCache::$__cache[$namespace] = array();
+ }
+}
diff --git a/engine/classes/ElggTranslit.php b/engine/classes/ElggTranslit.php
new file mode 100644
index 000000000..b4bf87797
--- /dev/null
+++ b/engine/classes/ElggTranslit.php
@@ -0,0 +1,269 @@
+<?php
+/**
+ * Elgg Transliterate
+ *
+ * For creating "friendly titles" for URLs
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ *
+ * @package Elgg.Core
+ * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
+ * @author Jonathan H. Wage <jonwage@gmail.com>
+ * @author Steve Clay <steve@mrclay.org>
+ *
+ * @access private Plugin authors should not use this directly
+ */
+class ElggTranslit {
+
+ /**
+ * Create a version of a string for embedding in a URL
+ *
+ * @param string $string A UTF-8 string
+ * @param string $separator The character to separate words with
+ * @return string
+ */
+ static public function urlize($string, $separator = '-') {
+ // Iñtërnâtiônàlizætiøn, AND 日本語!
+
+ // try to force combined chars because the translit map and others expect it
+ if (self::hasNormalizerSupport()) {
+ $nfc = normalizer_normalize($string);
+ if (is_string($nfc)) {
+ $string = $nfc;
+ }
+ }
+ // Internationalization, AND 日本語!
+ $string = self::transliterateAscii($string);
+
+ // allow HTML tags in titles
+ $string = preg_replace('~<([a-zA-Z][^>]*)>~', ' $1 ', $string);
+
+ // more substitutions
+ // @todo put these somewhere else
+ $string = strtr($string, array(
+ // currency
+ "\xE2\x82\xAC" /* € */ => ' E ',
+ "\xC2\xA3" /* £ */ => ' GBP ',
+ ));
+
+ // remove all ASCII except 0-9a-zA-Z, hyphen, underscore, and whitespace
+ // note: "x" modifier did not work with this pattern.
+ $string = preg_replace('~['
+ . '\x00-\x08' // control chars
+ . '\x0b\x0c' // vert tab, form feed
+ . '\x0e-\x1f' // control chars
+ . '\x21-\x2c' // ! ... ,
+ . '\x2e\x2f' // . slash
+ . '\x3a-\x40' // : ... @
+ . '\x5b-\x5e' // [ ... ^
+ . '\x60' // `
+ . '\x7b-\x7f' // { ... DEL
+ . ']~', '', $string);
+ $string = strtr($string, '', '');
+
+ // internationalization, and 日本語!
+ // note: not using elgg_strtolower to keep this class portable
+ $string = is_callable('mb_strtolower')
+ ? mb_strtolower($string, 'UTF-8')
+ : strtolower($string);
+
+ // split by ASCII chars not in 0-9a-zA-Z
+ // note: we cannot use [^0-9a-zA-Z] because that matches multibyte chars.
+ // note: "x" modifier did not work with this pattern.
+ $pattern = '~['
+ . '\x00-\x2f' // controls ... slash
+ . '\x3a-\x40' // : ... @
+ . '\x5b-\x60' // [ ... `
+ . '\x7b-\x7f' // { ... DEL
+ . ']+~x';
+
+ // ['internationalization', 'and', '日本語']
+ $words = preg_split($pattern, $string, -1, PREG_SPLIT_NO_EMPTY);
+
+ // ['internationalization', 'and', '%E6%97%A5%E6%9C%AC%E8%AA%9E']
+ $words = array_map('urlencode', $words);
+
+ // internationalization-and-%E6%97%A5%E6%9C%AC%E8%AA%9E
+ return implode($separator, $words);
+ }
+
+ /**
+ * Transliterate Western multibyte chars to ASCII
+ *
+ * @param string $utf8 a UTF-8 string
+ * @return string
+ */
+ static public function transliterateAscii($utf8) {
+ static $map = null;
+ if (!preg_match('/[\x80-\xff]/', $utf8)) {
+ return $utf8;
+ }
+ if (null === $map) {
+ $map = self::getAsciiTranslitMap();
+ }
+ return strtr($utf8, $map);
+ }
+
+ /**
+ * Get array of UTF-8 (NFC) character replacements.
+ *
+ * @return array
+ */
+ static public function getAsciiTranslitMap() {
+ return array(
+ // Decompositions for Latin-1 Supplement
+ "\xC2\xAA" /* ª */ => 'a', "\xC2\xBA" /* º */ => 'o', "\xC3\x80" /* À */ => 'A',
+ "\xC3\x81" /* Á */ => 'A', "\xC3\x82" /* Â */ => 'A', "\xC3\x83" /* Ã */ => 'A',
+ "\xC3\x84" /* Ä */ => 'A', "\xC3\x85" /* Å */ => 'A', "\xC3\x86" /* Æ */ => 'AE',
+ "\xC3\x87" /* Ç */ => 'C', "\xC3\x88" /* È */ => 'E', "\xC3\x89" /* É */ => 'E',
+ "\xC3\x8A" /* Ê */ => 'E', "\xC3\x8B" /* Ë */ => 'E', "\xC3\x8C" /* Ì */ => 'I',
+ "\xC3\x8D" /* Í */ => 'I', "\xC3\x8E" /* Î */ => 'I', "\xC3\x8F" /* Ï */ => 'I',
+ "\xC3\x90" /* Ð */ => 'D', "\xC3\x91" /* Ñ */ => 'N', "\xC3\x92" /* Ò */ => 'O',
+ "\xC3\x93" /* Ó */ => 'O', "\xC3\x94" /* Ô */ => 'O', "\xC3\x95" /* Õ */ => 'O',
+ "\xC3\x96" /* Ö */ => 'O', "\xC3\x99" /* Ù */ => 'U', "\xC3\x9A" /* Ú */ => 'U',
+ "\xC3\x9B" /* Û */ => 'U', "\xC3\x9C" /* Ü */ => 'U', "\xC3\x9D" /* Ý */ => 'Y',
+ "\xC3\x9E" /* Þ */ => 'TH', "\xC3\x9F" /* ß */ => 'ss', "\xC3\xA0" /* à */ => 'a',
+ "\xC3\xA1" /* á */ => 'a', "\xC3\xA2" /* â */ => 'a', "\xC3\xA3" /* ã */ => 'a',
+ "\xC3\xA4" /* ä */ => 'a', "\xC3\xA5" /* å */ => 'a', "\xC3\xA6" /* æ */ => 'ae',
+ "\xC3\xA7" /* ç */ => 'c', "\xC3\xA8" /* è */ => 'e', "\xC3\xA9" /* é */ => 'e',
+ "\xC3\xAA" /* ê */ => 'e', "\xC3\xAB" /* ë */ => 'e', "\xC3\xAC" /* ì */ => 'i',
+ "\xC3\xAD" /* í */ => 'i', "\xC3\xAE" /* î */ => 'i', "\xC3\xAF" /* ï */ => 'i',
+ "\xC3\xB0" /* ð */ => 'd', "\xC3\xB1" /* ñ */ => 'n', "\xC3\xB2" /* ò */ => 'o',
+ "\xC3\xB3" /* ó */ => 'o', "\xC3\xB4" /* ô */ => 'o', "\xC3\xB5" /* õ */ => 'o',
+ "\xC3\xB6" /* ö */ => 'o', "\xC3\xB8" /* ø */ => 'o', "\xC3\xB9" /* ù */ => 'u',
+ "\xC3\xBA" /* ú */ => 'u', "\xC3\xBB" /* û */ => 'u', "\xC3\xBC" /* ü */ => 'u',
+ "\xC3\xBD" /* ý */ => 'y', "\xC3\xBE" /* þ */ => 'th', "\xC3\xBF" /* ÿ */ => 'y',
+ "\xC3\x98" /* Ø */ => 'O',
+ // Decompositions for Latin Extended-A
+ "\xC4\x80" /* Ā */ => 'A', "\xC4\x81" /* ā */ => 'a', "\xC4\x82" /* Ă */ => 'A',
+ "\xC4\x83" /* ă */ => 'a', "\xC4\x84" /* Ą */ => 'A', "\xC4\x85" /* ą */ => 'a',
+ "\xC4\x86" /* Ć */ => 'C', "\xC4\x87" /* ć */ => 'c', "\xC4\x88" /* Ĉ */ => 'C',
+ "\xC4\x89" /* ĉ */ => 'c', "\xC4\x8A" /* Ċ */ => 'C', "\xC4\x8B" /* ċ */ => 'c',
+ "\xC4\x8C" /* Č */ => 'C', "\xC4\x8D" /* č */ => 'c', "\xC4\x8E" /* Ď */ => 'D',
+ "\xC4\x8F" /* ď */ => 'd', "\xC4\x90" /* Đ */ => 'D', "\xC4\x91" /* đ */ => 'd',
+ "\xC4\x92" /* Ē */ => 'E', "\xC4\x93" /* ē */ => 'e', "\xC4\x94" /* Ĕ */ => 'E',
+ "\xC4\x95" /* ĕ */ => 'e', "\xC4\x96" /* Ė */ => 'E', "\xC4\x97" /* ė */ => 'e',
+ "\xC4\x98" /* Ę */ => 'E', "\xC4\x99" /* ę */ => 'e', "\xC4\x9A" /* Ě */ => 'E',
+ "\xC4\x9B" /* ě */ => 'e', "\xC4\x9C" /* Ĝ */ => 'G', "\xC4\x9D" /* ĝ */ => 'g',
+ "\xC4\x9E" /* Ğ */ => 'G', "\xC4\x9F" /* ğ */ => 'g', "\xC4\xA0" /* Ġ */ => 'G',
+ "\xC4\xA1" /* ġ */ => 'g', "\xC4\xA2" /* Ģ */ => 'G', "\xC4\xA3" /* ģ */ => 'g',
+ "\xC4\xA4" /* Ĥ */ => 'H', "\xC4\xA5" /* ĥ */ => 'h', "\xC4\xA6" /* Ħ */ => 'H',
+ "\xC4\xA7" /* ħ */ => 'h', "\xC4\xA8" /* Ĩ */ => 'I', "\xC4\xA9" /* ĩ */ => 'i',
+ "\xC4\xAA" /* Ī */ => 'I', "\xC4\xAB" /* ī */ => 'i', "\xC4\xAC" /* Ĭ */ => 'I',
+ "\xC4\xAD" /* ĭ */ => 'i', "\xC4\xAE" /* Į */ => 'I', "\xC4\xAF" /* į */ => 'i',
+ "\xC4\xB0" /* İ */ => 'I', "\xC4\xB1" /* ı */ => 'i', "\xC4\xB2" /* IJ */ => 'IJ',
+ "\xC4\xB3" /* ij */ => 'ij', "\xC4\xB4" /* Ĵ */ => 'J', "\xC4\xB5" /* ĵ */ => 'j',
+ "\xC4\xB6" /* Ķ */ => 'K', "\xC4\xB7" /* ķ */ => 'k', "\xC4\xB8" /* ĸ */ => 'k',
+ "\xC4\xB9" /* Ĺ */ => 'L', "\xC4\xBA" /* ĺ */ => 'l', "\xC4\xBB" /* Ļ */ => 'L',
+ "\xC4\xBC" /* ļ */ => 'l', "\xC4\xBD" /* Ľ */ => 'L', "\xC4\xBE" /* ľ */ => 'l',
+ "\xC4\xBF" /* Ŀ */ => 'L', "\xC5\x80" /* ŀ */ => 'l', "\xC5\x81" /* Ł */ => 'L',
+ "\xC5\x82" /* ł */ => 'l', "\xC5\x83" /* Ń */ => 'N', "\xC5\x84" /* ń */ => 'n',
+ "\xC5\x85" /* Ņ */ => 'N', "\xC5\x86" /* ņ */ => 'n', "\xC5\x87" /* Ň */ => 'N',
+ "\xC5\x88" /* ň */ => 'n', "\xC5\x89" /* ʼn */ => 'N', "\xC5\x8A" /* Ŋ */ => 'n',
+ "\xC5\x8B" /* ŋ */ => 'N', "\xC5\x8C" /* Ō */ => 'O', "\xC5\x8D" /* ō */ => 'o',
+ "\xC5\x8E" /* Ŏ */ => 'O', "\xC5\x8F" /* ŏ */ => 'o', "\xC5\x90" /* Ő */ => 'O',
+ "\xC5\x91" /* ő */ => 'o', "\xC5\x92" /* Œ */ => 'OE', "\xC5\x93" /* œ */ => 'oe',
+ "\xC5\x94" /* Ŕ */ => 'R', "\xC5\x95" /* ŕ */ => 'r', "\xC5\x96" /* Ŗ */ => 'R',
+ "\xC5\x97" /* ŗ */ => 'r', "\xC5\x98" /* Ř */ => 'R', "\xC5\x99" /* ř */ => 'r',
+ "\xC5\x9A" /* Ś */ => 'S', "\xC5\x9B" /* ś */ => 's', "\xC5\x9C" /* Ŝ */ => 'S',
+ "\xC5\x9D" /* ŝ */ => 's', "\xC5\x9E" /* Ş */ => 'S', "\xC5\x9F" /* ş */ => 's',
+ "\xC5\xA0" /* Š */ => 'S', "\xC5\xA1" /* š */ => 's', "\xC5\xA2" /* Ţ */ => 'T',
+ "\xC5\xA3" /* ţ */ => 't', "\xC5\xA4" /* Ť */ => 'T', "\xC5\xA5" /* ť */ => 't',
+ "\xC5\xA6" /* Ŧ */ => 'T', "\xC5\xA7" /* ŧ */ => 't', "\xC5\xA8" /* Ũ */ => 'U',
+ "\xC5\xA9" /* ũ */ => 'u', "\xC5\xAA" /* Ū */ => 'U', "\xC5\xAB" /* ū */ => 'u',
+ "\xC5\xAC" /* Ŭ */ => 'U', "\xC5\xAD" /* ŭ */ => 'u', "\xC5\xAE" /* Ů */ => 'U',
+ "\xC5\xAF" /* ů */ => 'u', "\xC5\xB0" /* Ű */ => 'U', "\xC5\xB1" /* ű */ => 'u',
+ "\xC5\xB2" /* Ų */ => 'U', "\xC5\xB3" /* ų */ => 'u', "\xC5\xB4" /* Ŵ */ => 'W',
+ "\xC5\xB5" /* ŵ */ => 'w', "\xC5\xB6" /* Ŷ */ => 'Y', "\xC5\xB7" /* ŷ */ => 'y',
+ "\xC5\xB8" /* Ÿ */ => 'Y', "\xC5\xB9" /* Ź */ => 'Z', "\xC5\xBA" /* ź */ => 'z',
+ "\xC5\xBB" /* Ż */ => 'Z', "\xC5\xBC" /* ż */ => 'z', "\xC5\xBD" /* Ž */ => 'Z',
+ "\xC5\xBE" /* ž */ => 'z', "\xC5\xBF" /* ſ */ => 's',
+ // Decompositions for Latin Extended-B
+ "\xC8\x98" /* Ș */ => 'S', "\xC8\x99" /* ș */ => 's',
+ "\xC8\x9A" /* Ț */ => 'T', "\xC8\x9B" /* ț */ => 't',
+ // unmarked
+ "\xC6\xA0" /* Ơ */ => 'O', "\xC6\xA1" /* ơ */ => 'o',
+ "\xC6\xAF" /* Ư */ => 'U', "\xC6\xB0" /* ư */ => 'u',
+ // grave accent
+ "\xE1\xBA\xA6" /* Ầ */ => 'A', "\xE1\xBA\xA7" /* ầ */ => 'a',
+ "\xE1\xBA\xB0" /* Ằ */ => 'A', "\xE1\xBA\xB1" /* ằ */ => 'a',
+ "\xE1\xBB\x80" /* Ề */ => 'E', "\xE1\xBB\x81" /* ề */ => 'e',
+ "\xE1\xBB\x92" /* Ồ */ => 'O', "\xE1\xBB\x93" /* ồ */ => 'o',
+ "\xE1\xBB\x9C" /* Ờ */ => 'O', "\xE1\xBB\x9D" /* ờ */ => 'o',
+ "\xE1\xBB\xAA" /* Ừ */ => 'U', "\xE1\xBB\xAB" /* ừ */ => 'u',
+ "\xE1\xBB\xB2" /* Ỳ */ => 'Y', "\xE1\xBB\xB3" /* ỳ */ => 'y',
+ // hook
+ "\xE1\xBA\xA2" /* Ả */ => 'A', "\xE1\xBA\xA3" /* ả */ => 'a',
+ "\xE1\xBA\xA8" /* Ẩ */ => 'A', "\xE1\xBA\xA9" /* ẩ */ => 'a',
+ "\xE1\xBA\xB2" /* Ẳ */ => 'A', "\xE1\xBA\xB3" /* ẳ */ => 'a',
+ "\xE1\xBA\xBA" /* Ẻ */ => 'E', "\xE1\xBA\xBB" /* ẻ */ => 'e',
+ "\xE1\xBB\x82" /* Ể */ => 'E', "\xE1\xBB\x83" /* ể */ => 'e',
+ "\xE1\xBB\x88" /* Ỉ */ => 'I', "\xE1\xBB\x89" /* ỉ */ => 'i',
+ "\xE1\xBB\x8E" /* Ỏ */ => 'O', "\xE1\xBB\x8F" /* ỏ */ => 'o',
+ "\xE1\xBB\x94" /* Ổ */ => 'O', "\xE1\xBB\x95" /* ổ */ => 'o',
+ "\xE1\xBB\x9E" /* Ở */ => 'O', "\xE1\xBB\x9F" /* ở */ => 'o',
+ "\xE1\xBB\xA6" /* Ủ */ => 'U', "\xE1\xBB\xA7" /* ủ */ => 'u',
+ "\xE1\xBB\xAC" /* Ử */ => 'U', "\xE1\xBB\xAD" /* ử */ => 'u',
+ "\xE1\xBB\xB6" /* Ỷ */ => 'Y', "\xE1\xBB\xB7" /* ỷ */ => 'y',
+ // tilde
+ "\xE1\xBA\xAA" /* Ẫ */ => 'A', "\xE1\xBA\xAB" /* ẫ */ => 'a',
+ "\xE1\xBA\xB4" /* Ẵ */ => 'A', "\xE1\xBA\xB5" /* ẵ */ => 'a',
+ "\xE1\xBA\xBC" /* Ẽ */ => 'E', "\xE1\xBA\xBD" /* ẽ */ => 'e',
+ "\xE1\xBB\x84" /* Ễ */ => 'E', "\xE1\xBB\x85" /* ễ */ => 'e',
+ "\xE1\xBB\x96" /* Ỗ */ => 'O', "\xE1\xBB\x97" /* ỗ */ => 'o',
+ "\xE1\xBB\xA0" /* Ỡ */ => 'O', "\xE1\xBB\xA1" /* ỡ */ => 'o',
+ "\xE1\xBB\xAE" /* Ữ */ => 'U', "\xE1\xBB\xAF" /* ữ */ => 'u',
+ "\xE1\xBB\xB8" /* Ỹ */ => 'Y', "\xE1\xBB\xB9" /* ỹ */ => 'y',
+ // acute accent
+ "\xE1\xBA\xA4" /* Ấ */ => 'A', "\xE1\xBA\xA5" /* ấ */ => 'a',
+ "\xE1\xBA\xAE" /* Ắ */ => 'A', "\xE1\xBA\xAF" /* ắ */ => 'a',
+ "\xE1\xBA\xBE" /* Ế */ => 'E', "\xE1\xBA\xBF" /* ế */ => 'e',
+ "\xE1\xBB\x90" /* Ố */ => 'O', "\xE1\xBB\x91" /* ố */ => 'o',
+ "\xE1\xBB\x9A" /* Ớ */ => 'O', "\xE1\xBB\x9B" /* ớ */ => 'o',
+ "\xE1\xBB\xA8" /* Ứ */ => 'U', "\xE1\xBB\xA9" /* ứ */ => 'u',
+ // dot below
+ "\xE1\xBA\xA0" /* Ạ */ => 'A', "\xE1\xBA\xA1" /* ạ */ => 'a',
+ "\xE1\xBA\xAC" /* Ậ */ => 'A', "\xE1\xBA\xAD" /* ậ */ => 'a',
+ "\xE1\xBA\xB6" /* Ặ */ => 'A', "\xE1\xBA\xB7" /* ặ */ => 'a',
+ "\xE1\xBA\xB8" /* Ẹ */ => 'E', "\xE1\xBA\xB9" /* ẹ */ => 'e',
+ "\xE1\xBB\x86" /* Ệ */ => 'E', "\xE1\xBB\x87" /* ệ */ => 'e',
+ "\xE1\xBB\x8A" /* Ị */ => 'I', "\xE1\xBB\x8B" /* ị */ => 'i',
+ "\xE1\xBB\x8C" /* Ọ */ => 'O', "\xE1\xBB\x8D" /* ọ */ => 'o',
+ "\xE1\xBB\x98" /* Ộ */ => 'O', "\xE1\xBB\x99" /* ộ */ => 'o',
+ "\xE1\xBB\xA2" /* Ợ */ => 'O', "\xE1\xBB\xA3" /* ợ */ => 'o',
+ "\xE1\xBB\xA4" /* Ụ */ => 'U', "\xE1\xBB\xA5" /* ụ */ => 'u',
+ "\xE1\xBB\xB0" /* Ự */ => 'U', "\xE1\xBB\xB1" /* ự */ => 'u',
+ "\xE1\xBB\xB4" /* Ỵ */ => 'Y', "\xE1\xBB\xB5" /* ỵ */ => 'y',
+ );
+ }
+
+ /**
+ * Tests that "normalizer_normalize" exists and works
+ *
+ * @return bool
+ */
+ static public function hasNormalizerSupport() {
+ static $ret = null;
+ if (null === $ret) {
+ $form_c = "\xC3\x85"; // 'LATIN CAPITAL LETTER A WITH RING ABOVE' (U+00C5)
+ $form_d = "A\xCC\x8A"; // A followed by 'COMBINING RING ABOVE' (U+030A)
+ $ret = (function_exists('normalizer_normalize')
+ && $form_c === normalizer_normalize($form_d));
+ }
+ return $ret;
+ }
+}
diff --git a/engine/classes/ElggUser.php b/engine/classes/ElggUser.php
new file mode 100644
index 000000000..6163f9b62
--- /dev/null
+++ b/engine/classes/ElggUser.php
@@ -0,0 +1,588 @@
+<?php
+/**
+ * ElggUser
+ *
+ * Representation of a "user" in the system.
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel.User
+ *
+ * @property string $name The display name that the user will be known by in the network
+ * @property string $username The short, reference name for the user in the network
+ * @property string $email The email address to which Elgg will send email notifications
+ * @property string $language The language preference of the user (ISO 639-1 formatted)
+ * @property string $banned 'yes' if the user is banned from the network, 'no' otherwise
+ * @property string $admin 'yes' if the user is an administrator of the network, 'no' otherwise
+ * @property string $password The hashed password of the user
+ * @property string $salt The salt used to secure the password before hashing
+ */
+class ElggUser extends ElggEntity
+ implements Friendable {
+
+ /**
+ * Initialise the attributes array.
+ * This is vital to distinguish between metadata and base parameters.
+ *
+ * Place your base parameters here.
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['type'] = "user";
+ $this->attributes['name'] = NULL;
+ $this->attributes['username'] = NULL;
+ $this->attributes['password'] = NULL;
+ $this->attributes['salt'] = NULL;
+ $this->attributes['email'] = NULL;
+ $this->attributes['language'] = NULL;
+ $this->attributes['code'] = NULL;
+ $this->attributes['banned'] = "no";
+ $this->attributes['admin'] = 'no';
+ $this->attributes['prev_last_action'] = NULL;
+ $this->attributes['last_login'] = NULL;
+ $this->attributes['prev_last_login'] = NULL;
+ $this->attributes['tables_split'] = 2;
+ }
+
+ /**
+ * Construct a new user entity, optionally from a given id value.
+ *
+ * @param mixed $guid If an int, load that GUID.
+ * If an entity table db row then will load the rest of the data.
+ *
+ * @throws Exception if there was a problem creating the user.
+ */
+ function __construct($guid = null) {
+ $this->initializeAttributes();
+
+ // compatibility for 1.7 api.
+ $this->initialise_attributes(false);
+
+ if (!empty($guid)) {
+ // Is $guid is a DB entity row
+ if ($guid instanceof stdClass) {
+ // Load the rest
+ if (!$this->load($guid)) {
+ $msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
+ throw new IOException($msg);
+ }
+ } else if (is_string($guid)) {
+ // $guid is a username
+ $user = get_user_by_username($guid);
+ if ($user) {
+ foreach ($user->attributes as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+ }
+ } else if ($guid instanceof ElggUser) {
+ // $guid is an ElggUser so this is a copy constructor
+ elgg_deprecated_notice('This type of usage of the ElggUser constructor was deprecated. Please use the clone method.', 1.7);
+
+ foreach ($guid->attributes as $key => $value) {
+ $this->attributes[$key] = $value;
+ }
+ } else if ($guid instanceof ElggEntity) {
+ // @todo why have a special case here
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggUser'));
+ } else if (is_numeric($guid)) {
+ // $guid is a GUID so load entity
+ if (!$this->load($guid)) {
+ throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
+ }
+ } else {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
+ }
+ }
+ }
+
+ /**
+ * Load the ElggUser data from the database
+ *
+ * @param mixed $guid ElggUser GUID or stdClass database row from entity table
+ *
+ * @return bool
+ */
+ protected function load($guid) {
+ $attr_loader = new ElggAttributeLoader(get_class(), 'user', $this->attributes);
+ $attr_loader->secondary_loader = 'get_user_entity_as_row';
+
+ $attrs = $attr_loader->getRequiredAttributes($guid);
+ if (!$attrs) {
+ return false;
+ }
+
+ $this->attributes = $attrs;
+ $this->attributes['tables_loaded'] = 2;
+ _elgg_cache_entity($this);
+
+ return true;
+ }
+
+ /**
+ * Saves this user to the database.
+ *
+ * @return bool
+ */
+ public function save() {
+ // Save generic stuff
+ if (!parent::save()) {
+ return false;
+ }
+
+ // Now save specific stuff
+ _elgg_disable_caching_for_entity($this->guid);
+ $ret = create_user_entity($this->get('guid'), $this->get('name'), $this->get('username'),
+ $this->get('password'), $this->get('salt'), $this->get('email'), $this->get('language'),
+ $this->get('code'));
+ _elgg_enable_caching_for_entity($this->guid);
+
+ return $ret;
+ }
+
+ /**
+ * User specific override of the entity delete method.
+ *
+ * @return bool
+ */
+ public function delete() {
+ global $USERNAME_TO_GUID_MAP_CACHE, $CODE_TO_GUID_MAP_CACHE;
+
+ // clear cache
+ if (isset($USERNAME_TO_GUID_MAP_CACHE[$this->username])) {
+ unset($USERNAME_TO_GUID_MAP_CACHE[$this->username]);
+ }
+ if (isset($CODE_TO_GUID_MAP_CACHE[$this->code])) {
+ unset($CODE_TO_GUID_MAP_CACHE[$this->code]);
+ }
+
+ clear_user_files($this);
+
+ // Delete entity
+ return parent::delete();
+ }
+
+ /**
+ * Ban this user.
+ *
+ * @param string $reason Optional reason
+ *
+ * @return bool
+ */
+ public function ban($reason = "") {
+ return ban_user($this->guid, $reason);
+ }
+
+ /**
+ * Unban this user.
+ *
+ * @return bool
+ */
+ public function unban() {
+ return unban_user($this->guid);
+ }
+
+ /**
+ * Is this user banned or not?
+ *
+ * @return bool
+ */
+ public function isBanned() {
+ return $this->banned == 'yes';
+ }
+
+ /**
+ * Is this user admin?
+ *
+ * @return bool
+ */
+ public function isAdmin() {
+
+ // for backward compatibility we need to pull this directly
+ // from the attributes instead of using the magic methods.
+ // this can be removed in 1.9
+ // return $this->admin == 'yes';
+ return $this->attributes['admin'] == 'yes';
+ }
+
+ /**
+ * Make the user an admin
+ *
+ * @return bool
+ */
+ public function makeAdmin() {
+ // If already saved, use the standard function.
+ if ($this->guid && !make_user_admin($this->guid)) {
+ return FALSE;
+ }
+
+ // need to manually set attributes since they've already been loaded.
+ $this->attributes['admin'] = 'yes';
+
+ return TRUE;
+ }
+
+ /**
+ * Remove the admin flag for user
+ *
+ * @return bool
+ */
+ public function removeAdmin() {
+ // If already saved, use the standard function.
+ if ($this->guid && !remove_user_admin($this->guid)) {
+ return FALSE;
+ }
+
+ // need to manually set attributes since they've already been loaded.
+ $this->attributes['admin'] = 'no';
+
+ return TRUE;
+ }
+
+ /**
+ * Get sites that this user is a member of
+ *
+ * @param string $subtype Optionally, the subtype of result we want to limit to
+ * @param int $limit The number of results to return
+ * @param int $offset Any indexing offset
+ *
+ * @return array
+ */
+ function getSites($subtype = "", $limit = 10, $offset = 0) {
+ return get_user_sites($this->getGUID(), $subtype, $limit, $offset);
+ }
+
+ /**
+ * Add this user to a particular site
+ *
+ * @param int $site_guid The guid of the site to add it to
+ *
+ * @return bool
+ */
+ function addToSite($site_guid) {
+ return add_site_user($site_guid, $this->getGUID());
+ }
+
+ /**
+ * Remove this user from a particular site
+ *
+ * @param int $site_guid The guid of the site to remove it from
+ *
+ * @return bool
+ */
+ function removeFromSite($site_guid) {
+ return remove_site_user($site_guid, $this->getGUID());
+ }
+
+ /**
+ * Adds a user as a friend
+ *
+ * @param int $friend_guid The GUID of the user to add
+ *
+ * @return bool
+ */
+ function addFriend($friend_guid) {
+ return user_add_friend($this->getGUID(), $friend_guid);
+ }
+
+ /**
+ * Removes a user as a friend
+ *
+ * @param int $friend_guid The GUID of the user to remove
+ *
+ * @return bool
+ */
+ function removeFriend($friend_guid) {
+ return user_remove_friend($this->getGUID(), $friend_guid);
+ }
+
+ /**
+ * Determines whether or not this user is a friend of the currently logged in user
+ *
+ * @return bool
+ */
+ function isFriend() {
+ return $this->isFriendOf(elgg_get_logged_in_user_guid());
+ }
+
+ /**
+ * Determines whether this user is friends with another user
+ *
+ * @param int $user_guid The GUID of the user to check against
+ *
+ * @return bool
+ */
+ function isFriendsWith($user_guid) {
+ return user_is_friend($this->getGUID(), $user_guid);
+ }
+
+ /**
+ * Determines whether or not this user is another user's friend
+ *
+ * @param int $user_guid The GUID of the user to check against
+ *
+ * @return bool
+ */
+ function isFriendOf($user_guid) {
+ return user_is_friend($user_guid, $this->getGUID());
+ }
+
+ /**
+ * Gets this user's friends
+ *
+ * @param string $subtype Optionally, the user subtype (leave blank for all)
+ * @param int $limit The number of users to retrieve
+ * @param int $offset Indexing offset, if any
+ *
+ * @return array|false Array of ElggUser, or false, depending on success
+ */
+ function getFriends($subtype = "", $limit = 10, $offset = 0) {
+ return get_user_friends($this->getGUID(), $subtype, $limit, $offset);
+ }
+
+ /**
+ * Gets users who have made this user a friend
+ *
+ * @param string $subtype Optionally, the user subtype (leave blank for all)
+ * @param int $limit The number of users to retrieve
+ * @param int $offset Indexing offset, if any
+ *
+ * @return array|false Array of ElggUser, or false, depending on success
+ */
+ function getFriendsOf($subtype = "", $limit = 10, $offset = 0) {
+ return get_user_friends_of($this->getGUID(), $subtype, $limit, $offset);
+ }
+
+ /**
+ * Lists the user's friends
+ *
+ * @param string $subtype Optionally, the user subtype (leave blank for all)
+ * @param int $limit The number of users to retrieve
+ * @param array $vars Display variables for the user view
+ *
+ * @return string Rendered list of friends
+ * @since 1.8.0
+ */
+ function listFriends($subtype = "", $limit = 10, array $vars = array()) {
+ $defaults = array(
+ 'type' => 'user',
+ 'relationship' => 'friend',
+ 'relationship_guid' => $this->guid,
+ 'limit' => $limit,
+ 'full_view' => false,
+ );
+
+ $options = array_merge($defaults, $vars);
+
+ if ($subtype) {
+ $options['subtype'] = $subtype;
+ }
+
+ return elgg_list_entities_from_relationship($options);
+ }
+
+ /**
+ * Gets the user's groups
+ *
+ * @param string $subtype Optionally, the subtype of user to filter to (leave blank for all)
+ * @param int $limit The number of groups to retrieve
+ * @param int $offset Indexing offset, if any
+ *
+ * @return array|false Array of ElggGroup, or false, depending on success
+ */
+ function getGroups($subtype = "", $limit = 10, $offset = 0) {
+ $options = array(
+ 'type' => 'group',
+ 'relationship' => 'member',
+ 'relationship_guid' => $this->guid,
+ 'limit' => $limit,
+ 'offset' => $offset,
+ );
+
+ if ($subtype) {
+ $options['subtype'] = $subtype;
+ }
+
+ return elgg_get_entities_from_relationship($options);
+ }
+
+ /**
+ * Lists the user's groups
+ *
+ * @param string $subtype Optionally, the user subtype (leave blank for all)
+ * @param int $limit The number of users to retrieve
+ * @param int $offset Indexing offset, if any
+ *
+ * @return string
+ */
+ function listGroups($subtype = "", $limit = 10, $offset = 0) {
+ $options = array(
+ 'type' => 'group',
+ 'relationship' => 'member',
+ 'relationship_guid' => $this->guid,
+ 'limit' => $limit,
+ 'offset' => $offset,
+ 'full_view' => false,
+ );
+
+ if ($subtype) {
+ $options['subtype'] = $subtype;
+ }
+
+ return elgg_list_entities_from_relationship($options);
+ }
+
+ /**
+ * Get an array of ElggObject owned by this user.
+ *
+ * @param string $subtype The subtype of the objects, if any
+ * @param int $limit Number of results to return
+ * @param int $offset Any indexing offset
+ *
+ * @return array|false
+ */
+ public function getObjects($subtype = "", $limit = 10, $offset = 0) {
+ $params = array(
+ 'type' => 'object',
+ 'subtype' => $subtype,
+ 'owner_guid' => $this->getGUID(),
+ 'limit' => $limit,
+ 'offset' => $offset
+ );
+ return elgg_get_entities($params);
+ }
+
+ /**
+ * Get an array of ElggObjects owned by this user's friends.
+ *
+ * @param string $subtype The subtype of the objects, if any
+ * @param int $limit Number of results to return
+ * @param int $offset Any indexing offset
+ *
+ * @return array|false
+ */
+ public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) {
+ return get_user_friends_objects($this->getGUID(), $subtype, $limit, $offset);
+ }
+
+ /**
+ * Counts the number of ElggObjects owned by this user
+ *
+ * @param string $subtype The subtypes of the objects, if any
+ *
+ * @return int The number of ElggObjects
+ */
+ public function countObjects($subtype = "") {
+ return count_user_objects($this->getGUID(), $subtype);
+ }
+
+ /**
+ * Get the collections associated with a user.
+ *
+ * @param string $subtype Optionally, the subtype of result we want to limit to
+ * @param int $limit The number of results to return
+ * @param int $offset Any indexing offset
+ *
+ * @return array|false
+ */
+ public function getCollections($subtype = "", $limit = 10, $offset = 0) {
+ elgg_deprecated_notice("ElggUser::getCollections() has been deprecated", 1.8);
+ return false;
+ }
+
+ /**
+ * Get a user's owner GUID
+ *
+ * Returns it's own GUID if the user is not owned.
+ *
+ * @return int
+ */
+ function getOwnerGUID() {
+ if ($this->owner_guid == 0) {
+ return $this->guid;
+ }
+
+ return $this->owner_guid;
+ }
+
+ /**
+ * If a user's owner is blank, return its own GUID as the owner
+ *
+ * @return int User GUID
+ * @deprecated 1.8 Use getOwnerGUID()
+ */
+ function getOwner() {
+ elgg_deprecated_notice("ElggUser::getOwner deprecated for ElggUser::getOwnerGUID", 1.8);
+ $this->getOwnerGUID();
+ }
+
+ // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
+
+ /**
+ * Return an array of fields which can be exported.
+ *
+ * @return array
+ */
+ public function getExportableValues() {
+ return array_merge(parent::getExportableValues(), array(
+ 'name',
+ 'username',
+ 'language',
+ ));
+ }
+
+ /**
+ * Need to catch attempts to make a user an admin. Remove for 1.9
+ *
+ * @param string $name Name
+ * @param mixed $value Value
+ *
+ * @return bool
+ */
+ public function __set($name, $value) {
+ if ($name == 'admin' || $name == 'siteadmin') {
+ elgg_deprecated_notice('The admin/siteadmin metadata are not longer used. Use ElggUser->makeAdmin() and ElggUser->removeAdmin().', 1.7);
+
+ if ($value == 'yes' || $value == '1') {
+ $this->makeAdmin();
+ } else {
+ $this->removeAdmin();
+ }
+ }
+ return parent::__set($name, $value);
+ }
+
+ /**
+ * Need to catch attempts to test user for admin. Remove for 1.9
+ *
+ * @param string $name Name
+ *
+ * @return bool
+ */
+ public function __get($name) {
+ if ($name == 'admin' || $name == 'siteadmin') {
+ elgg_deprecated_notice('The admin/siteadmin metadata are not longer used. Use ElggUser->isAdmin().', 1.7);
+ return $this->isAdmin();
+ }
+
+ return parent::__get($name);
+ }
+
+ /**
+ * Can a user comment on this user?
+ *
+ * @see ElggEntity::canComment()
+ *
+ * @param int $user_guid User guid (default is logged in user)
+ * @return bool
+ * @since 1.8.0
+ */
+ public function canComment($user_guid = 0) {
+ $result = parent::canComment($user_guid);
+ if ($result !== null) {
+ return $result;
+ }
+ return false;
+ }
+}
diff --git a/engine/classes/ElggVolatileMetadataCache.php b/engine/classes/ElggVolatileMetadataCache.php
new file mode 100644
index 000000000..4acda7cee
--- /dev/null
+++ b/engine/classes/ElggVolatileMetadataCache.php
@@ -0,0 +1,355 @@
+<?php
+/**
+ * ElggVolatileMetadataCache
+ * In memory cache of known metadata values stored by entity.
+ *
+ * @package Elgg.Core
+ * @subpackage Cache
+ *
+ * @access private
+ */
+class ElggVolatileMetadataCache {
+
+ /**
+ * The cached values (or null for known to be empty). If the portion of the cache
+ * is synchronized, missing values are assumed to indicate that values do not
+ * exist in storage, otherwise, we don't know what's there.
+ *
+ * @var array
+ */
+ protected $values = array();
+
+ /**
+ * Does the cache know that it contains all names fetch-able from storage?
+ * The keys are entity GUIDs and either the value exists (true) or it's not set.
+ *
+ * @var array
+ */
+ protected $isSynchronized = array();
+
+ /**
+ * @var null|bool
+ */
+ protected $ignoreAccess = null;
+
+ /**
+ * Cache metadata for an entity
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @param array $values The metadata values to cache
+ * @return void
+ */
+ public function saveAll($entity_guid, array $values) {
+ if (!$this->getIgnoreAccess()) {
+ $this->values[$entity_guid] = $values;
+ $this->isSynchronized[$entity_guid] = true;
+ }
+ }
+
+ /**
+ * Get the metadata for an entity
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @return array
+ */
+ public function loadAll($entity_guid) {
+ if (isset($this->values[$entity_guid])) {
+ return $this->values[$entity_guid];
+ } else {
+ return array();
+ }
+ }
+
+ /**
+ * Declare that there may be fetch-able metadata names in storage that this
+ * cache doesn't know about
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @return void
+ */
+ public function markOutOfSync($entity_guid) {
+ unset($this->isSynchronized[$entity_guid]);
+ }
+
+ /**
+ * Have all the metadata for this entity been cached?
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @return bool
+ */
+ public function isSynchronized($entity_guid) {
+ return isset($this->isSynchronized[$entity_guid]);
+ }
+
+ /**
+ * Cache a piece of metadata
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
+ * @param array|int|string|null $value The metadata value. null means it is
+ * known that there is no fetch-able
+ * metadata under this name
+ * @param bool $allow_multiple Can the metadata be an array
+ * @return void
+ */
+ public function save($entity_guid, $name, $value, $allow_multiple = false) {
+ if ($this->getIgnoreAccess()) {
+ // we don't know if what gets saves here will be available to user once
+ // access control returns, hence it's best to forget :/
+ $this->markUnknown($entity_guid, $name);
+ } else {
+ if ($allow_multiple) {
+ if ($this->isKnown($entity_guid, $name)) {
+ $existing = $this->load($entity_guid, $name);
+ if ($existing !== null) {
+ $existing = (array) $existing;
+ $existing[] = $value;
+ $value = $existing;
+ }
+ } else {
+ // we don't know whether there are unknown values, so it's
+ // safest to leave that assumption
+ $this->markUnknown($entity_guid, $name);
+ return;
+ }
+ }
+ $this->values[$entity_guid][$name] = $value;
+ }
+ }
+
+ /**
+ * Warning: You should always call isKnown() beforehand to verify that this
+ * function's return value should be trusted (otherwise a null return value
+ * is ambiguous).
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
+ * @return array|string|int|null null = value does not exist
+ */
+ public function load($entity_guid, $name) {
+ if (isset($this->values[$entity_guid]) && array_key_exists($name, $this->values[$entity_guid])) {
+ return $this->values[$entity_guid][$name];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Forget about this metadata entry. We don't want to try to guess what the
+ * next fetch from storage will return
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
+ * @return void
+ */
+ public function markUnknown($entity_guid, $name) {
+ unset($this->values[$entity_guid][$name]);
+ $this->markOutOfSync($entity_guid);
+ }
+
+ /**
+ * If true, load() will return an accurate value for this name
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
+ * @return bool
+ */
+ public function isKnown($entity_guid, $name) {
+ if (isset($this->isSynchronized[$entity_guid])) {
+ return true;
+ } else {
+ return (isset($this->values[$entity_guid]) && array_key_exists($name, $this->values[$entity_guid]));
+ }
+
+ }
+
+ /**
+ * Declare that metadata under this name is known to be not fetch-able from storage
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
+ * @return array
+ */
+ public function markEmpty($entity_guid, $name) {
+ $this->values[$entity_guid][$name] = null;
+ }
+
+ /**
+ * Forget about all metadata for an entity
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @return void
+ */
+ public function clear($entity_guid) {
+ $this->values[$entity_guid] = array();
+ $this->markOutOfSync($entity_guid);
+ }
+
+ /**
+ * Clear entire cache and mark all entities as out of sync
+ *
+ * @return void
+ */
+ public function flush() {
+ $this->values = array();
+ $this->isSynchronized = array();
+ }
+
+ /**
+ * Use this value instead of calling elgg_get_ignore_access(). By default that
+ * function will be called.
+ *
+ * This setting makes this component a little more loosely-coupled.
+ *
+ * @param bool $ignore Whether to ignore access or not
+ * @return void
+ */
+ public function setIgnoreAccess($ignore) {
+ $this->ignoreAccess = (bool) $ignore;
+ }
+
+ /**
+ * Tell the cache to call elgg_get_ignore_access() to determing access status.
+ *
+ * @return void
+ */
+ public function unsetIgnoreAccess() {
+ $this->ignoreAccess = null;
+ }
+
+ /**
+ * Get the ignore access value
+ *
+ * @return bool
+ */
+ protected function getIgnoreAccess() {
+ if (null === $this->ignoreAccess) {
+ return elgg_get_ignore_access();
+ } else {
+ return $this->ignoreAccess;
+ }
+ }
+
+ /**
+ * Invalidate based on options passed to the global *_metadata functions
+ *
+ * @param string $action Action performed on metadata. "delete", "disable", or "enable"
+ * @param array $options Options passed to elgg_(delete|disable|enable)_metadata
+ * "guid" if given, invalidation will be limited to this entity
+ * "metadata_name" if given, invalidation will be limited to metadata with this name
+ * @return void
+ */
+ public function invalidateByOptions($action, array $options) {
+ // remove as little as possible, optimizing for common cases
+ if (empty($options['guid'])) {
+ // safest to clear everything unless we want to make this even more complex :(
+ $this->flush();
+ } else {
+ if (empty($options['metadata_name'])) {
+ // safest to clear the whole entity
+ $this->clear($options['guid']);
+ } else {
+ switch ($action) {
+ case 'delete':
+ $this->markEmpty($options['guid'], $options['metadata_name']);
+ break;
+ default:
+ $this->markUnknown($options['guid'], $options['metadata_name']);
+ }
+ }
+ }
+ }
+
+ /**
+ * Populate the cache from a set of entities
+ *
+ * @param int|array $guids Array of or single GUIDs
+ * @return void
+ */
+ public function populateFromEntities($guids) {
+ if (empty($guids)) {
+ return;
+ }
+ if (!is_array($guids)) {
+ $guids = array($guids);
+ }
+ $guids = array_unique($guids);
+
+ // could be useful at some point in future
+ //$guids = $this->filterMetadataHeavyEntities($guids);
+
+ $db_prefix = elgg_get_config('dbprefix');
+ $options = array(
+ 'guids' => $guids,
+ 'limit' => 0,
+ 'callback' => false,
+ 'joins' => array(
+ "JOIN {$db_prefix}metastrings v ON n_table.value_id = v.id",
+ "JOIN {$db_prefix}metastrings n ON n_table.name_id = n.id",
+ ),
+ 'selects' => array('n.string AS name', 'v.string AS value'),
+ 'order_by' => 'n_table.entity_guid, n_table.time_created ASC',
+
+ // @todo don't know why this is necessary
+ 'wheres' => array(get_access_sql_suffix('n_table')),
+ );
+ $data = elgg_get_metadata($options);
+
+ // build up metadata for each entity, save when GUID changes (or data ends)
+ $last_guid = null;
+ $metadata = array();
+ $last_row_idx = count($data) - 1;
+ foreach ($data as $i => $row) {
+ $name = $row->name;
+ $value = ($row->value_type === 'text') ? $row->value : (int) $row->value;
+ $guid = $row->entity_guid;
+ if ($guid !== $last_guid) {
+ if ($last_guid) {
+ $this->saveAll($last_guid, $metadata);
+ }
+ $metadata = array();
+ }
+ if (isset($metadata[$name])) {
+ $metadata[$name] = (array) $metadata[$name];
+ $metadata[$name][] = $value;
+ } else {
+ $metadata[$name] = $value;
+ }
+ if (($i == $last_row_idx)) {
+ $this->saveAll($guid, $metadata);
+ }
+ $last_guid = $guid;
+ }
+ }
+
+ /**
+ * Filter out entities whose concatenated metadata values (INTs casted as string)
+ * exceed a threshold in characters. This could be used to avoid overpopulating the
+ * cache if RAM usage becomes an issue.
+ *
+ * @param array $guids GUIDs of entities to examine
+ * @param int $limit Limit in characters of all metadata (with ints casted to strings)
+ * @return array
+ */
+ public function filterMetadataHeavyEntities(array $guids, $limit = 1024000) {
+ $db_prefix = elgg_get_config('dbprefix');
+
+ $options = array(
+ 'guids' => $guids,
+ 'limit' => 0,
+ 'callback' => false,
+ 'joins' => "JOIN {$db_prefix}metastrings v ON n_table.value_id = v.id",
+ 'selects' => array('SUM(LENGTH(v.string)) AS bytes'),
+ 'order_by' => 'n_table.entity_guid, n_table.time_created ASC',
+ 'group_by' => 'n_table.entity_guid',
+ );
+ $data = elgg_get_metadata($options);
+ // don't cache if metadata for entity is over 10MB (or rolled INT)
+ foreach ($data as $row) {
+ if ($row->bytes > $limit || $row->bytes < 0) {
+ array_splice($guids, array_search($row->entity_guid, $guids), 1);
+ }
+ }
+ return $guids;
+ }
+}
diff --git a/engine/classes/ElggWidget.php b/engine/classes/ElggWidget.php
new file mode 100644
index 000000000..66191bf47
--- /dev/null
+++ b/engine/classes/ElggWidget.php
@@ -0,0 +1,245 @@
+<?php
+
+/**
+ * ElggWidget
+ *
+ * Stores metadata in private settings rather than as ElggMetadata
+ *
+ * @package Elgg.Core
+ * @subpackage Widgets
+ *
+ * @property-read string $handler internal, do not use
+ * @property-read string $column internal, do not use
+ * @property-read string $order internal, do not use
+ * @property-read string $context internal, do not use
+ */
+class ElggWidget extends ElggObject {
+
+ /**
+ * Set subtype to widget.
+ *
+ * @return void
+ */
+ protected function initializeAttributes() {
+ parent::initializeAttributes();
+
+ $this->attributes['subtype'] = "widget";
+ }
+
+ /**
+ * Override entity get and sets in order to save data to private data store.
+ *
+ * @param string $name Name
+ *
+ * @return mixed
+ */
+ public function get($name) {
+ // See if its in our base attribute
+ if (array_key_exists($name, $this->attributes)) {
+ return $this->attributes[$name];
+ }
+
+ // No, so see if its in the private data store.
+ $meta = $this->getPrivateSetting($name);
+ if ($meta) {
+ return $meta;
+ }
+
+ // Can't find it, so return null
+ return null;
+ }
+
+ /**
+ * Override entity get and sets in order to save data to private data store.
+ *
+ * @param string $name Name
+ * @param string $value Value
+ *
+ * @return bool
+ */
+ public function set($name, $value) {
+ if (array_key_exists($name, $this->attributes)) {
+ // Check that we're not trying to change the guid!
+ if ((array_key_exists('guid', $this->attributes)) && ($name == 'guid')) {
+ return false;
+ }
+
+ $this->attributes[$name] = $value;
+ } else {
+ return $this->setPrivateSetting($name, $value);
+ }
+
+ return true;
+ }
+
+ /**
+ * Set the widget context
+ *
+ * @param string $context The widget context
+ * @return bool
+ * @since 1.8.0
+ */
+ public function setContext($context) {
+ return $this->setPrivateSetting('context', $context);
+ }
+
+ /**
+ * Get the widget context
+ *
+ * @return string
+ * @since 1.8.0
+ */
+ public function getContext() {
+ return $this->getPrivateSetting('context');
+ }
+
+ /**
+ * Get the title of the widget
+ *
+ * @return string
+ * @since 1.8.0
+ */
+ public function getTitle() {
+ $title = $this->title;
+ if (!$title) {
+ global $CONFIG;
+ $title = $CONFIG->widgets->handlers[$this->handler]->name;
+ }
+ return $title;
+ }
+
+ /**
+ * Move the widget
+ *
+ * @param int $column The widget column
+ * @param int $rank Zero-based rank from the top of the column
+ * @return void
+ * @since 1.8.0
+ */
+ public function move($column, $rank) {
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'widget',
+ 'container_guid' => $this->container_guid,
+ 'limit' => false,
+ 'private_setting_name_value_pairs' => array(
+ array('name' => 'context', 'value' => $this->getContext()),
+ array('name' => 'column', 'value' => $column)
+ )
+ );
+ $widgets = elgg_get_entities_from_private_settings($options);
+ if (!$widgets) {
+ $this->column = (int)$column;
+ $this->order = 0;
+ return;
+ }
+
+ 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]);
+ }
+ }
+
+ $bottom_rank = count($widgets);
+ if ($column == $this->column) {
+ $bottom_rank--;
+ }
+
+ if ($rank == 0) {
+ // top of the column
+ $this->order = reset($widgets)->order - 10;
+ } elseif ($rank == $bottom_rank) {
+ // bottom of the column of active widgets
+ $this->order = end($widgets)->order + 10;
+ } else {
+ // reorder widgets
+
+ // remove the widget that's being moved from the array
+ foreach ($widgets as $index => $widget) {
+ if ($widget->guid == $this->guid) {
+ unset($widgets[$index]);
+ }
+ }
+
+ // 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);
+ $widgets = array_merge($before, $after);
+ ksort($widgets);
+ $order = 0;
+ foreach ($widgets as $widget) {
+ $widget->order = $order;
+ $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;
+ }
+
+ /**
+ * Saves the widget's settings
+ *
+ * Plugins can override the save mechanism using the plugin hook:
+ * 'widget_settings', <widget handler identifier>. The widget and
+ * the parameters are passed. The plugin hook handler should return
+ * true to indicate that it has successfully saved the settings.
+ *
+ * @warning The values in the parameter array cannot be arrays
+ *
+ * @param array $params An array of name => value parameters
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+ public function saveSettings($params) {
+ if (!$this->canEdit()) {
+ return false;
+ }
+
+ // plugin hook handlers should return true to indicate the settings have
+ // been saved so that default code does not run
+ $hook_params = array(
+ 'widget' => $this,
+ 'params' => $params
+ );
+ if (elgg_trigger_plugin_hook('widget_settings', $this->handler, $hook_params, false) == true) {
+ return true;
+ }
+
+ if (is_array($params) && count($params) > 0) {
+ foreach ($params as $name => $value) {
+ if (is_array($value)) {
+ // private settings cannot handle arrays
+ return false;
+ } else {
+ $this->$name = $value;
+ }
+ }
+ $this->save();
+ }
+
+ return true;
+ }
+}
diff --git a/engine/classes/ElggXMLElement.php b/engine/classes/ElggXMLElement.php
new file mode 100644
index 000000000..cbd3fc5ce
--- /dev/null
+++ b/engine/classes/ElggXMLElement.php
@@ -0,0 +1,131 @@
+<?php
+/**
+ * A parser for XML that uses SimpleXMLElement
+ *
+ * @package Elgg.Core
+ * @subpackage XML
+ */
+class ElggXMLElement {
+ /**
+ * @var SimpleXMLElement
+ */
+ private $_element;
+
+ /**
+ * Creates an ElggXMLParser from a string or existing SimpleXMLElement
+ *
+ * @param string|SimpleXMLElement $xml The XML to parse
+ */
+ public function __construct($xml) {
+ if ($xml instanceof SimpleXMLElement) {
+ $this->_element = $xml;
+ } else {
+ // do not load entities
+ $disable_load_entities = libxml_disable_entity_loader(true);
+
+ $this->_element = new SimpleXMLElement($xml);
+
+ libxml_disable_entity_loader($disable_load_entities);
+ }
+ }
+
+ /**
+ * @return string The name of the element
+ */
+ public function getName() {
+ return $this->_element->getName();
+ }
+
+ /**
+ * @return string[] The attributes
+ */
+ public function getAttributes() {
+ //include namespace declarations as attributes
+ $xmlnsRaw = $this->_element->getNamespaces();
+ $xmlns = array();
+ foreach ($xmlnsRaw as $key => $val) {
+ $label = 'xmlns' . ($key ? ":$key" : $key);
+ $xmlns[$label] = $val;
+ }
+ //get attributes and merge with namespaces
+ $attrRaw = $this->_element->attributes();
+ $attr = array();
+ foreach ($attrRaw as $key => $val) {
+ $attr[$key] = $val;
+ }
+ $attr = array_merge((array) $xmlns, (array) $attr);
+ $result = array();
+ foreach ($attr as $key => $val) {
+ $result[$key] = (string) $val;
+ }
+ return $result;
+ }
+
+ /**
+ * @return string CData
+ */
+ public function getContent() {
+ return (string) $this->_element;
+ }
+
+ /**
+ * @return ElggXMLElement[] Child elements
+ */
+ public function getChildren() {
+ $children = $this->_element->children();
+ $result = array();
+ foreach ($children as $val) {
+ $result[] = new ElggXMLElement($val);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Override ->
+ *
+ * @param string $name Property name
+ * @return mixed
+ */
+ function __get($name) {
+ switch ($name) {
+ case 'name':
+ return $this->getName();
+ break;
+ case 'attributes':
+ return $this->getAttributes();
+ break;
+ case 'content':
+ return $this->getContent();
+ break;
+ case 'children':
+ return $this->getChildren();
+ break;
+ }
+ return null;
+ }
+
+ /**
+ * Override isset
+ *
+ * @param string $name Property name
+ * @return boolean
+ */
+ function __isset($name) {
+ switch ($name) {
+ case 'name':
+ return $this->getName() !== null;
+ break;
+ case 'attributes':
+ return $this->getAttributes() !== null;
+ break;
+ case 'content':
+ return $this->getContent() !== null;
+ break;
+ case 'children':
+ return $this->getChildren() !== null;
+ break;
+ }
+ return false;
+ }
+}
diff --git a/engine/classes/ErrorResult.php b/engine/classes/ErrorResult.php
new file mode 100644
index 000000000..afad4c740
--- /dev/null
+++ b/engine/classes/ErrorResult.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * ErrorResult
+ * The error result class.
+ *
+ * @package Elgg.Core
+ * @subpackage WebServicesAPI
+ */
+class ErrorResult extends GenericResult {
+ // Fail with no specific code
+ public static $RESULT_FAIL = -1 ;
+
+ public static $RESULT_FAIL_APIKEY_DISABLED = -30;
+ public static $RESULT_FAIL_APIKEY_INACTIVE = -31;
+ public static $RESULT_FAIL_APIKEY_INVALID = -32;
+
+ // Invalid, expired or missing auth token
+ public static $RESULT_FAIL_AUTHTOKEN = -20;
+
+ /**
+ * A new error result
+ *
+ * @param string $message Message
+ * @param int $code Error Code
+ * @param Exception $exception Exception object
+ *
+ * @return void
+ */
+ public function __construct($message, $code = "", Exception $exception = NULL) {
+ if ($code == "") {
+ $code = ErrorResult::$RESULT_FAIL;
+ }
+
+ if ($exception != NULL) {
+ $this->setResult($exception->__toString());
+ }
+
+ $this->setStatusCode($code, $message);
+ }
+
+ /**
+ * Get a new instance of the ErrorResult.
+ *
+ * @param string $message Message
+ * @param int $code Code
+ * @param Exception $exception Optional exception for generating a stack trace.
+ *
+ * @return ErrorResult
+ */
+ public static function getInstance($message, $code = "", Exception $exception = NULL) {
+ // Return a new error object.
+ return new ErrorResult($message, $code, $exception);
+ }
+}
diff --git a/engine/classes/ExportException.php b/engine/classes/ExportException.php
new file mode 100644
index 000000000..ae8a8e41b
--- /dev/null
+++ b/engine/classes/ExportException.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Export exception
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ *
+ */
+class ExportException extends DataFormatException {}
diff --git a/engine/classes/Exportable.php b/engine/classes/Exportable.php
new file mode 100644
index 000000000..0c1ea5282
--- /dev/null
+++ b/engine/classes/Exportable.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Define an interface for all ODD exportable objects.
+ *
+ * @package Elgg.Core
+ * @subpackage ODD
+ */
+interface Exportable {
+ /**
+ * This must take the contents of the object and convert it to exportable ODD
+ *
+ * @return object or array of objects.
+ */
+ public function export();
+
+ /**
+ * Return a list of all fields that can be exported.
+ * This should be used as the basis for the values returned by export()
+ *
+ * @return array
+ */
+ public function getExportableValues();
+}
diff --git a/engine/classes/Friendable.php b/engine/classes/Friendable.php
new file mode 100644
index 000000000..c308b4598
--- /dev/null
+++ b/engine/classes/Friendable.php
@@ -0,0 +1,104 @@
+<?php
+/**
+ * An interface for objects that behave as elements within a social network that have a profile.
+ *
+ * @package Elgg.Core
+ * @subpackage SocialModel.Friendable
+ */
+interface Friendable {
+ /**
+ * Adds a user as a friend
+ *
+ * @param int $friend_guid The GUID of the user to add
+ *
+ * @return bool
+ */
+ public function addFriend($friend_guid);
+
+ /**
+ * Removes a user as a friend
+ *
+ * @param int $friend_guid The GUID of the user to remove
+ *
+ * @return bool
+ */
+ public function removeFriend($friend_guid);
+
+ /**
+ * Determines whether or not the current user is a friend of this entity
+ *
+ * @return bool
+ */
+ public function isFriend();
+
+ /**
+ * Determines whether or not this entity is friends with a particular entity
+ *
+ * @param int $user_guid The GUID of the entity this entity may or may not be friends with
+ *
+ * @return bool
+ */
+ public function isFriendsWith($user_guid);
+
+ /**
+ * Determines whether or not a foreign entity has made this one a friend
+ *
+ * @param int $user_guid The GUID of the foreign entity
+ *
+ * @return bool
+ */
+ public function isFriendOf($user_guid);
+
+ /**
+ * Returns this entity's friends
+ *
+ * @param string $subtype The subtype of entity to return
+ * @param int $limit The number of entities to return
+ * @param int $offset Indexing offset
+ *
+ * @return array|false
+ */
+ public function getFriends($subtype = "", $limit = 10, $offset = 0);
+
+ /**
+ * Returns entities that have made this entity a friend
+ *
+ * @param string $subtype The subtype of entity to return
+ * @param int $limit The number of entities to return
+ * @param int $offset Indexing offset
+ *
+ * @return array|false
+ */
+ public function getFriendsOf($subtype = "", $limit = 10, $offset = 0);
+
+ /**
+ * Returns objects in this entity's container
+ *
+ * @param string $subtype The subtype of entity to return
+ * @param int $limit The number of entities to return
+ * @param int $offset Indexing offset
+ *
+ * @return array|false
+ */
+ public function getObjects($subtype = "", $limit = 10, $offset = 0);
+
+ /**
+ * Returns objects in the containers of this entity's friends
+ *
+ * @param string $subtype The subtype of entity to return
+ * @param int $limit The number of entities to return
+ * @param int $offset Indexing offset
+ *
+ * @return array|false
+ */
+ public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0);
+
+ /**
+ * Returns the number of object entities in this entity's container
+ *
+ * @param string $subtype The subtype of entity to count
+ *
+ * @return int
+ */
+ public function countObjects($subtype = "");
+}
diff --git a/engine/classes/GenericResult.php b/engine/classes/GenericResult.php
new file mode 100644
index 000000000..e42e924d1
--- /dev/null
+++ b/engine/classes/GenericResult.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * GenericResult Result superclass.
+ *
+ * @package Elgg.Core
+ * @subpackage WebServicesAPI
+ */
+abstract class GenericResult {
+ /**
+ * The status of the result.
+ * @var int
+ */
+ private $status_code;
+
+ /**
+ * Message returned along with the status which is almost always an error message.
+ * This must be human readable, understandable and localised.
+ * @var string
+ */
+ private $message;
+
+ /**
+ * Result store.
+ * Attach result specific informaton here.
+ *
+ * @var mixed. Should probably be an object of some sort.
+ */
+ private $result;
+
+ /**
+ * Set a status code and optional message.
+ *
+ * @param int $status The status code.
+ * @param string $message The message.
+ *
+ * @return void
+ */
+ protected function setStatusCode($status, $message = "") {
+ $this->status_code = $status;
+ $this->message = $message;
+ }
+
+ /**
+ * Set the result.
+ *
+ * @param mixed $result The result
+ *
+ * @return void
+ */
+ protected function setResult($result) {
+ $this->result = $result;
+ }
+
+ /**
+ * Return the current status code
+ *
+ * @return string
+ */
+ protected function getStatusCode() {
+ return $this->status_code;
+ }
+
+ /**
+ * Return the current status message
+ *
+ * @return string
+ */
+ protected function getStatusMessage() {
+ return $this->message;
+ }
+
+ /**
+ * Return the current result
+ *
+ * @return string
+ */
+ protected function getResult() {
+ return $this->result;
+ }
+
+ /**
+ * Serialise to a standard class.
+ *
+ * DEVNOTE: The API is only interested in data, we can not easily serialise
+ * custom classes without the need for 1) the other side being PHP, 2) you need to have the class
+ * definition installed, 3) its the right version!
+ *
+ * Therefore, I'm not bothering.
+ *
+ * Override this to include any more specific information, however api results
+ * should be attached to the class using setResult().
+ *
+ * if $CONFIG->debug is set then additional information about the runtime environment and
+ * authentication will be returned.
+ *
+ * @return stdClass Object containing the serialised result.
+ */
+ public function export() {
+ global $ERRORS, $CONFIG, $_PAM_HANDLERS_MSG;
+
+ $result = new stdClass;
+
+ $result->status = $this->getStatusCode();
+ if ($this->getStatusMessage() != "") {
+ $result->message = $this->getStatusMessage();
+ }
+
+ $resultdata = $this->getResult();
+ if (isset($resultdata)) {
+ $result->result = $resultdata;
+ }
+
+ if (isset($CONFIG->debug)) {
+ if (count($ERRORS)) {
+ $result->runtime_errors = $ERRORS;
+ }
+
+ if (count($_PAM_HANDLERS_MSG)) {
+ $result->pam = $_PAM_HANDLERS_MSG;
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/engine/classes/IOException.php b/engine/classes/IOException.php
new file mode 100644
index 000000000..57403f44c
--- /dev/null
+++ b/engine/classes/IOException.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * IOException
+ * An IO Exception, throw when an IO Exception occurs. Subclass for specific IO Exceptions.
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ */
+class IOException extends Exception {}
diff --git a/engine/classes/ImportException.php b/engine/classes/ImportException.php
new file mode 100644
index 000000000..909c599d5
--- /dev/null
+++ b/engine/classes/ImportException.php
@@ -0,0 +1,8 @@
+<?php
+/**
+ * Import exception
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ */
+class ImportException extends DataFormatException {}
diff --git a/engine/classes/Importable.php b/engine/classes/Importable.php
new file mode 100644
index 000000000..23b2ce2c8
--- /dev/null
+++ b/engine/classes/Importable.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Define an interface for all ODD importable objects.
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel.Importable
+ */
+interface Importable {
+ /**
+ * Accepts an array of data to import, this data is parsed from the XML produced by export.
+ * The function should return the constructed object data, or NULL.
+ *
+ * @param ODD $data Data in ODD format
+ *
+ * @return bool
+ * @throws ImportException if there was a critical error importing data.
+ */
+ public function import(ODD $data);
+}
diff --git a/engine/classes/IncompleteEntityException.php b/engine/classes/IncompleteEntityException.php
new file mode 100644
index 000000000..8c86edcc6
--- /dev/null
+++ b/engine/classes/IncompleteEntityException.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * IncompleteEntityException
+ * Thrown when constructing an entity that is missing its secondary entity table
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ * @access private
+ */
+class IncompleteEntityException extends Exception {}
diff --git a/engine/classes/InstallationException.php b/engine/classes/InstallationException.php
new file mode 100644
index 000000000..1dad6c1e5
--- /dev/null
+++ b/engine/classes/InstallationException.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * InstallationException
+ * Thrown when there is a major problem with the installation.
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ */
+class InstallationException extends ConfigurationException {}
diff --git a/engine/classes/InvalidClassException.php b/engine/classes/InvalidClassException.php
new file mode 100644
index 000000000..12f353b9a
--- /dev/null
+++ b/engine/classes/InvalidClassException.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * InvalidClassException
+ * An invalid class Exception, throw when a class is invalid.
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ */
+class InvalidClassException extends ClassException {}
diff --git a/engine/classes/InvalidParameterException.php b/engine/classes/InvalidParameterException.php
new file mode 100644
index 000000000..fbc9bffc9
--- /dev/null
+++ b/engine/classes/InvalidParameterException.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * InvalidParameterException
+ * A parameter is invalid.
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ */
+class InvalidParameterException extends CallException {}
diff --git a/engine/classes/Locatable.php b/engine/classes/Locatable.php
new file mode 100644
index 000000000..7287d9798
--- /dev/null
+++ b/engine/classes/Locatable.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * Define an interface for geo-tagging entities.
+ *
+ * @package Elgg.Core
+ * @subpackage SocialModel.Locatable
+ */
+interface Locatable {
+ /**
+ * Set a location text
+ *
+ * @param string $location Textual representation of location
+ *
+ * @return bool
+ */
+ public function setLocation($location);
+
+ /**
+ * Set latitude and longitude tags for a given entity.
+ *
+ * @param float $lat Latitude
+ * @param float $long Longitude
+ *
+ * @return bool
+ */
+ public function setLatLong($lat, $long);
+
+ /**
+ * Get the contents of the ->geo:lat field.
+ *
+ * @return int
+ */
+ public function getLatitude();
+
+ /**
+ * Get the contents of the ->geo:lat field.
+ *
+ * @return int
+ */
+ public function getLongitude();
+
+ /**
+ * Get the ->location metadata.
+ *
+ * @return string
+ */
+ public function getLocation();
+}
diff --git a/engine/classes/Loggable.php b/engine/classes/Loggable.php
new file mode 100644
index 000000000..b9e8bf26b
--- /dev/null
+++ b/engine/classes/Loggable.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Interface that provides an interface which must be implemented by all objects wishing to be
+ * recorded in the system log (and by extension the river).
+ *
+ * This interface defines a set of methods that permit the system log functions to
+ * hook in and retrieve the necessary information and to identify what events can
+ * actually be logged.
+ *
+ * To have events involving your object to be logged simply implement this interface.
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel.Loggable
+ */
+interface Loggable {
+ /**
+ * Return an identification for the object for storage in the system log.
+ * This id must be an integer.
+ *
+ * @return int
+ */
+ public function getSystemLogID();
+
+ /**
+ * Return the class name of the object.
+ * Added as a function because get_class causes errors for some reason.
+ *
+ * @return string
+ */
+ public function getClassName();
+
+ /**
+ * Return the type of the object - eg. object, group, user, relationship, metadata, annotation etc
+ *
+ * @return string
+ */
+ public function getType();
+
+ /**
+ * Return a subtype. For metadata & annotations this is the 'name' and for relationship this is the
+ * relationship type.
+ *
+ * @return string
+ */
+ public function getSubtype();
+
+ /**
+ * For a given ID, return the object associated with it.
+ * This is used by the river functionality primarily.
+ * This is useful for checking access permissions etc on objects.
+ *
+ * @param int $id GUID of an entity
+ *
+ * @return ElggEntity
+ */
+ public function getObjectFromID($id);
+
+ /**
+ * Return the GUID of the owner of this object.
+ *
+ * @return int
+ * @deprecated 1.8 Use getOwnerGUID() instead
+ */
+ public function getObjectOwnerGUID();
+}
diff --git a/engine/classes/LoginException.php b/engine/classes/LoginException.php
new file mode 100644
index 000000000..7546fa36f
--- /dev/null
+++ b/engine/classes/LoginException.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Login Exception Stub
+ *
+ * Generic parent class for login exceptions.
+ *
+ * @package Elgg.Core
+ * @subpackage Exceptions.Stub
+ */
+class LoginException extends Exception {}
diff --git a/engine/classes/NotImplementedException.php b/engine/classes/NotImplementedException.php
new file mode 100644
index 000000000..d1decf75c
--- /dev/null
+++ b/engine/classes/NotImplementedException.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * NotImplementedException
+ * Thrown when a method or function has not been implemented, primarily used
+ * in development... you should not see these!
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ */
+class NotImplementedException extends CallException {}
diff --git a/engine/classes/Notable.php b/engine/classes/Notable.php
new file mode 100644
index 000000000..0c21af27d
--- /dev/null
+++ b/engine/classes/Notable.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Calendar interface for events.
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel.Notable
+ *
+ * @todo Implement or remove.
+ */
+interface Notable {
+ /**
+ * Calendar functionality.
+ * This function sets the time of an object on a calendar listing.
+ *
+ * @param int $hour If ommitted, now is assumed.
+ * @param int $minute If ommitted, now is assumed.
+ * @param int $second If ommitted, now is assumed.
+ * @param int $day If ommitted, now is assumed.
+ * @param int $month If ommitted, now is assumed.
+ * @param int $year If ommitted, now is assumed.
+ * @param int $duration Duration of event, remainder of the day is assumed.
+ *
+ * @return bool
+ */
+ public function setCalendarTimeAndDuration($hour = NULL, $minute = NULL, $second = NULL,
+ $day = NULL, $month = NULL, $year = NULL, $duration = NULL);
+
+ /**
+ * Return the start timestamp.
+ *
+ * @return int
+ */
+ public function getCalendarStartTime();
+
+ /**
+ * Return the end timestamp.
+ *
+ * @return int
+ */
+ public function getCalendarEndTime();
+}
diff --git a/engine/classes/NotificationException.php b/engine/classes/NotificationException.php
new file mode 100644
index 000000000..71c742f17
--- /dev/null
+++ b/engine/classes/NotificationException.php
@@ -0,0 +1,8 @@
+<?php
+/**
+ * Notification exception.
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ */
+class NotificationException extends Exception {}
diff --git a/engine/classes/ODD.php b/engine/classes/ODD.php
new file mode 100644
index 000000000..fa5b616fc
--- /dev/null
+++ b/engine/classes/ODD.php
@@ -0,0 +1,131 @@
+<?php
+/**
+ * Open Data Definition (ODD) superclass.
+ *
+ * @package Elgg.Core
+ * @subpackage ODD
+ */
+abstract class ODD {
+ /**
+ * Attributes.
+ */
+ private $attributes = array();
+
+ /**
+ * Optional body.
+ */
+ private $body;
+
+ /**
+ * Construct an ODD document with initial values.
+ */
+ public function __construct() {
+ $this->body = "";
+ }
+
+ /**
+ * Returns an array of attributes
+ *
+ * @return array
+ */
+ public function getAttributes() {
+ return $this->attributes;
+ }
+
+ /**
+ * Sets an attribute
+ *
+ * @param string $key Name
+ * @param mixed $value Value
+ *
+ * @return void
+ */
+ public function setAttribute($key, $value) {
+ $this->attributes[$key] = $value;
+ }
+
+ /**
+ * Returns an attribute
+ *
+ * @param string $key Name
+ *
+ * @return mixed
+ */
+ public function getAttribute($key) {
+ if (isset($this->attributes[$key])) {
+ return $this->attributes[$key];
+ }
+
+ return NULL;
+ }
+
+ /**
+ * Sets the body of the ODD.
+ *
+ * @param mixed $value Value
+ *
+ * @return void
+ */
+ public function setBody($value) {
+ $this->body = $value;
+ }
+
+ /**
+ * Gets the body of the ODD.
+ *
+ * @return mixed
+ */
+ public function getBody() {
+ return $this->body;
+ }
+
+ /**
+ * Set the published time.
+ *
+ * @param int $time Unix timestamp
+ *
+ * @return void
+ */
+ public function setPublished($time) {
+ $this->attributes['published'] = date("r", $time);
+ }
+
+ /**
+ * Return the published time as a unix timestamp.
+ *
+ * @return int or false on failure.
+ */
+ public function getPublishedAsTime() {
+ return strtotime($this->attributes['published']);
+ }
+
+ /**
+ * For serialisation, implement to return a string name of the tag eg "header" or "metadata".
+ *
+ * @return string
+ */
+ abstract protected function getTagName();
+
+ /**
+ * Magic function to generate valid ODD XML for this item.
+ *
+ * @return string
+ */
+ public function __toString() {
+ // Construct attributes
+ $attr = "";
+ foreach ($this->attributes as $k => $v) {
+ $attr .= ($v != "") ? "$k=\"$v\" " : "";
+ }
+
+ $body = $this->getBody();
+ $tag = $this->getTagName();
+
+ $end = "/>";
+ if ($body != "") {
+ $end = "><![CDATA[$body]]></{$tag}>";
+ }
+
+ return "<{$tag} $attr" . $end . "\n";
+ }
+}
diff --git a/engine/classes/ODDDocument.php b/engine/classes/ODDDocument.php
new file mode 100644
index 000000000..540c35a3b
--- /dev/null
+++ b/engine/classes/ODDDocument.php
@@ -0,0 +1,202 @@
+<?php
+/**
+ * This class is used during import and export to construct.
+ *
+ * @package Elgg.Core
+ * @subpackage ODD
+ */
+class ODDDocument implements Iterator {
+ /**
+ * ODD Version
+ *
+ * @var string
+ */
+ private $ODDSupportedVersion = "1.0";
+
+ /**
+ * Elements of the document.
+ */
+ private $elements;
+
+ /**
+ * Optional wrapper factory.
+ */
+ private $wrapperfactory;
+
+ /**
+ * Create a new ODD Document.
+ *
+ * @param array $elements Elements to add
+ *
+ * @return void
+ */
+ public function __construct(array $elements = NULL) {
+ if ($elements) {
+ if (is_array($elements)) {
+ $this->elements = $elements;
+ } else {
+ $this->addElement($elements);
+ }
+ } else {
+ $this->elements = array();
+ }
+ }
+
+ /**
+ * Return the version of ODD being used.
+ *
+ * @return string
+ */
+ public function getVersion() {
+ return $this->ODDSupportedVersion;
+ }
+
+ /**
+ * Returns the number of elements
+ *
+ * @return int
+ */
+ public function getNumElements() {
+ return count($this->elements);
+ }
+
+ /**
+ * Add an element
+ *
+ * @param ODD $element An ODD element
+ *
+ * @return void
+ */
+ public function addElement(ODD $element) {
+ if (!is_array($this->elements)) {
+ $this->elements = array();
+ }
+ $this->elements[] = $element;
+ }
+
+ /**
+ * Add multiple elements at once
+ *
+ * @param array $elements Array of ODD elements
+ *
+ * @return void
+ */
+ public function addElements(array $elements) {
+ foreach ($elements as $element) {
+ $this->addElement($element);
+ }
+ }
+
+ /**
+ * Return all elements
+ *
+ * @return array
+ */
+ public function getElements() {
+ return $this->elements;
+ }
+
+ /**
+ * Set an optional wrapper factory to optionally embed the ODD document in another format.
+ *
+ * @param ODDWrapperFactory $factory The factory
+ *
+ * @return void
+ */
+ public function setWrapperFactory(ODDWrapperFactory $factory) {
+ $this->wrapperfactory = $factory;
+ }
+
+ /**
+ * Magic function to generate valid ODD XML for this item.
+ *
+ * @return string
+ */
+ public function __toString() {
+ $xml = "";
+
+ if ($this->wrapperfactory) {
+ // A wrapper has been provided
+ $wrapper = $this->wrapperfactory->getElementWrapper($this); // Get the wrapper for this element
+
+ $xml = $wrapper->wrap($this); // Wrap this element (and subelements)
+ } else {
+ // Output begin tag
+ $generated = date("r");
+ $xml .= "<odd version=\"{$this->ODDSupportedVersion}\" generated=\"$generated\">\n";
+
+ // Get XML for elements
+ foreach ($this->elements as $element) {
+ $xml .= "$element";
+ }
+
+ // Output end tag
+ $xml .= "</odd>\n";
+ }
+
+ return $xml;
+ }
+
+ // ITERATOR INTERFACE //////////////////////////////////////////////////////////////
+ /*
+ * This lets an entity's attributes be displayed using foreach as a normal array.
+ * Example: http://www.sitepoint.com/print/php5-standard-library
+ */
+
+ private $valid = FALSE;
+
+ /**
+ * Iterator interface
+ *
+ * @see Iterator::rewind()
+ *
+ * @return void
+ */
+ function rewind() {
+ $this->valid = (FALSE !== reset($this->elements));
+ }
+
+ /**
+ * Iterator interface
+ *
+ * @see Iterator::current()
+ *
+ * @return void
+ */
+ function current() {
+ return current($this->elements);
+ }
+
+ /**
+ * Iterator interface
+ *
+ * @see Iterator::key()
+ *
+ * @return void
+ */
+ function key() {
+ return key($this->elements);
+ }
+
+ /**
+ * Iterator interface
+ *
+ * @see Iterator::next()
+ *
+ * @return void
+ */
+ function next() {
+ $this->valid = (FALSE !== next($this->elements));
+ }
+
+ /**
+ * Iterator interface
+ *
+ * @see Iterator::valid()
+ *
+ * @return void
+ */
+ function valid() {
+ return $this->valid;
+ }
+}
diff --git a/engine/classes/ODDEntity.php b/engine/classes/ODDEntity.php
new file mode 100644
index 000000000..e9bb5da6a
--- /dev/null
+++ b/engine/classes/ODDEntity.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * ODD Entity class.
+ *
+ * @package Elgg.Core
+ * @subpackage ODD
+ */
+class ODDEntity extends ODD {
+
+ /**
+ * New ODD Entity
+ *
+ * @param string $uuid A universally unique ID
+ * @param string $class Class
+ * @param string $subclass Subclass
+ */
+ function __construct($uuid, $class, $subclass = "") {
+ parent::__construct();
+
+ $this->setAttribute('uuid', $uuid);
+ $this->setAttribute('class', $class);
+ $this->setAttribute('subclass', $subclass);
+ }
+
+ /**
+ * Returns entity.
+ *
+ * @return 'entity'
+ */
+ protected function getTagName() {
+ return "entity";
+ }
+}
diff --git a/engine/classes/ODDMetaData.php b/engine/classes/ODDMetaData.php
new file mode 100644
index 000000000..09b653582
--- /dev/null
+++ b/engine/classes/ODDMetaData.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * ODD Metadata class.
+ *
+ * @package Elgg.Core
+ * @subpackage ODD
+ */
+class ODDMetaData extends ODD {
+
+ /**
+ * New ODD metadata
+ *
+ * @param string $uuid Unique ID
+ * @param string $entity_uuid Another unique ID
+ * @param string $name Name
+ * @param string $value Value
+ * @param string $type Type
+ * @param string $owner_uuid Owner ID
+ */
+ function __construct($uuid, $entity_uuid, $name, $value, $type = "", $owner_uuid = "") {
+ parent::__construct();
+
+ $this->setAttribute('uuid', $uuid);
+ $this->setAttribute('entity_uuid', $entity_uuid);
+ $this->setAttribute('name', $name);
+ $this->setAttribute('type', $type);
+ $this->setAttribute('owner_uuid', $owner_uuid);
+ $this->setBody($value);
+ }
+
+ /**
+ * Returns 'metadata'
+ *
+ * @return string 'metadata'
+ */
+ protected function getTagName() {
+ return "metadata";
+ }
+}
diff --git a/engine/classes/ODDRelationship.php b/engine/classes/ODDRelationship.php
new file mode 100644
index 000000000..8b1fe217b
--- /dev/null
+++ b/engine/classes/ODDRelationship.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * ODD Relationship class.
+ *
+ * @package Elgg
+ * @subpackage Core
+ */
+class ODDRelationship extends ODD {
+
+ /**
+ * New ODD Relationship
+ *
+ * @param string $uuid1 First UUID
+ * @param string $type Type of telationship
+ * @param string $uuid2 Second UUId
+ */
+ function __construct($uuid1, $type, $uuid2) {
+ parent::__construct();
+
+ $this->setAttribute('uuid1', $uuid1);
+ $this->setAttribute('type', $type);
+ $this->setAttribute('uuid2', $uuid2);
+ }
+
+ /**
+ * Returns 'relationship'
+ *
+ * @return string 'relationship'
+ */
+ protected function getTagName() {
+ return "relationship";
+ }
+}
diff --git a/engine/classes/PluginException.php b/engine/classes/PluginException.php
new file mode 100644
index 000000000..a74303695
--- /dev/null
+++ b/engine/classes/PluginException.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * PluginException
+ *
+ * A plugin Exception, thrown when an Exception occurs relating to the plugin mechanism.
+ * Subclass for specific plugin Exceptions.
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ */
+class PluginException extends Exception {}
diff --git a/engine/classes/RegistrationException.php b/engine/classes/RegistrationException.php
new file mode 100644
index 000000000..5246efc25
--- /dev/null
+++ b/engine/classes/RegistrationException.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * RegistrationException
+ * Could not register a new user for whatever reason.
+ *
+ * @package Elgg.Core
+ * @subpackage Exceptions
+ */
+class RegistrationException extends InstallationException {}
diff --git a/engine/classes/SecurityException.php b/engine/classes/SecurityException.php
new file mode 100644
index 000000000..3b6382f9e
--- /dev/null
+++ b/engine/classes/SecurityException.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * SecurityException
+ * An Security Exception, throw when a Security Exception occurs. Subclass for
+ * specific Security Execeptions (access problems etc)
+ *
+ * @package Elgg.Core
+ * @subpackage Exception
+ */
+class SecurityException extends Exception {}
diff --git a/engine/classes/SuccessResult.php b/engine/classes/SuccessResult.php
new file mode 100644
index 000000000..ab5468ad8
--- /dev/null
+++ b/engine/classes/SuccessResult.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * SuccessResult
+ * Generic success result class, extend if you want to do something special.
+ *
+ * @package Elgg.Core
+ * @subpackage WebServicesAPI
+ */
+class SuccessResult extends GenericResult {
+ // Do not change this from 0
+ public static $RESULT_SUCCESS = 0;
+
+ /**
+ * A new success result
+ *
+ * @param string $result The result
+ */
+ public function __construct($result) {
+ $this->setResult($result);
+ $this->setStatusCode(SuccessResult::$RESULT_SUCCESS);
+ }
+
+ /**
+ * Returns a new instance of this class
+ *
+ * @param unknown $result A result of some kind?
+ *
+ * @return SuccessResult
+ */
+ public static function getInstance($result) {
+ // Return a new error object.
+ return new SuccessResult($result);
+ }
+}
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/XMLRPCCall.php b/engine/classes/XMLRPCCall.php
new file mode 100644
index 000000000..fd28f1e3e
--- /dev/null
+++ b/engine/classes/XMLRPCCall.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * An XMLRPC call
+ *
+ * @package Elgg.Core
+ * @subpackage XMLRPC
+ */
+class XMLRPCCall {
+ /** Method name */
+ private $methodname;
+
+ /** Parameters */
+ private $params;
+
+ /**
+ * Construct a new XML RPC Call
+ *
+ * @param string $xml XML
+ */
+ function __construct($xml) {
+ $this->parse($xml);
+ }
+
+ /**
+ * Return the method name associated with the call.
+ *
+ * @return string
+ */
+ public function getMethodName() { return $this->methodname; }
+
+ /**
+ * Return the parameters.
+ * Returns a nested array of XmlElement.
+ *
+ * @see XmlElement
+ * @return array
+ */
+ public function getParameters() { return $this->params; }
+
+ /**
+ * Parse the xml into its components according to spec.
+ * This first version is a little primitive.
+ *
+ * @param string $xml XML
+ *
+ * @return void
+ */
+ private function parse($xml) {
+ $xml = xml_to_object($xml);
+
+ // sanity check
+ if ((isset($xml->name)) && (strcasecmp($xml->name, "methodCall") != 0)) {
+ throw new CallException(elgg_echo('CallException:NotRPCCall'));
+ }
+
+ // method name
+ $this->methodname = $xml->children[0]->content;
+
+ // parameters
+ $this->params = $xml->children[1]->children;
+ }
+}
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/classes/XmlElement.php b/engine/classes/XmlElement.php
new file mode 100644
index 000000000..280bba664
--- /dev/null
+++ b/engine/classes/XmlElement.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * A class representing an XML element for import.
+ *
+ * @package Elgg.Core
+ * @subpackage XML
+ */
+class XmlElement {
+ /** The name of the element */
+ public $name;
+
+ /** The attributes */
+ public $attributes;
+
+ /** CData */
+ public $content;
+
+ /** Child elements */
+ public $children;
+};
diff --git a/engine/handlers/action_handler.php b/engine/handlers/action_handler.php
index f99e1efdc..bcad110b2 100644
--- a/engine/handlers/action_handler.php
+++ b/engine/handlers/action_handler.php
@@ -1,17 +1,20 @@
<?php
-
/**
- * Elgg action handler
+ * Action handler.
+ *
+ * This file dispatches actions. It is called via a URL rewrite in .htaccess
+ * from http://site/action/. Anything after 'action/' is considered the action
+ * and will be passed to {@link action()}.
+ *
+ * @warning This sets the input named 'action' to the current action. When calling
+ * an action, get_input('action') will always return the action name.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Actions
+ * @link http://docs.elgg.org/Tutorials/Actions
*/
-/**
- * Load Elgg framework
- */
-require_once("../start.php");
+require_once(dirname(dirname(__FILE__)) . "/start.php");
+
$action = get_input("action");
-action($action); \ No newline at end of file
+action($action);
diff --git a/engine/handlers/cache_handler.php b/engine/handlers/cache_handler.php
new file mode 100644
index 000000000..36fc665bb
--- /dev/null
+++ b/engine/handlers/cache_handler.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * Cache handler.
+ *
+ * External access to cached CSS and JavaScript views. The cached file URLS
+ * should be of the form: cache/<type>/<viewtype>/<name/of/view>.<unique_id>.<type> where
+ * type is either css or js, view is the name of the cached view, and
+ * unique_id is an identifier that is updated every time the cache is flushed.
+ * The simplest way to maintain a unique identifier is to use the lastcache
+ * variable in Elgg's config object.
+ *
+ * @see elgg_register_simplecache_view()
+ *
+ * @package Elgg.Core
+ * @subpackage Cache
+ */
+
+// Get dataroot
+require_once(dirname(dirname(__FILE__)) . '/settings.php');
+$mysql_dblink = mysql_connect($CONFIG->dbhost, $CONFIG->dbuser, $CONFIG->dbpass, true);
+if (!$mysql_dblink) {
+ echo 'Cache error: unable to connect to database server';
+ exit;
+}
+
+if (!mysql_select_db($CONFIG->dbname, $mysql_dblink)) {
+ echo 'Cache error: unable to connect to Elgg database';
+ exit;
+}
+
+$query = "select name, value from {$CONFIG->dbprefix}datalists
+ where name in ('dataroot', 'simplecache_enabled')";
+
+$result = mysql_query($query, $mysql_dblink);
+if (!$result) {
+ echo 'Cache error: unable to get the data root';
+ exit;
+}
+while ($row = mysql_fetch_object($result)) {
+ ${$row->name} = $row->value;
+}
+mysql_free_result($result);
+
+
+$dirty_request = $_GET['request'];
+// only alphanumeric characters plus /, ., and _ and no '..'
+$filter = array("options" => array("regexp" => "/^(\.?[_a-zA-Z0-9\/]+)+$/"));
+$request = filter_var($dirty_request, FILTER_VALIDATE_REGEXP, $filter);
+if (!$request || !$simplecache_enabled) {
+ echo 'Cache error: bad request';
+ exit;
+}
+
+// testing showed regex to be marginally faster than array / string functions over 100000 reps
+// it won't make a difference in real life and regex is easier to read.
+// <type>/<viewtype>/<name/of/view.and.dots>.<ts>.<type>
+$regex = '|([^/]+)/([^/]+)/(.+)\.([^\.]+)\.([^.]+)$|';
+preg_match($regex, $request, $matches);
+
+$type = $matches[1];
+$viewtype = $matches[2];
+$view = $matches[3];
+$ts = $matches[4];
+
+// If is the same ETag, content didn't changed.
+$etag = $ts;
+if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) == "\"$etag\"") {
+ header("HTTP/1.1 304 Not Modified");
+ exit;
+}
+
+switch ($type) {
+ case 'css':
+ header("Content-type: text/css", true);
+ $view = "css/$view";
+ break;
+ case 'js':
+ header('Content-type: text/javascript', true);
+ $view = "js/$view";
+ break;
+}
+
+header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+6 months")), true);
+header("Pragma: public", true);
+header("Cache-Control: public", true);
+header("ETag: \"$etag\"");
+
+$filename = $dataroot . 'views_simplecache/' . md5($viewtype . $view);
+
+if (file_exists($filename)) {
+ readfile($filename);
+} else {
+ // someone trying to access a non-cached file or a race condition with cache flushing
+ mysql_close($mysql_dblink);
+ require_once(dirname(dirname(__FILE__)) . "/start.php");
+
+ global $CONFIG;
+ if (!in_array($view, $CONFIG->views->simplecache)) {
+ header("HTTP/1.1 404 Not Found");
+ exit;
+ }
+
+ elgg_set_viewtype($viewtype);
+ echo elgg_view($view);
+}
diff --git a/engine/handlers/cron_handler.php b/engine/handlers/cron_handler.php
deleted file mode 100644
index a86cf7f4c..000000000
--- a/engine/handlers/cron_handler.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-/**
- * Elgg Cron handler.
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-
-// Load Elgg engine
-require_once("../start.php");
-global $CONFIG;
-
-// Get basic parameters
-$period = get_input('period');
-if (!$period) {
- throw new CronException(sprintf(elgg_echo('CronException:unknownperiod'), $period));
-}
-
-// Get a list of parameters
-$params = array();
-$params['time'] = time();
-
-foreach ($CONFIG->input as $k => $v) {
- $params[$k] = $v;
-}
-
-// Trigger hack
-
-// Data to return to
-$std_out = "";
-$old_stdout = "";
-ob_start();
-
-$old_stdout = trigger_plugin_hook('cron', $period, $params, $old_stdout);
-$std_out = ob_get_clean();
-
-// Return event
-echo $std_out . $old_stdout; \ No newline at end of file
diff --git a/engine/handlers/export_handler.php b/engine/handlers/export_handler.php
new file mode 100644
index 000000000..aa5214c23
--- /dev/null
+++ b/engine/handlers/export_handler.php
@@ -0,0 +1,118 @@
+<?php
+/**
+ * Export handler.
+ *
+ * @package Elgg.Core
+ * @subpackage Export
+ */
+
+require_once(dirname(dirname(__FILE__)) . "/start.php");
+
+
+// Get input values, these will be mapped via modrewrite
+$guid = get_input("guid"); // guid of the entity
+
+// For attributes eg http://example.com/odd/73/attr/owner_uuid/
+// or http://example.com/odd/73/metadata/86/
+$type = get_input("type"); // attr, metadata, annotation, relationship
+$id_or_name = get_input("idname"); // Either a number or the key name (if attribute)
+
+$body = "";
+$title = "";
+
+// Only export the GUID
+if (($guid != "") && ($type == "") && ($id_or_name == "")) {
+ $entity = get_entity($guid);
+
+ if (!$entity) {
+ $query = elgg_echo('InvalidParameterException:GUIDNotFound', array($guid));
+ throw new InvalidParameterException($query);
+ }
+
+ $title = "GUID:$guid";
+ $body = elgg_view("export/entity", array("entity" => $entity, "uuid" => guid_to_uuid($guid)));
+
+ // Export an individual attribute
+} else if (($guid != "") && ($type != "") && ($id_or_name != "")) {
+ // Get a uuid
+ $entity = get_entity($guid);
+ if (!$entity) {
+ $msg = elgg_echo('InvalidParameterException:GUIDNotFound', array($guid));
+ throw new InvalidParameterException($msg);
+ }
+
+ $uuid = guid_to_uuid($entity->getGUID()) . "$type/$id_or_name/";
+
+ switch ($type) {
+ case 'attr' : // @todo: Do this better? - This is a bit of a hack...
+ $v = $entity->get($id_or_name);
+ if (!$v) {
+ $msg = elgg_echo('InvalidParameterException:IdNotExistForGUID', array($id_or_name, $guid));
+ throw new InvalidParameterException($msg);
+ }
+
+ $m = new ElggMetadata();
+
+ $m->value = $v;
+ $m->name = $id_or_name;
+ $m->entity_guid = $guid;
+ $m->time_created = $entity->time_created;
+ $m->time_updated = $entity->time_updated;
+ $m->owner_guid = $entity->owner_guid;
+ $m->id = $id_or_name;
+ $m->type = "attr";
+ break;
+ case 'metadata' :
+ $m = elgg_get_metadata_from_id($id_or_name);
+ break;
+ case 'annotation' :
+ $m = elgg_get_annotation_from_id($id_or_name);
+ break;
+ case 'relationship' :
+ $r = get_relationship($id_or_name);
+ break;
+ case 'volatile' :
+ $m = elgg_trigger_plugin_hook('volatile', 'metadata', array(
+ 'guid' => $guid,
+ 'varname' => $id_or_name,
+ ));
+ break;
+
+ default :
+ $msg = elgg_echo('InvalidParameterException:CanNotExportType', array($type));
+ throw new InvalidParameterException($msg);
+ }
+
+ // Render metadata or relationship
+ if ((!$m) && (!$r)) {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:NoDataFound'));
+ }
+
+ // Exporting metadata?
+ if ($m) {
+ if ($m->entity_guid != $entity->guid) {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:DoesNotBelong'));
+ }
+
+ $title = "$type:$id_or_name";
+ $body = elgg_view("export/metadata", array("metadata" => $m, "uuid" => $uuid));
+ }
+
+ // Exporting relationship
+ if ($r) {
+ if (($r->guid_one != $entity->guid) && ($r->guid_two != $entity->guid)) {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:DoesNotBelongOrRefer'));
+ }
+
+ $title = "$type:$id_or_name";
+ $body = elgg_view("export/relationship", array("relationship" => $r, "uuid" => $uuid));
+ }
+
+ // Something went wrong
+} else {
+ throw new InvalidParameterException(elgg_echo('InvalidParameterException:MissingParameter'));
+}
+
+$content = elgg_view_title($title) . $body;
+$body = elgg_view_layout('one_sidebar', array('content' => $content));
+echo elgg_view_page($title, $body);
diff --git a/engine/handlers/page_handler.php b/engine/handlers/page_handler.php
new file mode 100644
index 000000000..1ed295b7d
--- /dev/null
+++ b/engine/handlers/page_handler.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Pages handler.
+ *
+ * This file dispatches pages. It is called via a URL rewrite in .htaccess
+ * from http://site/handler/page1/page2. The first element after site/ is
+ * the page handler name as registered by {@link elgg_register_page_handler()}.
+ * The rest of the string is sent to {@link page_handler()}.
+ *
+ * Note that the following handler names are reserved by elgg and should not be
+ * registered by any plugins:
+ * * action
+ * * cache
+ * * services
+ * * export
+ * * mt
+ * * xml-rpc.php
+ * * rewrite.php
+ * * tag (deprecated, reserved for backwards compatibility)
+ * * pg (deprecated, reserved for backwards compatibility)
+ *
+ * {@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.
+ *
+ * @package Elgg.Core
+ * @subpackage PageHandler
+ * @link http://docs.elgg.org/Tutorials/PageHandlers
+ */
+
+
+// Permanent redirect to pg-less urls
+$url = $_SERVER['REQUEST_URI'];
+$new_url = preg_replace('#/pg/#', '/', $url, 1);
+
+if ($url !== $new_url) {
+ header("HTTP/1.1 301 Moved Permanently");
+ header("Location: $new_url");
+}
+
+require_once(dirname(dirname(__FILE__)) . "/start.php");
+
+$handler = get_input('handler');
+$page = get_input('page');
+
+if (!page_handler($handler, $page)) {
+ forward('', '404');
+}
diff --git a/engine/handlers/pagehandler.php b/engine/handlers/pagehandler.php
deleted file mode 100644
index 2e98c071a..000000000
--- a/engine/handlers/pagehandler.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-/**
- * Elgg page handler
- *
- * If page_handler() fails, send to front page.
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-
-// Load Elgg engine
-require_once("../start.php");
-
-// Get input
-$handler = get_input('handler');
-$page = get_input('page');
-
-// Call the page handler functions
-if (!page_handler($handler, $page)) {
- forward();
-} \ No newline at end of file
diff --git a/engine/handlers/service_handler.php b/engine/handlers/service_handler.php
index 4cc54a987..9cfcd230f 100644
--- a/engine/handlers/service_handler.php
+++ b/engine/handlers/service_handler.php
@@ -1,19 +1,27 @@
<?php
/**
- * Elgg web services handler.
+ * Services handler.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * This file dispatches requests to web services. It is called via a URL rewrite
+ * in .htaccess from http://site/services/api/handler/response_format/request.
+ * The first element after 'services/api/' is the service handler name as
+ * registered by {@link register_service_handler()}.
+ *
+ * The remaining string is then passed to the {@link service_handler()}
+ * which explodes by /, extracts the first element as the response format
+ * (viewtype), and then passes the remaining array to the service handler
+ * function registered by {@link register_service_handler()}.
+ *
+ * If a service handler isn't found, a 404 header is sent.
+ *
+ * @package Elgg.Core
+ * @subpackage WebServices
+ * @link http://docs.elgg.org/Tutorials/WebServices
*/
+require_once(dirname(dirname(__FILE__)) . "/start.php");
-// Load Elgg engine
-require_once("../start.php");
-
-// Get input
$handler = get_input('handler');
$request = get_input('request');
-service_handler($handler, $request); \ No newline at end of file
+service_handler($handler, $request);
diff --git a/engine/handlers/xml-rpc_handler.php b/engine/handlers/xml-rpc_handler.php
index 67d99a6ac..2ee29e5b7 100644
--- a/engine/handlers/xml-rpc_handler.php
+++ b/engine/handlers/xml-rpc_handler.php
@@ -1,23 +1,23 @@
<?php
/**
- * Elgg XML-RPC handler.
+ * XML-RPC handler.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @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?
*/
-// Load Elgg engine
-require_once("../start.php");
-global $CONFIG;
+require_once(dirname(dirname(__FILE__)) . "/start.php");
// Register the error handler
error_reporting(E_ALL);
-set_error_handler('__php_xmlrpc_error_handler');
+set_error_handler('_php_xmlrpc_error_handler');
// Register a default exception handler
-set_exception_handler('__php_xmlrpc_exception_handler');
+set_exception_handler('_php_xmlrpc_exception_handler');
// Set some defaults
$result = null;
@@ -41,4 +41,4 @@ if (!($result instanceof XMLRPCResponse)) {
}
// Output result
-page_draw("XML-RPC", elgg_view("xml-rpc/output", array('result' => $result))); \ No newline at end of file
+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 b8d8820e1..de0693ea8 100644
--- a/engine/lib/access.php
+++ b/engine/lib/access.php
@@ -1,68 +1,62 @@
<?php
/**
- * Elgg access permissions
- * For users, objects, collections and all metadata
+ * Functions for Elgg's access system for entities, metadata, and annotations.
*
- * @package Elgg
- * @subpackage Core
-
- * @author Curverider Ltd
-
- * @link http://elgg.org/
+ * Access is generally saved in the database as access_id. This corresponds to
+ * one of the ACCESS_* constants defined in {@link elgglib.php} or the ID of an
+ * access collection.
+ *
+ * @package Elgg.Core
+ * @subpackage Access
+ * @link http://docs.elgg.org/Access
*/
/**
- * Temporary class used to determing if access is being ignored
+ * Return an ElggCache static variable cache for the access caches
+ *
+ * @staticvar ElggStaticVariableCache $access_cache
+ * @return \ElggStaticVariableCache
+ * @access private
*/
-class ElggAccess {
+function _elgg_get_access_cache() {
/**
- * Bypass Elgg's access control if true.
- * @var bool
+ * A default filestore cache using the dataroot.
*/
- private $ignore_access;
+ static $access_cache;
- /**
- * Get current ignore access setting.
- * @return bool
- */
- public function get_ignore_access() {
- return $this->ignore_access;
+ if (!$access_cache) {
+ $access_cache = new ElggStaticVariableCache('access');
}
- /**
- * Set ignore access.
- *
- * @param $ignore bool true || false to ignore
- * @return bool Previous setting
- */
- public function set_ignore_access($ignore = true) {
- $prev = $this->ignore_access;
- $this->ignore_access = $ignore;
-
- return $prev;
- }
+ return $access_cache;
}
-
/**
* Return a string of access_ids for $user_id appropriate for inserting into an SQL IN clause.
*
* @uses get_access_array
- * @param int $user_id User ID; defaults to currently logged in user
- * @param int $site_id Site ID; defaults to current site
- * @param boolean $flush If set to true, will refresh the access list from the database
- * @return string A list of access collections suitable for injection in an SQL call
+ *
+ * @link http://docs.elgg.org/Access
+ * @see get_access_array()
+ *
+ * @param int $user_id User ID; defaults to currently logged in user
+ * @param int $site_id Site ID; defaults to current site
+ * @param bool $flush If set to true, will refresh the access list from the
+ * database rather than using this function's cache.
+ *
+ * @return string A list of access collections suitable for using in an SQL call
+ * @access private
*/
function get_access_list($user_id = 0, $site_id = 0, $flush = false) {
- global $CONFIG, $init_finished, $SESSION;
- static $access_list;
-
- if (!isset($access_list) || !$init_finished) {
- $access_list = array();
+ global $CONFIG, $init_finished;
+ $cache = _elgg_get_access_cache();
+
+ if ($flush) {
+ $cache->clear();
}
if ($user_id == 0) {
- $user_id = $SESSION['id'];
+ $user_id = elgg_get_logged_in_user_guid();
}
if (($site_id == 0) && (isset($CONFIG->site_id))) {
@@ -71,36 +65,55 @@ function get_access_list($user_id = 0, $site_id = 0, $flush = false) {
$user_id = (int) $user_id;
$site_id = (int) $site_id;
- if (isset($access_list[$user_id])) {
- return $access_list[$user_id];
- }
+ $hash = $user_id . $site_id . 'get_access_list';
- $access_list[$user_id] = "(" . implode(",", get_access_array($user_id, $site_id, $flush)) . ")";
+ if ($cache[$hash]) {
+ return $cache[$hash];
+ }
+
+ $access_array = get_access_array($user_id, $site_id, $flush);
+ $access = "(" . implode(",", $access_array) . ")";
- return $access_list[$user_id];
+ if ($init_finished) {
+ $cache[$hash] = $access;
+ }
+
+ return $access;
}
/**
- * Gets an array of access restrictions the given user is allowed to see on this site
+ * Returns an array of access IDs a user is permitted to see.
+ *
+ * Can be overridden with the 'access:collections:read', 'user' plugin hook.
+ *
+ * This returns a list of all the collection ids a user owns or belongs
+ * to plus public and logged in access levels. If the user is an admin, it includes
+ * the private access level.
*
- * @param int $user_id User ID; defaults to currently logged in user
- * @param int $site_id Site ID; defaults to current site
- * @param boolean $flush If set to true, will refresh the access list from the database
- * @return array An array of access collections suitable for injection in an SQL call
+ * @internal this is only used in core for creating the SQL where clause when
+ * retrieving content from the database. The friends access level is handled by
+ * get_access_sql_suffix().
+ *
+ * @see get_write_access_array() for the access levels that a user can write to.
+ *
+ * @param int $user_id User ID; defaults to currently logged in user
+ * @param int $site_id Site ID; defaults to current site
+ * @param bool $flush If set to true, will refresh the access ids from the
+ * database rather than using this function's cache.
+ *
+ * @return array An array of access collections ids
*/
function get_access_array($user_id = 0, $site_id = 0, $flush = false) {
global $CONFIG, $init_finished;
- // @todo everything from the db is cached.
- // this cache might be redundant.
- static $access_array;
+ $cache = _elgg_get_access_cache();
- if (!isset($access_array) || (!isset($init_finished)) || (!$init_finished)) {
- $access_array = array();
+ if ($flush) {
+ $cache->clear();
}
if ($user_id == 0) {
- $user_id = get_loggedin_userid();
+ $user_id = elgg_get_logged_in_user_guid();
}
if (($site_id == 0) && (isset($CONFIG->site_guid))) {
@@ -110,34 +123,41 @@ function get_access_array($user_id = 0, $site_id = 0, $flush = false) {
$user_id = (int) $user_id;
$site_id = (int) $site_id;
- if (empty($access_array[$user_id]) || $flush == true) {
- $tmp_access_array = array(ACCESS_PUBLIC);
- if (isloggedin()) {
- $tmp_access_array[] = ACCESS_LOGGED_IN;
+ $hash = $user_id . $site_id . 'get_access_array';
+
+ if ($cache[$hash]) {
+ $access_array = $cache[$hash];
+ } else {
+ $access_array = array(ACCESS_PUBLIC);
- // The following can only return sensible data if the user is logged in.
+ // The following can only return sensible data if the user is logged in.
+ if (elgg_is_logged_in()) {
+ $access_array[] = ACCESS_LOGGED_IN;
// Get ACL memberships
- $query = "SELECT am.access_collection_id FROM {$CONFIG->dbprefix}access_collection_membership am ";
- $query .= " LEFT JOIN {$CONFIG->dbprefix}access_collections ag ON ag.id = am.access_collection_id ";
- $query .= " WHERE am.user_guid = {$user_id} AND (ag.site_guid = {$site_id} OR ag.site_guid = 0)";
-
- if ($collections = get_data($query)) {
- foreach($collections as $collection) {
+ $query = "SELECT am.access_collection_id"
+ . " FROM {$CONFIG->dbprefix}access_collection_membership am"
+ . " LEFT JOIN {$CONFIG->dbprefix}access_collections ag ON ag.id = am.access_collection_id"
+ . " WHERE am.user_guid = $user_id AND (ag.site_guid = $site_id OR ag.site_guid = 0)";
+
+ $collections = get_data($query);
+ if ($collections) {
+ foreach ($collections as $collection) {
if (!empty($collection->access_collection_id)) {
- $tmp_access_array[] = $collection->access_collection_id;
+ $access_array[] = (int)$collection->access_collection_id;
}
}
}
// Get ACLs owned.
- $query = "SELECT ag.id FROM {$CONFIG->dbprefix}access_collections ag ";
- $query .= " WHERE ag.owner_guid = {$user_id} AND (ag.site_guid = {$site_id} OR ag.site_guid = 0)";
+ $query = "SELECT ag.id FROM {$CONFIG->dbprefix}access_collections ag ";
+ $query .= "WHERE ag.owner_guid = $user_id AND (ag.site_guid = $site_id OR ag.site_guid = 0)";
- if ($collections = get_data($query)) {
- foreach($collections as $collection) {
+ $collections = get_data($query);
+ if ($collections) {
+ foreach ($collections as $collection) {
if (!empty($collection->id)) {
- $tmp_access_array[] = $collection->id;
+ $access_array[] = (int)$collection->id;
}
}
}
@@ -145,26 +165,32 @@ function get_access_array($user_id = 0, $site_id = 0, $flush = false) {
$ignore_access = elgg_check_access_overrides($user_id);
if ($ignore_access == true) {
- $tmp_access_array[] = ACCESS_PRIVATE;
+ $access_array[] = ACCESS_PRIVATE;
}
-
- $access_array[$user_id] = $tmp_access_array;
- } else {
- // No user id logged in so we can only access public info
- $tmp_return = $tmp_access_array;
}
- } else {
- $tmp_access_array = $access_array[$user_id];
+ if ($init_finished) {
+ $cache[$hash] = $access_array;
+ }
}
- return trigger_plugin_hook('access:collections:read','user',array('user_id' => $user_id, 'site_id' => $site_id),$tmp_access_array);
+ $options = array(
+ 'user_id' => $user_id,
+ 'site_id' => $site_id
+ );
+
+ return elgg_trigger_plugin_hook('access:collections:read', 'user', $options, $access_array);
}
/**
- * Gets the default access permission for new content
+ * Gets the default access permission.
+ *
+ * This returns the default access level for the site or optionally for the user.
+ *
+ * @param ElggUser $user Get the user's default access. Defaults to logged in user.
*
* @return int default access id (see ACCESS defines in elgglib.php)
+ * @link http://docs.elgg.org/Access
*/
function get_default_access(ElggUser $user = null) {
global $CONFIG;
@@ -173,7 +199,7 @@ function get_default_access(ElggUser $user = null) {
return $CONFIG->default_access;
}
- if (!($user) && (!$user = get_loggedin_user())) {
+ if (!($user) && (!$user = elgg_get_logged_in_user_entity())) {
return $CONFIG->default_access;
}
@@ -185,17 +211,20 @@ function get_default_access(ElggUser $user = null) {
}
/**
- * Override the default behaviour and allow results to show hidden entities as well.
- * THIS IS A HACK.
+ * Allow disabled entities and metadata to be returned by getter functions
*
- * TODO: Replace this with query object!
+ * @todo Replace this with query object!
+ * @global bool $ENTITY_SHOW_HIDDEN_OVERRIDE
+ * @access private
*/
$ENTITY_SHOW_HIDDEN_OVERRIDE = false;
/**
- * This will be replaced. Do not use in plugins!
+ * Show or hide disabled entities.
*
- * @param bool $show
+ * @param bool $show_hidden Show disabled entities.
+ * @return void
+ * @access private
*/
function access_show_hidden_entities($show_hidden) {
global $ENTITY_SHOW_HIDDEN_OVERRIDE;
@@ -203,7 +232,10 @@ function access_show_hidden_entities($show_hidden) {
}
/**
- * This will be replaced. Do not use in plugins!
+ * Return current status of showing disabled entities.
+ *
+ * @return bool
+ * @access private
*/
function access_get_show_hidden_status() {
global $ENTITY_SHOW_HIDDEN_OVERRIDE;
@@ -211,45 +243,17 @@ function access_get_show_hidden_status() {
}
/**
- * Add annotation restriction
- *
- * Returns an SQL fragment that is true (or optionally false) if the given user has
- * added an annotation with the given name to the given entity.
+ * Returns the SQL where clause for a table with a access_id and enabled columns.
*
- * TODO: This is fairly generic so perhaps it could be moved to annotations.php
- *
- * @param string $annotation_name name of the annotation
- * @param string $entity_guid SQL string that evaluates to the GUID of the entity the annotation should be attached to
- * @param string $owner_guid SQL string that evaluates to the GUID of the owner of the annotation *
- * @param boolean $exists If set to true, will return true if the annotation exists, otherwise returns false
- * @return string An SQL fragment suitable for inserting into a WHERE clause
- */
-function get_annotation_sql($annotation_name, $entity_guid, $owner_guid, $exists) {
- global $CONFIG;
-
- if ($exists) {
- $not = '';
- } else {
- $not = 'NOT';
- }
-
- $sql = <<<END
-$not EXISTS (SELECT * FROM {$CONFIG->dbprefix}annotations a
-INNER JOIN {$CONFIG->dbprefix}metastrings ms ON (a.name_id = ms.id)
-WHERE ms.string = '$annotation_name'
-AND a.entity_guid = $entity_guid
-AND a.owner_guid = $owner_guid)
-END;
- return $sql;
-}
-
-/**
- * Add access restriction sql code to a given query.
- * Note that if this code is executed in privileged mode it will return blank.
- * @TODO: DELETE once Query classes are fully integrated
+ * This handles returning where clauses for ACCESS_FRIENDS and the currently
+ * unused block and filter lists in addition to using get_access_list() for
+ * access collections and the standard access levels.
*
* @param string $table_prefix Optional table. prefix for the access code.
- * @param int $owner
+ * @param int $owner The guid to check access for. Defaults to logged in user.
+ *
+ * @return string The SQL for a where clause
+ * @access private
*/
function get_access_sql_suffix($table_prefix = '', $owner = null) {
global $ENTITY_SHOW_HIDDEN_OVERRIDE, $CONFIG;
@@ -259,11 +263,11 @@ function get_access_sql_suffix($table_prefix = '', $owner = null) {
$enemies_bit = "";
if ($table_prefix) {
- $table_prefix = sanitise_string($table_prefix) . ".";
+ $table_prefix = sanitise_string($table_prefix) . ".";
}
if (!isset($owner)) {
- $owner = get_loggedin_userid();
+ $owner = elgg_get_logged_in_user_guid();
}
if (!$owner) {
@@ -276,22 +280,24 @@ function get_access_sql_suffix($table_prefix = '', $owner = null) {
if ($ignore_access) {
$sql = " (1 = 1) ";
} else if ($owner != -1) {
+ // we have an entity's guid and auto check for friend relationships
$friends_bit = "{$table_prefix}access_id = " . ACCESS_FRIENDS . "
AND {$table_prefix}owner_guid IN (
SELECT guid_one FROM {$CONFIG->dbprefix}entity_relationships
WHERE relationship='friend' AND guid_two=$owner
)";
- $friends_bit = '('.$friends_bit.') OR ';
+ $friends_bit = '(' . $friends_bit . ') OR ';
+ // @todo untested and unsupported at present
if ((isset($CONFIG->user_block_and_filter_enabled)) && ($CONFIG->user_block_and_filter_enabled)) {
// check to see if the user is in the entity owner's block list
// or if the entity owner is in the user's filter list
// if so, disallow access
- $enemies_bit = get_annotation_sql('elgg_block_list', "{$table_prefix}owner_guid", $owner, false);
+ $enemies_bit = get_access_restriction_sql('elgg_block_list', "{$table_prefix}owner_guid", $owner, false);
$enemies_bit = '('
. $enemies_bit
- . ' AND ' . get_annotation_sql('elgg_filter_list', $owner, "{$table_prefix}owner_guid", false)
+ . ' AND ' . get_access_restriction_sql('elgg_filter_list', $owner, "{$table_prefix}owner_guid", false)
. ')';
}
}
@@ -310,18 +316,68 @@ function get_access_sql_suffix($table_prefix = '', $owner = null) {
$sql = "$enemies_bit AND ($sql)";
}
- if (!$ENTITY_SHOW_HIDDEN_OVERRIDE)
+ if (!$ENTITY_SHOW_HIDDEN_OVERRIDE) {
$sql .= " and {$table_prefix}enabled='yes'";
- return '('.$sql.')';
+ }
+
+ return '(' . $sql . ')';
+}
+
+/**
+ * Get the where clause for an access restriction based on annotations
+ *
+ * Returns an SQL fragment that is true (or optionally false) if the given user has
+ * added an annotation with the given name to the given entity.
+ *
+ * @warning this is a private function for an untested capability and will likely
+ * be removed from a future version of Elgg.
+ *
+ * @param string $annotation_name Name of the annotation
+ * @param string $entity_guid SQL GUID of entity the annotation is attached to.
+ * @param string $owner_guid SQL string that evaluates to the GUID of the annotation owner
+ * @param boolean $exists If true, returns BOOL if the annotation exists
+ *
+ * @return string An SQL fragment suitable for inserting into a WHERE clause
+ * @access private
+ */
+function get_access_restriction_sql($annotation_name, $entity_guid, $owner_guid, $exists) {
+ global $CONFIG;
+
+ if ($exists) {
+ $not = '';
+ } else {
+ $not = 'NOT';
+ }
+
+ $sql = <<<END
+$not EXISTS (SELECT * FROM {$CONFIG->dbprefix}annotations a
+INNER JOIN {$CONFIG->dbprefix}metastrings ms ON (a.name_id = ms.id)
+WHERE ms.string = '$annotation_name'
+AND a.entity_guid = $entity_guid
+AND a.owner_guid = $owner_guid)
+END;
+ return $sql;
}
/**
- * Determines whether the given user has access to the given entity
+ * Can a user access an entity.
+ *
+ * @warning If a logged in user doesn't have access to an entity, the
+ * core engine will not load that entity.
+ *
+ * @tip This is mostly useful for checking if a user other than the logged in
+ * user has access to an entity that is currently loaded.
+ *
+ * @todo This function would be much more useful if we could pass the guid of the
+ * entity to test access for. We need to be able to tell whether the entity exists
+ * and whether the user has access to the entity.
*
* @param ElggEntity $entity The entity to check access for.
- * @param ElggUser $user Optionally the user to check access for.
+ * @param ElggUser $user Optionally user to check access for. Defaults to
+ * logged in user (which is a useless default).
*
- * @return boolean True if the user can access the entity
+ * @return bool
+ * @link http://docs.elgg.org/Access
*/
function has_access_to_entity($entity, $user = null) {
global $CONFIG;
@@ -333,7 +389,8 @@ function has_access_to_entity($entity, $user = null) {
}
$query = "SELECT guid from {$CONFIG->dbprefix}entities e WHERE e.guid = " . $entity->getGUID();
- $query .= " AND " . $access_bit; // Add access controls
+ // Add access controls
+ $query .= " AND " . $access_bit;
if (get_data($query)) {
return true;
} else {
@@ -342,22 +399,41 @@ function has_access_to_entity($entity, $user = null) {
}
/**
- * Returns an array of access permissions that the specified user is allowed to save objects with.
- * Permissions are of the form ('id' => 'Description')
+ * Returns an array of access permissions that the user is allowed to save content with.
+ * Permissions returned are of the form (id => 'name').
+ *
+ * Example return value in English:
+ * array(
+ * 0 => 'Private',
+ * -2 => 'Friends',
+ * 1 => 'Logged in users',
+ * 2 => 'Public',
+ * 34 => 'My favorite friends',
+ * );
*
- * @param int $user_id The user's GUID.
- * @param int $site_id The current site.
- * @param true|false $flush If this is set to true, this will shun any cached version
+ * Plugin hook of 'access:collections:write', 'user'
+ *
+ * @warning this only returns access collections that the user owns plus the
+ * standard access levels. It does not return access collections that the user
+ * belongs to such as the access collection for a group.
+ *
+ * @param int $user_id The user's GUID.
+ * @param int $site_id The current site.
+ * @param bool $flush If this is set to true, this will ignore a cached access array
*
* @return array List of access permissions
+ * @link http://docs.elgg.org/Access
*/
function get_write_access_array($user_id = 0, $site_id = 0, $flush = false) {
- global $CONFIG;
- //@todo this is probably not needed since caching happens at the DB level.
- static $access_array;
+ global $CONFIG, $init_finished;
+ $cache = _elgg_get_access_cache();
+
+ if ($flush) {
+ $cache->clear();
+ }
if ($user_id == 0) {
- $user_id = get_loggedin_userid();
+ $user_id = elgg_get_logged_in_user_guid();
}
if (($site_id == 0) && (isset($CONFIG->site_id))) {
@@ -367,40 +443,99 @@ function get_write_access_array($user_id = 0, $site_id = 0, $flush = false) {
$user_id = (int) $user_id;
$site_id = (int) $site_id;
- if (empty($access_array[$user_id]) || $flush == true) {
+ $hash = $user_id . $site_id . 'get_write_access_array';
+
+ if ($cache[$hash]) {
+ $access_array = $cache[$hash];
+ } else {
+ // @todo is there such a thing as public write access?
+ $access_array = array(
+ ACCESS_PRIVATE => elgg_echo("PRIVATE"),
+ ACCESS_FRIENDS => elgg_echo("access:friends:label"),
+ ACCESS_LOGGED_IN => elgg_echo("LOGGED_IN"),
+ ACCESS_PUBLIC => elgg_echo("PUBLIC")
+ );
+
$query = "SELECT ag.* FROM {$CONFIG->dbprefix}access_collections ag ";
- $query .= " WHERE (ag.site_guid = {$site_id} OR ag.site_guid = 0)";
- $query .= " AND (ag.owner_guid = {$user_id})";
- $query .= " AND ag.id >= 3";
-
- $tmp_access_array = array( ACCESS_PRIVATE => elgg_echo("PRIVATE"),
- ACCESS_FRIENDS => elgg_echo("access:friends:label"),
- ACCESS_LOGGED_IN => elgg_echo("LOGGED_IN"),
- ACCESS_PUBLIC => elgg_echo("PUBLIC"));
- if ($collections = get_data($query)) {
- foreach($collections as $collection) {
- $tmp_access_array[$collection->id] = $collection->name;
+ $query .= " WHERE (ag.site_guid = $site_id OR ag.site_guid = 0)";
+ $query .= " AND (ag.owner_guid = $user_id)";
+
+ $collections = get_data($query);
+ if ($collections) {
+ foreach ($collections as $collection) {
+ $access_array[$collection->id] = $collection->name;
}
}
- $access_array[$user_id] = $tmp_access_array;
+ if ($init_finished) {
+ $cache[$hash] = $access_array;
+ }
+ }
+
+ $options = array(
+ 'user_id' => $user_id,
+ 'site_id' => $site_id
+ );
+ return elgg_trigger_plugin_hook('access:collections:write', 'user',
+ $options, $access_array);
+}
+
+/**
+ * Can the user change this access collection?
+ *
+ * Use the plugin hook of 'access:collections:write', 'user' to change this.
+ * @see get_write_access_array() for details on the hook.
+ *
+ * Respects access control disabling for admin users and {@see elgg_set_ignore_access()}
+ *
+ * @see get_write_access_array()
+ *
+ * @param int $collection_id The collection id
+ * @param mixed $user_guid The user GUID to check for. Defaults to logged in user.
+ * @return bool
+ */
+function can_edit_access_collection($collection_id, $user_guid = null) {
+ if ($user_guid) {
+ $user = get_entity((int) $user_guid);
} else {
- $tmp_access_array = $access_array[$user_id];
+ $user = elgg_get_logged_in_user_entity();
+ }
+
+ $collection = get_access_collection($collection_id);
+
+ if (!($user instanceof ElggUser) || !$collection) {
+ return false;
}
- $tmp_access_array = trigger_plugin_hook('access:collections:write','user',array('user_id' => $user_id, 'site_id' => $site_id),$tmp_access_array);
+ $write_access = get_write_access_array($user->getGUID(), 0, true);
- return $tmp_access_array;
+ // don't ignore access when checking users.
+ if ($user_guid) {
+ return array_key_exists($collection_id, $write_access);
+ } else {
+ return elgg_get_ignore_access() || array_key_exists($collection_id, $write_access);
+ }
}
/**
- * Creates a new access control collection owned by the specified user.
+ * Creates a new access collection.
+ *
+ * Access colletions allow plugins and users to create granular access
+ * for entities.
+ *
+ * Triggers plugin hook 'access:collections:addcollection', 'collection'
+ *
+ * @internal Access collections are stored in the access_collections table.
+ * Memberships to collections are in access_collections_membership.
*
- * @param string $name The name of the collection.
- * @param int $owner_guid The GUID of the owner (default: currently logged in user).
- * @param int $site_guid The GUID of the site (default: current site).
+ * @param string $name The name of the collection.
+ * @param int $owner_guid The GUID of the owner (default: currently logged in user).
+ * @param int $site_guid The GUID of the site (default: current site).
*
- * @return int|false Depending on success (the collection ID if successful).
+ * @return int|false The collection ID if successful and false on failure.
+ * @link http://docs.elgg.org/Access/Collections
+ * @see update_access_collection()
+ * @see delete_access_collection()
*/
function create_access_collection($name, $owner_guid = 0, $site_guid = 0) {
global $CONFIG;
@@ -411,7 +546,7 @@ function create_access_collection($name, $owner_guid = 0, $site_guid = 0) {
}
if ($owner_guid == 0) {
- $owner_guid = get_loggedin_userid();
+ $owner_guid = elgg_get_logged_in_user_guid();
}
if (($site_guid == 0) && (isset($CONFIG->site_guid))) {
$site_guid = $CONFIG->site_guid;
@@ -422,7 +557,8 @@ function create_access_collection($name, $owner_guid = 0, $site_guid = 0) {
SET name = '{$name}',
owner_guid = {$owner_guid},
site_guid = {$site_guid}";
- if (!$id = insert_data($q)) {
+ $id = insert_data($q);
+ if (!$id) {
return false;
}
@@ -430,7 +566,7 @@ function create_access_collection($name, $owner_guid = 0, $site_guid = 0) {
'collection_id' => $id
);
- if (!trigger_plugin_hook('access:collections:addcollection', 'collection', $params, true)) {
+ if (!elgg_trigger_plugin_hook('access:collections:addcollection', 'collection', $params, true)) {
return false;
}
@@ -440,168 +576,198 @@ function create_access_collection($name, $owner_guid = 0, $site_guid = 0) {
/**
* Updates the membership in an access collection.
*
- * @param int $collection_id The ID of the collection.
- * @param array $members Array of member GUIDs
- * @return true|false Depending on success
+ * @warning Expects a full list of all members that should
+ * be part of the access collection
+ *
+ * @note This will run all hooks associated with adding or removing
+ * members to access collections.
+ *
+ * @param int $collection_id The ID of the collection.
+ * @param array $members Array of member GUIDs
+ *
+ * @return bool
+ * @link http://docs.elgg.org/Access/Collections
+ * @see add_user_to_access_collection()
+ * @see remove_user_from_access_collection()
*/
function update_access_collection($collection_id, $members) {
- global $CONFIG;
+ $acl = get_access_collection($collection_id);
- $collection_id = (int) $collection_id;
+ if (!$acl) {
+ return false;
+ }
$members = (is_array($members)) ? $members : array();
- $collections = get_write_access_array();
+ $cur_members = get_members_of_access_collection($collection_id, true);
+ $cur_members = (is_array($cur_members)) ? $cur_members : array();
- if (array_key_exists($collection_id, $collections)) {
- $cur_members = get_members_of_access_collection($collection_id, true);
- $cur_members = (is_array($cur_members)) ? $cur_members : array();
+ $remove_members = array_diff($cur_members, $members);
+ $add_members = array_diff($members, $cur_members);
- $remove_members = array_diff($cur_members, $members);
- $add_members = array_diff($members, $cur_members);
+ $result = true;
- $params = array(
- 'collection_id' => $collection_id,
- 'members' => $members,
- 'add_members' => $add_members,
- 'remove_members' => $remove_members
- );
-
- foreach ($add_members as $guid) {
- add_user_to_access_collection($guid, $collection_id);
- }
-
- foreach ($remove_members as $guid) {
- remove_user_from_access_collection($guid, $collection_id);
- }
+ foreach ($add_members as $guid) {
+ $result = $result && add_user_to_access_collection($guid, $collection_id);
+ }
- return true;
+ foreach ($remove_members as $guid) {
+ $result = $result && remove_user_from_access_collection($guid, $collection_id);
}
- return false;
+ return $result;
}
/**
- * Deletes a specified access collection
+ * Deletes a specified access collection and its membership.
*
* @param int $collection_id The collection ID
- * @return true|false Depending on success
+ *
+ * @return bool
+ * @link http://docs.elgg.org/Access/Collections
+ * @see create_access_collection()
+ * @see update_access_collection()
*/
function delete_access_collection($collection_id) {
+ global $CONFIG;
$collection_id = (int) $collection_id;
- $collections = get_write_access_array(null, null, TRUE);
$params = array('collection_id' => $collection_id);
- if (!trigger_plugin_hook('access:collections:deletecollection', 'collection', $params, true)) {
+ if (!elgg_trigger_plugin_hook('access:collections:deletecollection', 'collection', $params, true)) {
return false;
}
- if (array_key_exists($collection_id, $collections)) {
- global $CONFIG;
- delete_data("delete from {$CONFIG->dbprefix}access_collection_membership where access_collection_id = {$collection_id}");
- delete_data("delete from {$CONFIG->dbprefix}access_collections where id = {$collection_id}");
- return true;
- } else {
- return false;
- }
+ // Deleting membership doesn't affect result of deleting ACL.
+ $q = "DELETE FROM {$CONFIG->dbprefix}access_collection_membership
+ WHERE access_collection_id = {$collection_id}";
+ delete_data($q);
+
+ $q = "DELETE FROM {$CONFIG->dbprefix}access_collections
+ WHERE id = {$collection_id}";
+ $result = delete_data($q);
+ return (bool)$result;
}
/**
* Get a specified access collection
*
+ * @note This doesn't return the members of an access collection,
+ * just the database row of the actual collection.
+ *
+ * @see get_members_of_access_collection()
+ *
* @param int $collection_id The collection ID
- * @return array|false Depending on success
+ *
+ * @return object|false
*/
function get_access_collection($collection_id) {
global $CONFIG;
$collection_id = (int) $collection_id;
- $get_collection = get_data_row("SELECT * FROM {$CONFIG->dbprefix}access_collections WHERE id = {$collection_id}");
+ $query = "SELECT * FROM {$CONFIG->dbprefix}access_collections WHERE id = {$collection_id}";
+ $get_collection = get_data_row($query);
return $get_collection;
}
/**
- * Adds a user to the specified user collection
+ * Adds a user to an access collection.
+ *
+ * Triggers the 'access:collections:add_user', 'collection' plugin hook.
*
- * @param int $user_guid The GUID of the user to add
+ * @param int $user_guid The GUID of the user to add
* @param int $collection_id The ID of the collection to add them to
- * @return true|false Depending on success
+ *
+ * @return bool
+ * @see update_access_collection()
+ * @see remove_user_from_access_collection()
+ * @link http://docs.elgg.org/Access/Collections
*/
function add_user_to_access_collection($user_guid, $collection_id) {
+ global $CONFIG;
+
$collection_id = (int) $collection_id;
$user_guid = (int) $user_guid;
- $collections = get_write_access_array();
-
- if (!($collection = get_access_collection($collection_id)))
- return false;
+ $user = get_user($user_guid);
- if ((array_key_exists($collection_id, $collections) || $collection->owner_guid == 0)
- && $user = get_user($user_guid)) {
- global $CONFIG;
+ $collection = get_access_collection($collection_id);
- $params = array(
- 'collection_id' => $collection_id,
- 'user_guid' => $user_guid
- );
-
- if (!trigger_plugin_hook('access:collections:add_user', 'collection', $params, true)) {
- return false;
- }
+ if (!($user instanceof Elgguser) || !$collection) {
+ return false;
+ }
- try {
- insert_data("insert into {$CONFIG->dbprefix}access_collection_membership set access_collection_id = {$collection_id}, user_guid = {$user_guid}");
- } catch (DatabaseException $e) {
- // nothing.
- }
- return true;
+ $params = array(
+ 'collection_id' => $collection_id,
+ 'user_guid' => $user_guid
+ );
+ $result = elgg_trigger_plugin_hook('access:collections:add_user', 'collection', $params, true);
+ if ($result == false) {
+ return false;
}
- 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
+ ON DUPLICATE KEY UPDATE user_guid = user_guid";
+ $result = insert_data($q);
+
+ return $result !== false;
}
/**
- * Removes a user from an access collection
+ * Removes a user from an access collection.
+ *
+ * Triggers the 'access:collections:remove_user', 'collection' plugin hook.
*
- * @param int $user_guid The user GUID
+ * @param int $user_guid The user GUID
* @param int $collection_id The access collection ID
- * @return true|false Depending on success
+ *
+ * @return bool
+ * @see update_access_collection()
+ * @see remove_user_from_access_collection()
+ * @link http://docs.elgg.org/Access/Collections
*/
function remove_user_from_access_collection($user_guid, $collection_id) {
+ global $CONFIG;
+
$collection_id = (int) $collection_id;
$user_guid = (int) $user_guid;
- $collections = get_write_access_array();
+ $user = get_user($user_guid);
- if (!($collection = get_access_collection($collection_id)))
- return false;
-
- if ((array_key_exists($collection_id, $collections) || $collection->owner_guid == 0) && $user = get_user($user_guid)) {
- global $CONFIG;
- $params = array(
- 'collection_id' => $collection_id,
- 'user_guid' => $user_guid
- );
+ $collection = get_access_collection($collection_id);
- if (!trigger_plugin_hook('access:collections:remove_user', 'collection', $params, true)) {
- return false;
- }
+ if (!($user instanceof Elgguser) || !$collection) {
+ return false;
+ }
- delete_data("delete from {$CONFIG->dbprefix}access_collection_membership where access_collection_id = {$collection_id} and user_guid = {$user_guid}");
- return true;
+ $params = array(
+ 'collection_id' => $collection_id,
+ 'user_guid' => $user_guid
+ );
+ if (!elgg_trigger_plugin_hook('access:collections:remove_user', 'collection', $params, true)) {
+ return false;
}
- return false;
+ $q = "DELETE FROM {$CONFIG->dbprefix}access_collection_membership
+ WHERE access_collection_id = {$collection_id}
+ AND user_guid = {$user_guid}";
+
+ return (bool)delete_data($q);
}
/**
- * Get all of a users collections
+ * Returns an array of database row objects of the access collections owned by $owner_guid.
+ *
+ * @param int $owner_guid The entity guid
+ * @param int $site_guid The GUID of the site (default: current site).
*
- * @param int $owner_guid The user ID
- * @param int $site_guid The GUID of the site (default: current site).
- * @return true|false Depending on success
+ * @return array|false
+ * @see add_access_collection()
+ * @see get_members_of_access_collection()
+ * @link http://docs.elgg.org/Access/Collections
*/
function get_user_access_collections($owner_guid, $site_guid = 0) {
global $CONFIG;
@@ -622,26 +788,33 @@ function get_user_access_collections($owner_guid, $site_guid = 0) {
}
/**
- * Get all of members of a friend collection
+ * Get all of members of an access collection
*
- * @param int $collection The collection's ID
- * @param true|false $idonly If set to true, will only return the members' IDs (default: false)
- * @return ElggUser entities if successful, false if not
+ * @param int $collection The collection's ID
+ * @param bool $idonly If set to true, will only return the members' GUIDs (default: false)
+ *
+ * @return array ElggUser guids or entities if successful, false if not
+ * @see add_user_to_access_collection()
+ * @see http://docs.elgg.org/Access/Collections
*/
function get_members_of_access_collection($collection, $idonly = FALSE) {
global $CONFIG;
$collection = (int)$collection;
if (!$idonly) {
- $query = "SELECT e.* FROM {$CONFIG->dbprefix}access_collection_membership m JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.user_guid WHERE m.access_collection_id = {$collection}";
+ $query = "SELECT e.* FROM {$CONFIG->dbprefix}access_collection_membership m"
+ . " JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.user_guid"
+ . " WHERE m.access_collection_id = {$collection}";
$collection_members = get_data($query, "entity_row_to_elggstar");
} else {
- $query = "SELECT e.guid FROM {$CONFIG->dbprefix}access_collection_membership m JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.user_guid WHERE m.access_collection_id = {$collection}";
+ $query = "SELECT e.guid FROM {$CONFIG->dbprefix}access_collection_membership m"
+ . " JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.user_guid"
+ . " WHERE m.access_collection_id = {$collection}";
$collection_members = get_data($query);
if (!$collection_members) {
return FALSE;
}
- foreach($collection_members as $key => $val) {
+ foreach ($collection_members as $key => $val) {
$collection_members[$key] = $val->guid;
}
}
@@ -650,93 +823,16 @@ function get_members_of_access_collection($collection, $idonly = FALSE) {
}
/**
- * Displays a user's access collections, using the friends/collections view
+ * Return entities based upon access id.
*
- * @param int $owner_guid The GUID of the owning user
- * @return string A formatted rendition of the collections
- */
-function elgg_view_access_collections($owner_guid) {
- if ($collections = get_user_access_collections($owner_guid)) {
- foreach($collections as $key => $collection) {
- $collections[$key]->members = get_members_of_access_collection($collection->id, true);
- $collections[$key]->entities = get_user_friends($owner_guid,"",9999);
- }
- }
-
- return elgg_view('friends/collections',array('collections' => $collections));
-}
-
-/**
- * Get entities with the specified access collection id.
+ * @param array $options Any options accepted by {@link elgg_get_entities()} and
+ * access_id => int The access ID of the entity.
*
- * @deprecated 1.7. Use elgg_get_entities_from_access_id()
- *
- * @param $collection_id
- * @param $entity_type
- * @param $entity_subtype
- * @param $owner_guid
- * @param $limit
- * @param $offset
- * @param $order_by
- * @param $site_guid
- * @param $count
- * @return unknown_type
- */
-function get_entities_from_access_id($collection_id, $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) {
- // log deprecated warning
- elgg_deprecated_notice('get_entities_from_access_id() was deprecated by elgg_get_entities()!', 1.7);
-
- if (!$collection_id) {
- return FALSE;
- }
-
- // build the options using given parameters
- $options = array();
- $options['limit'] = $limit;
- $options['offset'] = $offset;
- $options['count'] = $count;
-
- if ($entity_type) {
- $options['type'] = sanitise_string($entity_type);
- }
-
- if ($entity_subtype) {
- $options['subtype'] = $entity_subtype;
- }
-
- if ($site_guid) {
- $options['site_guid'] = $site_guid;
- }
-
- if ($order_by) {
- $options['order_by'] = sanitise_string("e.time_created, $order_by");
- }
-
- if ((is_array($owner_guid) && (count($owner_guid)))) {
- $options['owner_guids'] = array();
- foreach($owner_guid as $guid) {
- $options['owner_guids'][] = $guid;
- }
- }
-
- if ($site_guid) {
- $options['site_guid'] = $site_guid;
- }
-
- $options['access_id'] = $collection_id;
-
- return elgg_get_entities_from_access_id($options);
-}
-
-/**
- * Retrieve entities for a given access collection
- *
- * @param int $collection_id
- * @param array $options @see elgg_get_entities()
- * @return array
- * @since 1.7
+ * @see elgg_get_entities()
+ * @return mixed If count, int. If not count, array. false on errors.
+ * @since 1.7.0
*/
-function elgg_get_entities_from_access_id(array $options=array()) {
+function elgg_get_entities_from_access_id(array $options = array()) {
// restrict the resultset to access collection provided
if (!isset($options['access_id'])) {
return FALSE;
@@ -761,70 +857,101 @@ function elgg_get_entities_from_access_id(array $options=array()) {
/**
* Lists entities from an access collection
*
- * @param $collection_id
- * @param $entity_type
- * @param $entity_subtype
- * @param $owner_guid
- * @param $limit
- * @param $fullview
- * @param $viewtypetoggle
- * @param $pagination
- * @return str
- */
-function list_entities_from_access_id($collection_id, $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = true, $pagination = true) {
- $offset = (int) get_input('offset');
- $limit = (int) $limit;
- $count = get_entities_from_access_id($collection_id, $entity_type, $entity_subtype, $owner_guid, $limit, $offset, "", 0, true);
- $entities = get_entities_from_access_id($collection_id, $entity_type, $entity_subtype, $owner_guid, $limit, $offset, "", 0, false);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
+ * @param array $options See elgg_list_entities() and elgg_get_entities_from_access_id()
+ *
+ * @see elgg_list_entities()
+ * @see elgg_get_entities_from_access_id()
+ *
+ * @return string
+ */
+function elgg_list_entities_from_access_id(array $options = array()) {
+ return elgg_list_entities($options, 'elgg_get_entities_from_access_id');
}
/**
- * Return a humanreadable version of an entity's access level
+ * Return the name of an ACCESS_* constant or a access collection,
+ * but only if the user has write access on that ACL.
+ *
+ * @warning This function probably doesn't work how it's meant to.
*
- * @param $entity_accessid (int) The entity's access id
- * @return string e.g. Public, Private etc
- **/
-function get_readable_access_level($entity_accessid){
- $access = (int) $entity_accessid;
+ * @param int $entity_access_id The entity's access id
+ *
+ * @return string 'Public', 'Private', etc.
+ * @since 1.7.0
+ * @todo I think this probably wants get_access_array() instead of get_write_access_array(),
+ * but those two functions return different types of arrays.
+ */
+function get_readable_access_level($entity_access_id) {
+ $access = (int) $entity_access_id;
+
//get the access level for object in readable string
$options = get_write_access_array();
- foreach($options as $key => $option) {
- if($key == $access){
- $entity_acl = htmlentities($option, ENT_QUOTES, 'UTF-8');
- return $entity_acl;
- break;
- }
+
+ if (array_key_exists($access, $options)) {
+ return $options[$access];
}
- return false;
+
+ // return 'Limited' if the user does not have access to the access collection
+ return elgg_echo('access:limited:label');
}
/**
* Set if entity access system should be ignored.
*
+ * The access system will not return entities in any getter
+ * functions if the user doesn't have access.
+ *
+ * @internal For performance reasons this is done at the database access clause level.
+ *
+ * @tip Use this to access entities in automated scripts
+ * when no user is logged in.
+ *
+ * @note This clears the access cache.
+ *
+ * @warning This will not show disabled entities.
+ * Use {@link access_show_hidden_entities()} to access disabled entities.
+ *
+ * @param bool $ignore If true, disables all access checks.
+ *
* @return bool Previous ignore_access setting.
+ * @since 1.7.0
+ * @see http://docs.elgg.org/Access/IgnoreAccess
+ * @see elgg_get_ignore_access()
*/
function elgg_set_ignore_access($ignore = true) {
+ $cache = _elgg_get_access_cache();
+ $cache->clear();
$elgg_access = elgg_get_access_object();
- return $elgg_access->set_ignore_access($ignore);
+ return $elgg_access->setIgnoreAccess($ignore);
}
/**
* Get current ignore access setting.
*
* @return bool
+ * @since 1.7.0
+ * @see http://docs.elgg.org/Access/IgnoreAccess
+ * @see elgg_set_ignore_access()
*/
function elgg_get_ignore_access() {
- return elgg_get_access_object()->get_ignore_access();
+ return elgg_get_access_object()->getIgnoreAccess();
}
/**
- * Decides if the access system is being ignored.
+ * Decides if the access system should be ignored for a user.
+ *
+ * Returns true (meaning ignore access) if either of these 2 conditions are true:
+ * 1) an admin user guid is passed to this function.
+ * 2) {@link elgg_get_ignore_access()} returns true.
+ *
+ * @see elgg_set_ignore_access()
+ *
+ * @param int $user_guid The user to check against.
*
* @return bool
+ * @since 1.7.0
*/
-function elgg_check_access_overrides($user_guid = null) {
+function elgg_check_access_overrides($user_guid = 0) {
if (!$user_guid || $user_guid <= 0) {
$is_admin = false;
} else {
@@ -837,7 +964,12 @@ function elgg_check_access_overrides($user_guid = null) {
/**
* Returns the ElggAccess object.
*
+ * // @todo comment is incomplete
+ * This is used to
+ *
* @return ElggAccess
+ * @since 1.7.0
+ * @access private
*/
function elgg_get_access_object() {
static $elgg_access;
@@ -849,12 +981,23 @@ function elgg_get_access_object() {
return $elgg_access;
}
-global $init_finished;
+/**
+ * A flag to set if Elgg's access initialization is finished.
+ *
+ * @global bool $init_finished
+ * @access private
+ * @todo This is required to tell the access system to start caching because
+ * calls are made while in ignore access mode and before the user is logged in.
+ */
$init_finished = false;
/**
* A quick and dirty way to make sure the access permissions have been correctly set up
*
+ * @elgg_event_handler init system
+ * @todo Invesigate
+ *
+ * @return void
*/
function access_init() {
global $init_finished;
@@ -862,12 +1005,35 @@ function access_init() {
}
/**
- * Override permissions system
+ * Overrides the access system if appropriate.
*
+ * Allows admin users and calls after {@link elgg_set_ignore_access} to
+ * bypass the access system.
+ *
+ * Registered for the 'permissions_check', 'all' and the
+ * 'container_permissions_check', 'all' plugin hooks.
+ *
+ * Returns true to override the access system or null if no change is needed.
+ *
+ * @param string $hook
+ * @param string $type
+ * @param bool $value
+ * @param array $params
* @return true|null
+ * @access private
*/
-function elgg_override_permissions_hook($hook, $type, $returnval, $params) {
- $user_guid = get_loggedin_userid();
+function elgg_override_permissions($hook, $type, $value, $params) {
+ $user = elgg_extract('user', $params);
+ if ($user) {
+ $user_guid = $user->getGUID();
+ } else {
+ $user_guid = elgg_get_logged_in_user_guid();
+ }
+
+ // don't do this so ignore access still works with no one logged in
+ //if (!$user instanceof ElggUser) {
+ // return false;
+ //}
// check for admin
if ($user_guid && elgg_is_admin_user($user_guid)) {
@@ -883,9 +1049,30 @@ function elgg_override_permissions_hook($hook, $type, $returnval, $params) {
return NULL;
}
-// This function will let us know when 'init' has finished
-register_elgg_event_handler('init', 'system', 'access_init', 9999);
+/**
+ * Runs unit tests for the entities object.
+ *
+ * @param string $hook
+ * @param string $type
+ * @param array $value
+ * @param array $params
+ * @return array
+ *
+ * @access private
+ */
+function access_test($hook, $type, $value, $params) {
+ global $CONFIG;
+
+ $value[] = $CONFIG->path . 'engine/tests/api/access_collections.php';
+ return $value;
+}
+
+// Tell the access functions the system has booted, plugins are loaded,
+// and the user is logged in so it can start caching
+elgg_register_event_handler('ready', 'system', 'access_init');
// For overrided permissions
-register_plugin_hook('permissions_check', 'all', 'elgg_override_permissions_hook');
-register_plugin_hook('container_permissions_check', 'all', 'elgg_override_permissions_hook');
+elgg_register_plugin_hook_handler('permissions_check', 'all', 'elgg_override_permissions');
+elgg_register_plugin_hook_handler('container_permissions_check', 'all', 'elgg_override_permissions');
+
+elgg_register_plugin_hook_handler('unit_test', 'system', 'access_test'); \ No newline at end of file
diff --git a/engine/lib/actions.php b/engine/lib/actions.php
index 57da1389f..8047914ac 100644
--- a/engine/lib/actions.php
+++ b/engine/lib/actions.php
@@ -1,95 +1,153 @@
<?php
/**
- * Elgg actions
- * Allows system modules to specify actions
+ * Elgg Actions
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * Actions are one of the primary controllers (The C in MVC) in Elgg. They are
+ * registered by {@link register_elgg_action()} and are called by URL
+ * http://elggsite.org/action/action_name. For URLs, a rewrite rule in
+ * .htaccess passes the action name to engine/handlers/action_handler.php,
+ * which dispatches the request for the action.
+ *
+ * An action name must be registered to a file in the system. Core actions are
+ * found in /actions/ and plugin actions are usually under /mod/<plugin>/actions/.
+ * It is recommended that actions be namespaced to avoid collisions.
+ *
+ * All actions require security tokens. Using the {@elgg_view input/form} view
+ * will automatically add tokens as hidden inputs as will the elgg_view_form()
+ * function. To manually add hidden inputs, use the {@elgg_view input/securitytoken} view.
+ *
+ * To include security tokens for actions called via GET, use
+ * {@link elgg_add_security_tokens_to_url()} or specify is_action as true when
+ * using {@lgg_view output/url}.
+ *
+ * Action tokens can be manually generated by using {@link generate_action_token()}.
+ *
+ * @tip When registered, actions can be restricted to logged in or admin users.
+ *
+ * @tip Action URLs should be called with a trailing / to prevent 301 redirects.
+ *
+ * @package Elgg.Core
+ * @subpackage Actions
+ * @link http://docs.elgg.org/Actions
+ * @link http://docs.elgg.org/Actions/Tokens
*/
-// Action setting and run *************************************************
-
/**
-* Loads an action script, if it exists, then forwards elsewhere
-*
-* @param string $action The requested action
-* @param string $forwarder Optionally, the location to forward to
-*/
-
+ * Perform an action.
+ *
+ * This function executes the action with name $action as registered
+ * by {@link elgg_register_action()}.
+ *
+ * The plugin hook 'action', $action_name will be triggered before the action
+ * is executed. If a handler returns false, it will prevent the action script
+ * from being called.
+ *
+ * @note If an action isn't registered in the system or is registered
+ * to an unavailable file the user will be forwarded to the site front
+ * page and an error will be emitted via {@link register_error()}.
+ *
+ * @warning All actions require {@link http://docs.elgg.org/Actions/Tokens Action Tokens}.
+ *
+ * @param string $action The requested action
+ * @param string $forwarder Optionally, the location to forward to
+ *
+ * @link http://docs.elgg.org/Actions
+ * @see elgg_register_action()
+ *
+ * @return void
+ * @access private
+ */
function action($action, $forwarder = "") {
global $CONFIG;
+ $action = rtrim($action, '/');
+
// @todo REMOVE THESE ONCE #1509 IS IN PLACE.
// Allow users to disable plugins without a token in order to
- // remove plugins that are imcompatible.
- // Installation cannot use tokens because it requires site secret to be
- // working. (#1462)
- // Login and logout are for convenience.
+ // remove plugins that are incompatible.
+ // Logout for convenience.
// file/download (see #2010)
$exceptions = array(
- 'systemsettings/install',
'admin/plugins/disable',
'logout',
- 'login',
'file/download',
);
if (!in_array($action, $exceptions)) {
- // All actions require a token.
- action_gatekeeper();
+ action_gatekeeper($action);
}
- $forwarder = str_replace($CONFIG->url, "", $forwarder);
+ $forwarder = str_replace(elgg_get_site_url(), "", $forwarder);
$forwarder = str_replace("http://", "", $forwarder);
$forwarder = str_replace("@", "", $forwarder);
-
- if (substr($forwarder,0,1) == "/") {
- $forwarder = substr($forwarder,1);
+ if (substr($forwarder, 0, 1) == "/") {
+ $forwarder = substr($forwarder, 1);
}
- if (isset($CONFIG->actions[$action])) {
- if ((isadminloggedin()) || (!$CONFIG->actions[$action]['admin'])) {
- if ($CONFIG->actions[$action]['public'] || get_loggedin_userid()) {
-
- // Trigger action event TODO: This is only called before the primary action is called. We need to rethink actions for 1.5
- $event_result = true;
- $event_result = trigger_plugin_hook('action', $action, null, $event_result);
-
- // Include action
- // Event_result being false doesn't produce an error -
- // since i assume this will be handled in the hook itself.
- // TODO make this better!
- if ($event_result) {
- if (!include($CONFIG->actions[$action]['file'])) {
- register_error(sprintf(elgg_echo('actionundefined'),$action));
- }
- }
- } else {
- register_error(elgg_echo('actionloggedout'));
+ if (!isset($CONFIG->actions[$action])) {
+ register_error(elgg_echo('actionundefined', array($action)));
+ } elseif (!elgg_is_admin_logged_in() && ($CONFIG->actions[$action]['access'] === 'admin')) {
+ register_error(elgg_echo('actionunauthorized'));
+ } elseif (!elgg_is_logged_in() && ($CONFIG->actions[$action]['access'] !== 'public')) {
+ register_error(elgg_echo('actionloggedout'));
+ } else {
+ // Returning falsy doesn't produce an error
+ // We assume this will be handled in the hook itself.
+ if (elgg_trigger_plugin_hook('action', $action, null, true)) {
+ if (!include($CONFIG->actions[$action]['file'])) {
+ register_error(elgg_echo('actionnotfound', array($action)));
}
- } else {
- register_error(elgg_echo('actionunauthorized'));
}
- } else {
- register_error(sprintf(elgg_echo('actionundefined'),$action));
}
- forward($CONFIG->url . $forwarder);
+ $forwarder = empty($forwarder) ? REFERER : $forwarder;
+ forward($forwarder);
}
/**
- * Registers a particular action in memory
+ * Registers an action.
+ *
+ * Actions are registered to a script in the system and are executed
+ * either by the URL http://elggsite.org/action/action_name/.
+ *
+ * $filename must be the full path of the file to register, or a path relative
+ * to the core actions/ dir.
+ *
+ * Actions should be namedspaced for your plugin. Example:
+ * <code>
+ * elgg_register_action('myplugin/save_settings', ...);
+ * </code>
+ *
+ * @tip Put action files under the actions/<plugin_name> directory of your plugin.
+ *
+ * @tip You don't need to include engine/start.php in your action files.
+ *
+ * @internal Actions are saved in $CONFIG->actions as an array in the form:
+ * <code>
+ * array(
+ * 'file' => '/location/to/file.php',
+ * 'access' => 'public', 'logged_in', or 'admin'
+ * )
+ * </code>
*
- * @param string $action The name of the action (eg "register", "account/settings/save")
- * @param boolean $public Can this action be accessed by people not logged into the system?
- * @param string $filename Optionally, the filename where this action is located
- * @param boolean $admin_only Whether this action is only available to admin users.
+ * @param string $action The name of the action (eg "register", "account/settings/save")
+ * @param string $filename Optionally, the filename where this action is located. If not specified,
+ * will assume the action is in elgg/actions/<action>.php
+ * @param string $access Who is allowed to execute this action: public, logged_in, admin.
+ * (default: logged_in)
+ *
+ * @see action()
+ * @see http://docs.elgg.org/Actions
+ *
+ * @return bool
*/
-function register_action($action, $public = false, $filename = "", $admin_only = false) {
+function elgg_register_action($action, $filename = "", $access = 'logged_in') {
global $CONFIG;
+ // plugins are encouraged to call actions with a trailing / to prevent 301
+ // redirects but we store the actions without it
+ $action = rtrim($action, '/');
+
if (!isset($CONFIG->actions)) {
$CONFIG->actions = array();
}
@@ -103,48 +161,92 @@ function register_action($action, $public = false, $filename = "", $admin_only =
$filename = $path . "actions/" . $action . ".php";
}
- $CONFIG->actions[$action] = array('file' => $filename, 'public' => $public, 'admin' => $admin_only);
+ $CONFIG->actions[$action] = array(
+ 'file' => $filename,
+ 'access' => $access,
+ );
return true;
}
/**
- * Actions to perform on initialisation
+ * Unregisters an action
*
- * @param string $event Events API required parameters
- * @param string $object_type Events API required parameters
- * @param string $object Events API required parameters
+ * @param string $action Action name
+ * @return bool
+ * @since 1.8.1
*/
-function actions_init($event, $object_type, $object) {
- register_action("error");
- return true;
+function elgg_unregister_action($action) {
+ global $CONFIG;
+
+ if (isset($CONFIG->actions[$action])) {
+ unset($CONFIG->actions[$action]);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Is the token timestamp within acceptable range?
+ *
+ * @param int $ts timestamp from the CSRF token
+ *
+ * @return bool
+ */
+function _elgg_validate_token_timestamp($ts) {
+ $action_token_timeout = elgg_get_config('action_token_timeout');
+ // default is 2 hours
+ $timeout = ($action_token_timeout !== null) ? $action_token_timeout : 2;
+
+ $hour = 60 * 60;
+ $timeout = $timeout * $hour;
+ $now = time();
+
+ // Validate time to ensure its not crazy
+ return ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout));
}
/**
- * Validate an action token, returning true if valid and false if not
+ * Validate an action token.
*
- * @return unknown
+ * Calls to actions will automatically validate tokens. If tokens are not
+ * present or invalid, the action will be denied and the user will be redirected.
+ *
+ * Plugin authors should never have to manually validate action tokens.
+ *
+ * @param bool $visibleerrors Emit {@link register_error()} errors on failure?
+ * @param mixed $token The token to test against. Default: $_REQUEST['__elgg_token']
+ * @param mixed $ts The time stamp to test against. Default: $_REQUEST['__elgg_ts']
+ *
+ * @return bool
+ * @see generate_action_token()
+ * @link http://docs.elgg.org/Actions/Tokens
+ * @access private
*/
-function validate_action_token($visibleerrors = true) {
- $token = get_input('__elgg_token');
- $ts = get_input('__elgg_ts');
+function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL) {
+ if (!$token) {
+ $token = get_input('__elgg_token');
+ }
+
+ if (!$ts) {
+ $ts = get_input('__elgg_ts');
+ }
+
$session_id = session_id();
if (($token) && ($ts) && ($session_id)) {
// generate token, check with input and forward if invalid
- $generated_token = generate_action_token($ts);
+ $required_token = generate_action_token($ts);
// Validate token
- if ($token == $generated_token) {
- $hour = 60*60;
- $now = time();
-
- // Validate time to ensure its not crazy
- if (($ts>$now-$hour) && ($ts<$now+$hour)) {
+ if ($token == $required_token) {
+
+ if (_elgg_validate_token_timestamp($ts)) {
// We have already got this far, so unless anything
- // else says something to the contry we assume we're ok
+ // else says something to the contrary we assume we're ok
$returnval = true;
- $returnval = trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', array(
+ $returnval = elgg_trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', array(
'token' => $token,
'time' => $ts
), $returnval);
@@ -155,63 +257,126 @@ function validate_action_token($visibleerrors = true) {
register_error(elgg_echo('actiongatekeeper:pluginprevents'));
}
} else if ($visibleerrors) {
- register_error(elgg_echo('actiongatekeeper:timeerror'));
+ // this is necessary because of #5133
+ if (elgg_is_xhr()) {
+ register_error(elgg_echo('js:security:token_refresh_failed', array(elgg_get_site_url())));
+ } else {
+ register_error(elgg_echo('actiongatekeeper:timeerror'));
+ }
}
} else if ($visibleerrors) {
- register_error(elgg_echo('actiongatekeeper:tokeninvalid'));
+ // this is necessary because of #5133
+ if (elgg_is_xhr()) {
+ register_error(elgg_echo('js:security:token_refresh_failed', array(elgg_get_site_url())));
+ } else {
+ register_error(elgg_echo('actiongatekeeper:tokeninvalid'));
+ }
+ }
+ } else {
+ if (! empty($_SERVER['CONTENT_LENGTH']) && empty($_POST)) {
+ // The size of $_POST or uploaded file has exceed the size limit
+ $error_msg = elgg_trigger_plugin_hook('action_gatekeeper:upload_exceeded_msg', 'all', array(
+ 'post_size' => $_SERVER['CONTENT_LENGTH'],
+ 'visible_errors' => $visibleerrors,
+ ), elgg_echo('actiongatekeeper:uploadexceeded'));
+ } else {
+ $error_msg = elgg_echo('actiongatekeeper:missingfields');
+ }
+ if ($visibleerrors) {
+ register_error($error_msg);
}
- }
- else if ($visibleerrors) {
- register_error(elgg_echo('actiongatekeeper:missingfields'));
}
return FALSE;
}
/**
-* Action gatekeeper.
-* This function verifies form input for security features (like a generated token), and forwards
-* the page if they are invalid.
-*
-* Place at the head of actions.
-*/
-function action_gatekeeper() {
- if (validate_action_token()) {
- return TRUE;
+ * Validates the presence of action tokens.
+ *
+ * This function is called for all actions. If action tokens are missing,
+ * the user will be forwarded to the site front page and an error emitted.
+ *
+ * This function verifies form input for security features (like a generated token),
+ * and forwards if they are invalid.
+ *
+ * @param string $action The action being performed
+ *
+ * @return mixed True if valid or redirects.
+ * @access private
+ */
+function action_gatekeeper($action) {
+ if ($action === 'login') {
+ if (validate_action_token(false)) {
+ return true;
+ }
+
+ $token = get_input('__elgg_token');
+ $ts = (int)get_input('__elgg_ts');
+ if ($token && _elgg_validate_token_timestamp($ts)) {
+ // The tokens are present and the time looks valid: this is probably a mismatch due to the
+ // login form being on a different domain.
+ register_error(elgg_echo('actiongatekeeper:crosssitelogin'));
+
+
+ forward('login', 'csrf');
+ }
+
+ // let the validator send an appropriate msg
+ validate_action_token();
+
+ } elseif (validate_action_token()) {
+ return true;
}
- forward();
- exit;
+ forward(REFERER, 'csrf');
}
/**
- * Generate a token for the current user suitable for being placed in a hidden field in action forms.
+ * Generate an action token.
+ *
+ * Action tokens are based on timestamps as returned by {@link time()}.
+ * They are valid for one hour.
+ *
+ * Action tokens should be passed to all actions name __elgg_ts and __elgg_token.
+ *
+ * @warning Action tokens are required for all actions.
*
* @param int $timestamp Unix timestamp
+ *
+ * @see @elgg_view input/securitytoken
+ * @see @elgg_view input/form
+ * @example actions/manual_tokens.php
+ *
+ * @return string|false
+ * @access private
*/
function generate_action_token($timestamp) {
- // Get input values
$site_secret = get_site_secret();
-
- // Current session id
$session_id = session_id();
-
// Session token
$st = $_SESSION['__elgg_session'];
if (($site_secret) && ($session_id)) {
- return md5($site_secret.$timestamp.$session_id.$st);
+ return md5($site_secret . $timestamp . $session_id . $st);
}
return FALSE;
}
/**
- * Initialise the site secret.
+ * Initialise the site secret (32 bytes: "z" to indicate format + 186-bit key in Base64 URL).
+ *
+ * Used during installation and saves as a datalist.
+ *
+ * Note: Old secrets were hex encoded.
*
+ * @return mixed The site secret hash or false
+ * @access private
+ * @todo Move to better file.
*/
function init_site_secret() {
- $secret = md5(rand().microtime());
+ $secret = 'z' . ElggCrypto::getRandomString(31);
+
if (datalist_set('__site_secret__', $secret)) {
return $secret;
}
@@ -220,8 +385,13 @@ function init_site_secret() {
}
/**
- * Retrieve the site secret.
+ * Returns the site secret.
+ *
+ * Used to generate difficult to guess hashes for sessions and action tokens.
*
+ * @return string Site secret.
+ * @access private
+ * @todo Move to better file.
*/
function get_site_secret() {
$secret = datalist_get('__site_secret__');
@@ -233,17 +403,147 @@ function get_site_secret() {
}
/**
- * Check if an action is registered and its file exists.
+ * Get the strength of the site secret
*
- * @param string $action
- * @return BOOL
- * @since 1.8
+ * @return string "strong", "moderate", or "weak"
+ * @access private
*/
-function elgg_action_exist($action) {
+function _elgg_get_site_secret_strength() {
+ $secret = get_site_secret();
+ if ($secret[0] !== 'z') {
+ $rand_max = getrandmax();
+ if ($rand_max < pow(2, 16)) {
+ return 'weak';
+ }
+ if ($rand_max < pow(2, 32)) {
+ return 'moderate';
+ }
+ }
+ return 'strong';
+}
+
+/**
+ * Check if an action is registered and its script exists.
+ *
+ * @param string $action Action name
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+function elgg_action_exists($action) {
global $CONFIG;
return (isset($CONFIG->actions[$action]) && file_exists($CONFIG->actions[$action]['file']));
}
-// Register some actions ***************************************************
-register_elgg_event_handler("init","system","actions_init");
+/**
+ * Checks whether the request was requested via ajax
+ *
+ * @return bool whether page was requested via ajax
+ * @since 1.8.0
+ */
+function elgg_is_xhr() {
+ return isset($_SERVER['HTTP_X_REQUESTED_WITH'])
+ && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' ||
+ get_input('X-Requested-With') === 'XMLHttpRequest';
+}
+
+/**
+ * Catch calls to forward() in ajax request and force an exit.
+ *
+ * Forces response is json of the following form:
+ * <pre>
+ * {
+ * "current_url": "the.url.we/were/coming/from",
+ * "forward_url": "the.url.we/were/going/to",
+ * "system_messages": {
+ * "messages": ["msg1", "msg2", ...],
+ * "errors": ["err1", "err2", ...]
+ * },
+ * "status": -1 //or 0 for success if there are no error messages present
+ * }
+ * </pre>
+ * where "system_messages" is all message registers at the point of forwarding
+ *
+ * @param string $hook
+ * @param string $type
+ * @param string $reason
+ * @param array $params
+ * @return void
+ * @access private
+ */
+function ajax_forward_hook($hook, $type, $reason, $params) {
+ if (elgg_is_xhr()) {
+ // always pass the full structure to avoid boilerplate JS code.
+ $params = array(
+ 'output' => '',
+ 'status' => 0,
+ 'system_messages' => array(
+ 'error' => array(),
+ 'success' => array()
+ )
+ );
+
+ //grab any data echo'd in the action
+ $output = ob_get_clean();
+
+ //Avoid double-encoding in case data is json
+ $json = json_decode($output);
+ if (isset($json)) {
+ $params['output'] = $json;
+ } else {
+ $params['output'] = $output;
+ }
+
+ //Grab any system messages so we can inject them via ajax too
+ $system_messages = system_messages(NULL, "");
+
+ if (isset($system_messages['success'])) {
+ $params['system_messages']['success'] = $system_messages['success'];
+ }
+
+ if (isset($system_messages['error'])) {
+ $params['system_messages']['error'] = $system_messages['error'];
+ $params['status'] = -1;
+ }
+
+ // Check the requester can accept JSON responses, if not fall back to
+ // returning JSON in a plain-text response. Some libraries request
+ // JSON in an invisible iframe which they then read from the iframe,
+ // however some browsers will not accept the JSON MIME type.
+ if (stripos($_SERVER['HTTP_ACCEPT'], 'application/json') === FALSE) {
+ header("Content-type: text/plain");
+ } else {
+ header("Content-type: application/json");
+ }
+
+ echo json_encode($params);
+ exit;
+ }
+}
+
+/**
+ * Buffer all output echo'd directly in the action for inclusion in the returned JSON.
+ * @return void
+ * @access private
+ */
+function ajax_action_hook() {
+ if (elgg_is_xhr()) {
+ ob_start();
+ }
+}
+
+/**
+ * Initialize some ajaxy actions features
+ * @access private
+ */
+function actions_init() {
+ elgg_register_action('security/refreshtoken', '', 'public');
+
+ elgg_register_simplecache_view('js/languages/en');
+
+ elgg_register_plugin_hook_handler('action', 'all', 'ajax_action_hook');
+ elgg_register_plugin_hook_handler('forward', 'all', 'ajax_forward_hook');
+}
+
+elgg_register_event_handler('init', 'system', 'actions_init');
diff --git a/engine/lib/admin.php b/engine/lib/admin.php
index ad7896cb7..f36f29668 100644
--- a/engine/lib/admin.php
+++ b/engine/lib/admin.php
@@ -1,298 +1,663 @@
<?php
/**
* Elgg admin functions.
- * Functions for adding and manipulating options on the admin panel.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * Admin menu items
+ * Elgg has a convenience function for adding menu items to the sidebar of the
+ * admin area. @see elgg_register_admin_menu_item()
+ *
+ * Admin pages
+ * Plugins no not need to provide their own page handler to add a page to the
+ * admin area. A view placed at admin/<section>/<subsection> can be access
+ * at http://example.org/admin/<section>/<subsection>. The title of the page
+ * will be elgg_echo('admin:<section>:<subsection>'). For an example of how to
+ * add a page to the admin area, see the diagnostics plugin.
+ *
+ * Admin notices
+ * System messages (success and error messages) are used in both the main site
+ * and the admin area. There is a special presistent message for the admin area
+ * called an admin notice. It should be used when a plugin requires an
+ * administrator to take an action. An example is the categories plugin
+ * requesting that the administrator set site categories after the plugin has
+ * been activated. @see elgg_add_admin_notice()
+ *
+ *
+ * @package Elgg.Core
+ * @subpackage Admin
*/
-
/**
- * Register an admin page with the admin panel.
- * This function extends the view "admin/main" with the provided view. This view should provide a description
- * and either a control or a link to.
- *
- * Usage:
- * - To add a control to the main admin panel then extend admin/main
- * - To add a control to a new page create a page which renders a view admin/subpage
- * (where subpage is your new page -
- * nb. some pages already exist that you can extend), extend the main view to point to it,
- * and add controls to your new view.
- *
- * At the moment this is essentially a wrapper around elgg_extend_view().
- *
- * @param string $new_admin_view The view associated with the control you're adding
- * @param string $view The view to extend, by default this is 'admin/main'.
- * @param int $priority Optional priority to govern the appearance in the list.
+ * Get the admin users
+ *
+ * @param array $options Options array, @see elgg_get_entities() for parameters
+ *
+ * @return mixed Array of admin users or false on failure. If a count, returns int.
+ * @since 1.8.0
*/
-function extend_elgg_admin_page($new_admin_view, $view = 'admin/main', $priority = 500) {
- elgg_deprecated_notice('extend_elgg_admin_page() does nothing now. Extend admin views manually. See http://docs.elgg.org/', 1.8);
- //return elgg_extend_view($view, $new_admin_view, $priority);
+function elgg_get_admins(array $options = array()) {
+ global $CONFIG;
+
+ if (isset($options['joins'])) {
+ if (!is_array($options['joins'])) {
+ $options['joins'] = array($options['joins']);
+ }
+ $options['joins'][] = "join {$CONFIG->dbprefix}users_entity u on e.guid=u.guid";
+ } else {
+ $options['joins'] = array("join {$CONFIG->dbprefix}users_entity u on e.guid=u.guid");
+ }
+
+ if (isset($options['wheres'])) {
+ if (!is_array($options['wheres'])) {
+ $options['wheres'] = array($options['wheres']);
+ }
+ $options['wheres'][] = "u.admin = 'yes'";
+ } else {
+ $options['wheres'][] = "u.admin = 'yes'";
+ }
+
+ return elgg_get_entities($options);
}
/**
- * Calculate the plugin settings submenu.
- * This is done in a separate function called from the admin
- * page handler because of performance concerns.
+ * Write a persistent message to the admin view.
+ * Useful to alert the admin to take a certain action.
+ * The id is a unique ID that can be cleared once the admin
+ * completes the action.
*
+ * eg: add_admin_notice('twitter_services_no_api',
+ * 'Before your users can use Twitter services on this site, you must set up
+ * the Twitter API key in the <a href="link">Twitter Services Settings</a>');
+ *
+ * @param string $id A unique ID that your plugin can remember
+ * @param string $message Body of the message
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function elgg_admin_add_plugin_settings_sidemenu() {
- global $CONFIG;
+function elgg_add_admin_notice($id, $message) {
+ if ($id && $message) {
+ if (elgg_admin_notice_exists($id)) {
+ return false;
+ }
- if (!$installed_plugins = get_installed_plugins()) {
- // nothing added because no items
- return FALSE;
- }
+ // need to handle when no one is logged in
+ $old_ia = elgg_set_ignore_access(true);
- $parent_item = array(
- 'text' => elgg_echo('admin:plugin_settings'),
- 'id' => 'admin:plugin_settings'
- );
+ $admin_notice = new ElggObject();
+ $admin_notice->subtype = 'admin_notice';
+ // admins can see ACCESS_PRIVATE but no one else can.
+ $admin_notice->access_id = ACCESS_PRIVATE;
+ $admin_notice->admin_notice_id = $id;
+ $admin_notice->description = $message;
- elgg_add_submenu_item($parent_item, 'admin');
+ $result = $admin_notice->save();
- foreach ($installed_plugins as $plugin_id => $info) {
- if (!$info['active']) {
- continue;
- }
+ elgg_set_ignore_access($old_ia);
+
+ return (bool)$result;
+ }
+
+ return false;
+}
- if (elgg_view_exists("settings/{$plugin_id}/edit")) {
- $item = array(
- 'text' => $info['manifest']['name'],
- 'href' => "{$CONFIG->url}pg/admin/plugin_settings/$plugin_id",
- 'parent_id' => 'admin:plugin_settings'
- );
+/**
+ * Remove an admin notice by ID.
+ *
+ * eg In actions/twitter_service/save_settings:
+ * if (is_valid_twitter_api_key()) {
+ * delete_admin_notice('twitter_services_no_api');
+ * }
+ *
+ * @param string $id The unique ID assigned in add_admin_notice()
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+function elgg_delete_admin_notice($id) {
+ if (!$id) {
+ return FALSE;
+ }
+ $result = TRUE;
+ $notices = elgg_get_entities_from_metadata(array(
+ 'metadata_name' => 'admin_notice_id',
+ 'metadata_value' => $id
+ ));
- elgg_add_submenu_item($item, 'admin');
+ if ($notices) {
+ // in case a bad plugin adds many, let it remove them all at once.
+ foreach ($notices as $notice) {
+ $result = ($result && $notice->delete());
}
+ return $result;
}
+ return FALSE;
+}
+
+/**
+ * Get admin notices. An admin must be logged in since the notices are private.
+ *
+ * @param int $limit Limit
+ *
+ * @return array Array of admin notices
+ * @since 1.8.0
+ */
+function elgg_get_admin_notices($limit = 10) {
+ return elgg_get_entities_from_metadata(array(
+ 'type' => 'object',
+ 'subtype' => 'admin_notice',
+ 'limit' => $limit
+ ));
+}
+
+/**
+ * Check if an admin notice is currently active.
+ *
+ * @param string $id The unique ID used to register the notice.
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+function elgg_admin_notice_exists($id) {
+ $old_ia = elgg_set_ignore_access(true);
+ $notice = elgg_get_entities_from_metadata(array(
+ 'type' => 'object',
+ 'subtype' => 'admin_notice',
+ 'metadata_name_value_pair' => array('name' => 'admin_notice_id', 'value' => $id)
+ ));
+ elgg_set_ignore_access($old_ia);
+
+ return ($notice) ? TRUE : FALSE;
}
/**
* Add an admin area section or child section.
- * This is a wrapper for elgg_add_admin_item(array(...), 'admin').
+ * This is a wrapper for elgg_register_menu_item().
*
* Used in conjuction with http://elgg.org/admin/section_id/child_section style
- * page handler.
+ * page handler. See the documentation at the top of this file for more details
+ * on that.
+ *
+ * The text of the menu item is obtained from elgg_echo(admin:$parent_id:$menu_id)
+ *
+ * This function handles registering the parent if it has not been registered.
+ *
+ * @param string $section The menu section to add to
+ * @param string $menu_id The unique ID of section
+ * @param string $parent_id If a child section, the parent section id
+ * @param int $priority The menu item priority
*
- * @param string $section_id
- * @param string $section_title Human readable section title.
- * @param string $parent_id If a child section, the parent section id.
+ * @return bool
+ * @since 1.8.0
*/
-function elgg_add_admin_submenu_item($section_id, $section_title, $parent_id = NULL) {
- global $CONFIG;
+function elgg_register_admin_menu_item($section, $menu_id, $parent_id = NULL, $priority = 100) {
+
+ // make sure parent is registered
+ if ($parent_id && !elgg_is_menu_item_registered('page', $parent_id)) {
+ elgg_register_admin_menu_item($section, $parent_id);
+ }
// in the admin section parents never have links
if ($parent_id) {
- $href = "{$CONFIG->url}pg/admin/$parent_id/$section_id";
- } elseif ($section_id == 'overview') {
- $href = "{$CONFIG->url}pg/admin/$section_id";
-
+ $href = "admin/$parent_id/$menu_id";
} else {
$href = NULL;
}
- $item = array(
- 'text' => $section_title,
- 'href' => $href,
- 'id' => $section_id,
- 'parent_id' => $parent_id
- );
+ $name = $menu_id;
+ if ($parent_id) {
+ $name = "$parent_id:$name";
+ }
- return elgg_add_submenu_item($item, 'admin');
+ return elgg_register_menu_item('page', array(
+ 'name' => $name,
+ 'href' => $href,
+ 'text' => elgg_echo("admin:$name"),
+ 'context' => 'admin',
+ 'parent_name' => $parent_id,
+ 'priority' => $priority,
+ 'section' => $section
+ ));
}
/**
- * Initialise the admin page.
+ * Initialize the admin backend.
+ * @return void
+ * @access private
*/
function admin_init() {
- register_action('admin/user/ban', FALSE, "", TRUE);
- register_action('admin/user/unban', FALSE, "", TRUE);
- register_action('admin/user/delete', FALSE, "", TRUE);
- register_action('admin/user/resetpassword', FALSE, "", TRUE);
- register_action('admin/user/makeadmin', FALSE, "", TRUE);
- register_action('admin/user/removeadmin', FALSE, "", TRUE);
+ elgg_register_action('admin/user/ban', '', 'admin');
+ elgg_register_action('admin/user/unban', '', 'admin');
+ elgg_register_action('admin/user/delete', '', 'admin');
+ elgg_register_action('admin/user/resetpassword', '', 'admin');
+ elgg_register_action('admin/user/makeadmin', '', 'admin');
+ elgg_register_action('admin/user/removeadmin', '', 'admin');
+
+ elgg_register_action('admin/site/update_basic', '', 'admin');
+ elgg_register_action('admin/site/update_advanced', '', 'admin');
+ elgg_register_action('admin/site/flush_cache', '', 'admin');
+ elgg_register_action('admin/site/unlock_upgrade', '', 'admin');
+ elgg_register_action('admin/site/regenerate_secret', '', 'admin');
+
+ elgg_register_action('admin/menu/save', '', 'admin');
+
+ elgg_register_action('admin/delete_admin_notice', '', 'admin');
+
+ elgg_register_action('profile/fields/reset', '', 'admin');
+ elgg_register_action('profile/fields/add', '', 'admin');
+ elgg_register_action('profile/fields/edit', '', 'admin');
+ elgg_register_action('profile/fields/delete', '', 'admin');
+ elgg_register_action('profile/fields/reorder', '', 'admin');
+
+ elgg_register_simplecache_view('css/admin');
+ elgg_register_simplecache_view('js/admin');
+ $url = elgg_get_simplecache_url('js', 'admin');
+ elgg_register_js('elgg.admin', $url);
+ elgg_register_js('jquery.jeditable', 'vendors/jquery/jquery.jeditable.mini.js');
+
+ // administer
+ // dashboard
+ elgg_register_menu_item('page', array(
+ 'name' => 'dashboard',
+ 'href' => 'admin/dashboard',
+ 'text' => elgg_echo('admin:dashboard'),
+ 'context' => 'admin',
+ 'priority' => 10,
+ 'section' => 'administer'
+ ));
+ // statistics
+ elgg_register_admin_menu_item('administer', 'statistics', null, 20);
+ elgg_register_admin_menu_item('administer', 'overview', 'statistics');
+ elgg_register_admin_menu_item('administer', 'server', 'statistics');
- register_action('admin/site/update_basic', FALSE, "", TRUE);
- register_action('admin/site/update_advanced', FALSE, "", TRUE);
+ // users
+ elgg_register_admin_menu_item('administer', 'users', null, 20);
+ elgg_register_admin_menu_item('administer', 'online', 'users', 10);
+ elgg_register_admin_menu_item('administer', 'admins', 'users', 20);
+ elgg_register_admin_menu_item('administer', 'newest', 'users', 30);
+ elgg_register_admin_menu_item('administer', 'add', 'users', 40);
- register_action('admin/menu_items', FALSE, "", TRUE);
+ // configure
+ // plugins
+ elgg_register_menu_item('page', array(
+ 'name' => 'plugins',
+ 'href' => 'admin/plugins',
+ 'text' => elgg_echo('admin:plugins'),
+ 'context' => 'admin',
+ 'priority' => 75,
+ 'section' => 'configure'
+ ));
- register_action('admin/plugins/simple_update_states', FALSE, '', TRUE);
+ // settings
+ elgg_register_admin_menu_item('configure', 'appearance', null, 50);
+ elgg_register_admin_menu_item('configure', 'settings', null, 100);
+ elgg_register_admin_menu_item('configure', 'basic', 'settings', 10);
+ elgg_register_admin_menu_item('configure', 'advanced', 'settings', 20);
+ elgg_register_admin_menu_item('configure', 'advanced/site_secret', 'settings', 25);
+ elgg_register_admin_menu_item('configure', 'menu_items', 'appearance', 30);
+ elgg_register_admin_menu_item('configure', 'profile_fields', 'appearance', 40);
+ // default widgets is added via an event handler elgg_default_widgets_init() in widgets.php
+ // because it requires additional setup.
+
+ // plugin settings are added in elgg_admin_add_plugin_settings_menu() via the admin page handler
+ // for performance reasons.
+
+ // we want plugin settings menu items to be sorted alphabetical
+ if (elgg_in_context('admin')) {
+ elgg_register_plugin_hook_handler('prepare', 'menu:page', 'elgg_admin_sort_page_menu');
+ }
+
+ if (elgg_is_admin_logged_in()) {
+ elgg_register_menu_item('topbar', array(
+ 'name' => 'administration',
+ 'href' => 'admin',
+ 'text' => elgg_view_icon('settings') . elgg_echo('admin'),
+ 'priority' => 100,
+ 'section' => 'alt',
+ ));
+ }
+
+ // widgets
+ $widgets = array('online_users', 'new_users', 'content_stats', 'admin_welcome', 'control_panel');
+ foreach ($widgets as $widget) {
+ elgg_register_widget_type(
+ $widget,
+ elgg_echo("admin:widget:$widget"),
+ elgg_echo("admin:widget:$widget:help"),
+ 'admin'
+ );
+ }
- // admin area overview and basic site settings
- elgg_add_admin_submenu_item('overview', elgg_echo('admin:overview'));
+ // automatic adding of widgets for admin
+ elgg_register_event_handler('make_admin', 'user', 'elgg_add_admin_widgets');
- elgg_add_admin_submenu_item('site', elgg_echo('admin:site'));
- elgg_add_admin_submenu_item('basic', elgg_echo('admin:site:basic'), 'site');
- elgg_add_admin_submenu_item('advanced', elgg_echo('admin:site:advanced'), 'site');
+ elgg_register_page_handler('admin', 'admin_page_handler');
+ elgg_register_page_handler('admin_plugin_screenshot', 'admin_plugin_screenshot_page_handler');
+ elgg_register_page_handler('admin_plugin_text_file', 'admin_markdown_page_handler');
+}
- // appearance
- elgg_add_admin_submenu_item('appearance', elgg_echo('admin:appearance'));
+/**
+ * Create the plugin settings page menu.
+ *
+ * This is done in a separate function called from the admin
+ * page handler because of performance concerns.
+ *
+ * @return void
+ * @access private
+ * @since 1.8.0
+ */
+function elgg_admin_add_plugin_settings_menu() {
- //elgg_add_admin_submenu_item('basic', elgg_echo('admin:appearance'), 'appearance');
- elgg_add_admin_submenu_item('menu_items', elgg_echo('admin:menu_items'), 'appearance');
+ $active_plugins = elgg_get_plugins('active');
+ if (!$active_plugins) {
+ // nothing added because no items
+ return;
+ }
- // users
- elgg_add_admin_submenu_item('users', elgg_echo('admin:users'));
- elgg_add_admin_submenu_item('online', elgg_echo('admin:users:online'), 'users');
- elgg_add_admin_submenu_item('newest', elgg_echo('admin:users:newest'), 'users');
- elgg_add_admin_submenu_item('add', elgg_echo('admin:users:add'), 'users');
+ foreach ($active_plugins as $plugin) {
+ $plugin_id = $plugin->getID();
+ $settings_view_old = 'settings/' . $plugin_id . '/edit';
+ $settings_view_new = 'plugins/' . $plugin_id . '/settings';
+ if (elgg_view_exists($settings_view_new) || elgg_view_exists($settings_view_old)) {
+ elgg_register_menu_item('page', array(
+ 'name' => $plugin_id,
+ 'href' => "admin/plugin_settings/$plugin_id",
+ 'text' => $plugin->getManifest()->getName(),
+ 'parent_name' => 'settings',
+ 'context' => 'admin',
+ 'section' => 'configure',
+ ));
+ }
+ }
+}
- // plugins
- elgg_add_admin_submenu_item('plugins', elgg_echo('admin:plugins'));
- elgg_add_admin_submenu_item('simple', elgg_echo('admin:plugins:simple'), 'plugins');
- elgg_add_admin_submenu_item('advanced', elgg_echo('admin:plugins:advanced'), 'plugins');
+/**
+ * Sort the plugin settings menu items
+ *
+ * @param string $hook
+ * @param string $type
+ * @param array $return
+ * @param array $params
+ *
+ * @return void
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_admin_sort_page_menu($hook, $type, $return, $params) {
+ $configure_items = $return['configure'];
+ /* @var ElggMenuItem[] $configure_items */
+ foreach ($configure_items as $menu_item) {
+ if ($menu_item->getName() == 'settings') {
+ $settings = $menu_item;
+ }
+ }
- // handled in the admin sidemenu so we don't have to generate this on every page load.
- //elgg_add_admin_submenu_item('plugin_settings', elgg_echo('admin:plugin_settings'));
+ // keep the basic and advanced settings at the top
+ /* @var ElggMenuItem $settings */
+ $children = $settings->getChildren();
+ $site_settings = array_splice($children, 0, 2);
+ usort($children, array('ElggMenuBuilder', 'compareByText'));
+ array_splice($children, 0, 0, $site_settings);
+ $settings->setChildren($children);
+}
- register_page_handler('admin', 'admin_settings_page_handler');
+/**
+ * Handles any set up required for administration pages
+ *
+ * @return void
+ * @access private
+ */
+function admin_pagesetup() {
+ if (elgg_in_context('admin')) {
+ $url = elgg_get_simplecache_url('css', 'admin');
+ elgg_register_css('elgg.admin', $url);
+ elgg_load_css('elgg.admin');
+ elgg_unregister_css('elgg');
+
+ // setup footer menu
+ elgg_register_menu_item('admin_footer', array(
+ 'name' => 'faq',
+ 'text' => elgg_echo('admin:footer:faq'),
+ 'href' => 'http://docs.elgg.org/wiki/Category:Administration_FAQ',
+ ));
+
+ elgg_register_menu_item('admin_footer', array(
+ 'name' => 'manual',
+ 'text' => elgg_echo('admin:footer:manual'),
+ 'href' => 'http://docs.elgg.org/wiki/Administration_Manual',
+ ));
+
+ elgg_register_menu_item('admin_footer', array(
+ 'name' => 'community_forums',
+ 'text' => elgg_echo('admin:footer:community_forums'),
+ 'href' => 'http://community.elgg.org/groups/all/',
+ ));
+
+ elgg_register_menu_item('admin_footer', array(
+ 'name' => 'blog',
+ 'text' => elgg_echo('admin:footer:blog'),
+ 'href' => 'http://blog.elgg.org/',
+ ));
+ }
}
/**
* Handle admin pages. Expects corresponding views as admin/section/subsection
*
- * @param $page
- * @return unknown_type
+ * @param array $page Array of pages
+ *
+ * @return bool
+ * @access private
*/
-function admin_settings_page_handler($page) {
- global $CONFIG;
+function admin_page_handler($page) {
admin_gatekeeper();
- elgg_admin_add_plugin_settings_sidemenu();
- set_context('admin');
+ elgg_admin_add_plugin_settings_menu();
+ elgg_set_context('admin');
- // default to overview
+ elgg_unregister_css('elgg');
+ elgg_load_js('elgg.admin');
+ elgg_load_js('jquery.jeditable');
+
+ // default to dashboard
if (!isset($page[0]) || empty($page[0])) {
- $page = array('overview');
+ $page = array('dashboard');
}
// was going to fix this in the page_handler() function but
// it's commented to explicitly return a string if there's a trailing /
- if (empty($page[count($page)-1])) {
+ if (empty($page[count($page) - 1])) {
array_pop($page);
}
$vars = array('page' => $page);
// special page for plugin settings since we create the form for them
- if ($page[0] == 'plugin_settings' && isset($page[1]) && elgg_view_exists("settings/{$page[1]}/edit")) {
- $view = '/admin/components/plugin_settings';
- $vars['plugin'] = $page[1];
- $vars['entity'] = find_plugin_settings($page[1]);
- $title = elgg_echo("admin:plugin_settings:{$page[1]}");
+ if ($page[0] == 'plugin_settings') {
+ if (isset($page[1]) && (elgg_view_exists("settings/{$page[1]}/edit") ||
+ elgg_view_exists("plugins/{$page[1]}/settings"))) {
+
+ $view = 'admin/plugin_settings';
+ $plugin = elgg_get_plugin_from_id($page[1]);
+ $vars['plugin'] = $plugin;
+
+ $title = elgg_echo("admin:{$page[0]}");
+ } else {
+ forward('', '404');
+ }
} else {
$view = 'admin/' . implode('/', $page);
- $title = elgg_echo('admin:' . implode(':', $page));
+ $title = elgg_echo("admin:{$page[0]}");
+ if (count($page) > 1) {
+ $title .= ' : ' . elgg_echo('admin:' . implode(':', $page));
+ }
}
- // allow a place to store helper views outside of the web-accessible views
+ // gets content and prevents direct access to 'components' views
if ($page[0] == 'components' || !($content = elgg_view($view, $vars))) {
$title = elgg_echo('admin:unknown_section');
$content = elgg_echo('admin:unknown_section');
}
- //$body = elgg_view('admin/components/admin_page_layout', array('content' => $content, 'page' => $page));
+ $body = elgg_view_layout('admin', array('content' => $content, 'title' => $title));
+ echo elgg_view_page($title, $body, 'admin');
+ return true;
+}
- $notices_html = '';
- if ($notices = elgg_get_admin_notices()) {
- foreach ($notices as $notice) {
- $notices_html .= elgg_view_entity($notice);
- }
+/**
+ * Serves up screenshots for plugins from
+ * admin_plugin_screenshot/<plugin_id>/<size>/<ss_name>.<ext>
+ *
+ * @param array $pages The pages array
+ * @return bool
+ * @access private
+ */
+function admin_plugin_screenshot_page_handler($pages) {
+ // only admins can use this for security
+ admin_gatekeeper();
+
+ $plugin_id = elgg_extract(0, $pages);
+ // only thumbnail or full.
+ $size = elgg_extract(1, $pages, 'thumbnail');
- $content = "<div class=\"admin_notices\">$notices_html</div>$content";
+ // the rest of the string is the filename
+ $filename_parts = array_slice($pages, 2);
+ $filename = implode('/', $filename_parts);
+ $filename = sanitise_filepath($filename, false);
+
+ $plugin = new ElggPlugin($plugin_id);
+ if (!$plugin) {
+ $file = elgg_get_root_path() . '_graphics/icons/default/medium.png';
+ } else {
+ $file = $plugin->getPath() . $filename;
+ if (!file_exists($file)) {
+ $file = elgg_get_root_path() . '_graphics/icons/default/medium.png';
+ }
}
- $body = elgg_view_layout('administration', $content);
- page_draw($title, $body, "", 'page_shells/admin');
+ header("Content-type: image/jpeg");
+
+ // resize to 100x100 for thumbnails
+ switch ($size) {
+ case 'thumbnail':
+ echo get_resized_image_from_existing_file($file, 100, 100, true);
+ break;
+
+ case 'full':
+ default:
+ echo file_get_contents($file);
+ break;
+ }
+ return true;
}
/**
- * Write a persistent message to the admin view.
- * Useful to alert the admin to take a certain action.
- * The id is a unique ID that can be cleared once the admin
- * completes the action.
+ * Formats and serves out markdown files from plugins.
*
- * eg: add_admin_notice('twitter_services_no_api',
- * 'Before your users can use Twitter services on this site, you must set up
- * the Twitter API key in the <a href="link">Twitter Services Settings</a>');
+ * URLs in format like admin_plugin_text_file/<plugin_id>/filename.ext
*
- * @param string $id A unique ID that your plugin can remember
- * @param string $message Body of the message
+ * The only valid files are:
+ * * README.txt
+ * * CHANGES.txt
+ * * INSTALL.txt
+ * * COPYRIGHT.txt
+ * * LICENSE.txt
+ *
+ * @param array $pages
+ * @return bool
+ * @access private
*/
-function elgg_add_admin_notice($id, $message) {
- if ($id && $message) {
- $admin_notice = new ElggObject();
- $admin_notice->subtype = 'admin_notice';
- // admins can see ACCESS_PRIVATE but no one else can.
- $admin_notice->access_id = ACCESS_PRIVATE;
- $admin_notice->admin_notice_id = $id;
- $admin_notice->description = $message;
+function admin_markdown_page_handler($pages) {
+ admin_gatekeeper();
- return $admin_notice->save();
+ elgg_set_context('admin');
+
+ elgg_unregister_css('elgg');
+ elgg_load_js('elgg.admin');
+ elgg_load_js('jquery.jeditable');
+ elgg_load_library('elgg:markdown');
+
+ $plugin_id = elgg_extract(0, $pages);
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+ $filename = elgg_extract(1, $pages);
+
+ $error = false;
+ if (!$plugin) {
+ $error = elgg_echo('admin:plugins:markdown:unknown_plugin');
+ $body = elgg_view_layout('admin', array('content' => $error, 'title' => $error));
+ echo elgg_view_page($error, $body, 'admin');
+ return true;
}
- return FALSE;
-}
+ $text_files = $plugin->getAvailableTextFiles();
+ if (!array_key_exists($filename, $text_files)) {
+ $error = elgg_echo('admin:plugins:markdown:unknown_file');
+ }
-/**
- * Remove an admin notice by ID.
- *
- * eg In actions/twitter_service/save_settings:
- * if (is_valid_twitter_api_key()) {
- * delete_admin_notice('twitter_services_no_api');
- * }
- *
- * @param string $id The unique ID assigned in add_admin_notice()
- */
-function elgg_delete_admin_notice($id) {
- if (!$id) {
- return FALSE;
+ $file = $text_files[$filename];
+ $file_contents = file_get_contents($file);
+
+ if (!$file_contents) {
+ $error = elgg_echo('admin:plugins:markdown:unknown_file');
}
- $result = TRUE;
- if ($notices = elgg_get_entities_from_metadata(array('metadata_name' => 'admin_notice_id', 'metadata_value' => $id))) {
- // in case a bad plugin adds many, let it remove them all at once.
- foreach ($notices as $notice) {
- $result = ($result && $notice->delete());
- }
- return $result;
+
+ if ($error) {
+ $title = $error;
+ $body = elgg_view_layout('admin', array('content' => $error, 'title' => $title));
+ echo elgg_view_page($title, $body, 'admin');
+ return true;
}
- return FALSE;
-}
-/**
- * List all admin messages.
- *
- * @param int $limit Limit
- */
-function elgg_get_admin_notices($limit = 10) {
- return elgg_get_entities_from_metadata(array(
- 'type' => 'object',
- 'subtype' => 'admin_notice',
- 'limit' => $limit
+ $title = $plugin->getManifest()->getName() . ": $filename";
+ $text = Markdown($file_contents);
+
+ $body = elgg_view_layout('admin', array(
+ // setting classes here because there's no way to pass classes
+ // to the layout
+ 'content' => '<div class="elgg-markdown">' . $text . '</div>',
+ 'title' => $title
));
+
+ echo elgg_view_page($title, $body, 'admin');
+ return true;
}
/**
- * Check if an admin notice is currently active.
- * @param string $id The unique ID used to register the notice.
+ * Adds default admin widgets to the admin dashboard.
+ *
+ * @param string $event
+ * @param string $type
+ * @param ElggUser $user
+ *
+ * @return null|true
+ * @access private
*/
-function elgg_admin_notice_exists($id) {
- $notice = elgg_get_entities_from_metadata(array(
- 'type' => 'object',
- 'subtype' => 'admin_notice',
- 'metadata_name_value_pair' => array('name' => 'admin_notice_id', 'value' => $id)
- ));
+function elgg_add_admin_widgets($event, $type, $user) {
+ elgg_set_ignore_access(true);
- return ($notice) ? TRUE : FALSE;
-}
+ // check if the user already has widgets
+ if (elgg_get_widgets($user->getGUID(), 'admin')) {
+ return true;
+ }
+ // In the form column => array of handlers in order, top to bottom
+ $adminWidgets = array(
+ 1 => array('control_panel', 'admin_welcome'),
+ 2 => array('online_users', 'new_users', 'content_stats'),
+ );
+
+ foreach ($adminWidgets as $column => $handlers) {
+ foreach ($handlers as $position => $handler) {
+ $guid = elgg_create_widget($user->getGUID(), $handler, 'admin');
+ if ($guid) {
+ $widget = get_entity($guid);
+ /* @var ElggWidget $widget */
+ $widget->move($column, $position);
+ }
+ }
+ }
+ elgg_set_ignore_access(false);
+}
-// Register init functions
-register_elgg_event_handler('init', 'system', 'admin_init');
-register_elgg_event_handler('pagesetup', 'system', 'admin_pagesetup');
+elgg_register_event_handler('init', 'system', 'admin_init');
+elgg_register_event_handler('pagesetup', 'system', 'admin_pagesetup', 1000);
diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php
index ac002f394..5e9b530de 100644
--- a/engine/lib/annotations.php
+++ b/engine/lib/annotations.php
@@ -5,130 +5,19 @@
*
* @package Elgg
* @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
*/
/**
- * Include the ElggExtender superclass
- *
- */
-require_once('extender.php');
-
-/**
- * ElggAnnotation
- *
- * An annotation is similar to metadata each entity can contain more than one of each annotation.
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- */
-class ElggAnnotation extends ElggExtender {
-
- /**
- * Construct a new site object, optionally from a given id value or db row.
- *
- * @param mixed $id
- */
- function __construct($id = null) {
- $this->attributes = array();
-
- if (!empty($id)) {
- if ($id instanceof stdClass) {
- $annotation = $id;
- } else {
- $annotation = get_annotation($id);
- }
-
- if ($annotation) {
- $objarray = (array) $annotation;
-
- foreach($objarray as $key => $value) {
- $this->attributes[$key] = $value;
- }
-
- $this->attributes['type'] = "annotation";
- }
- }
- }
-
- /**
- * Class member get overloading
- *
- * @param string $name
- * @return mixed
- */
- function __get($name) {
- return $this->get($name);
- }
-
- /**
- * Class member set overloading
- *
- * @param string $name
- * @param mixed $value
- * @return void
- */
- function __set($name, $value) {
- return $this->set($name, $value);
- }
-
- /**
- * Save this instance
- *
- * @return int an object id
- */
- function save() {
- if ($this->id > 0) {
- return update_annotation($this->id, $this->name, $this->value, $this->value_type, $this->owner_guid, $this->access_id);
- } else {
- $this->id = create_annotation($this->entity_guid, $this->name, $this->value,
- $this->value_type, $this->owner_guid, $this->access_id);
-
- if (!$this->id) {
- throw new IOException(sprintf(elgg_new('IOException:UnableToSaveNew'), get_class()));
- }
- return $this->id;
- }
- }
-
- /**
- * Delete a given site.
- */
- function delete() {
- return delete_annotation($this->id);
- }
-
- /**
- * Get a url for this annotation.
- *
- * @return string
- */
- public function getURL() {
- return get_annotation_url($this->id);
- }
-
- // SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * For a given ID, return the object associated with it.
- * This is used by the river functionality primarily.
- * This is useful for checking access permissions etc on objects.
- */
- public function getObjectFromID($id) {
- return get_annotation($id);
- }
-}
-
-/**
* Convert a database row to a new ElggAnnotation
*
- * @param stdClass $row
- * @return stdClass or ElggAnnotation
+ * @param stdClass $row Db row result object
+ *
+ * @return ElggAnnotation
+ * @access private
*/
function row_to_elggannotation($row) {
if (!($row instanceof stdClass)) {
+ // @todo should throw in this case?
return $row;
}
@@ -136,30 +25,46 @@ function row_to_elggannotation($row) {
}
/**
- * Get a specific annotation.
+ * Get a specific annotation by its id.
+ * If you want multiple annotation objects, use
+ * {@link elgg_get_annotations()}.
+ *
+ * @param int $id The id of the annotation object being retrieved.
*
- * @param int $annotation_id
+ * @return ElggAnnotation|false
*/
-function get_annotation($annotation_id) {
- global $CONFIG;
-
- $annotation_id = (int) $annotation_id;
- $access = get_access_sql_suffix("a");
+function elgg_get_annotation_from_id($id) {
+ return elgg_get_metastring_based_object_from_id($id, 'annotations');
+}
- return row_to_elggannotation(get_data_row("SELECT a.*, n.string as name, v.string as value from {$CONFIG->dbprefix}annotations a JOIN {$CONFIG->dbprefix}metastrings n on a.name_id = n.id JOIN {$CONFIG->dbprefix}metastrings v on a.value_id = v.id where a.id=$annotation_id and $access"));
+/**
+ * Deletes an annotation using its ID.
+ *
+ * @param int $id The annotation ID to delete.
+ * @return bool
+ */
+function elgg_delete_annotation_by_id($id) {
+ $annotation = elgg_get_annotation_from_id($id);
+ if (!$annotation) {
+ return false;
+ }
+ return $annotation->delete();
}
/**
* Create a new annotation.
*
- * @param int $entity_guid
- * @param string $name
- * @param string $value
- * @param string $value_type
- * @param int $owner_guid
- * @param int $access_id
+ * @param int $entity_guid Entity Guid
+ * @param string $name Name of annotation
+ * @param string $value Value of annotation
+ * @param string $value_type Type of value (default is auto detection)
+ * @param int $owner_guid Owner of annotation (default is logged in user)
+ * @param int $access_id Access level of annotation
+ *
+ * @return int|bool id on success or false on failure
*/
-function create_annotation($entity_guid, $name, $value, $value_type, $owner_guid, $access_id = ACCESS_PRIVATE) {
+function create_annotation($entity_guid, $name, $value, $value_type = '',
+$owner_guid = 0, $access_id = ACCESS_PRIVATE) {
global $CONFIG;
$result = false;
@@ -170,8 +75,8 @@ function create_annotation($entity_guid, $name, $value, $value_type, $owner_guid
$value_type = detect_extender_valuetype($value, sanitise_string(trim($value_type)));
$owner_guid = (int)$owner_guid;
- if ($owner_guid==0) {
- $owner_guid = get_loggedin_userid();
+ if ($owner_guid == 0) {
+ $owner_guid = elgg_get_logged_in_user_guid();
}
$access_id = (int)$access_id;
@@ -190,20 +95,20 @@ function create_annotation($entity_guid, $name, $value, $value_type, $owner_guid
$entity = get_entity($entity_guid);
- if (trigger_elgg_event('annotate',$entity->type,$entity)) {
- system_log($entity, 'annotate');
-
+ if (elgg_trigger_event('annotate', $entity->type, $entity)) {
// If ok then add it
$result = insert_data("INSERT into {$CONFIG->dbprefix}annotations
(entity_guid, name_id, value_id, value_type, owner_guid, time_created, access_id) VALUES
($entity_guid,'$name',$value,'$value_type', $owner_guid, $time, $access_id)");
- if ($result!==false) {
- $obj = get_annotation($result);
- if (trigger_elgg_event('create', 'annotation', $obj)) {
+ if ($result !== false) {
+ $obj = elgg_get_annotation_from_id($result);
+ if (elgg_trigger_event('create', 'annotation', $obj)) {
return $result;
} else {
- delete_annotation($result);
+ // plugin returned false to reject annotation
+ elgg_delete_annotation_by_id($result);
+ return FALSE;
}
}
}
@@ -214,12 +119,14 @@ function create_annotation($entity_guid, $name, $value, $value_type, $owner_guid
/**
* Update an annotation.
*
- * @param int $annotation_id
- * @param string $name
- * @param string $value
- * @param string $value_type
- * @param int $owner_guid
- * @param int $access_id
+ * @param int $annotation_id Annotation ID
+ * @param string $name Name of annotation
+ * @param string $value Value of annotation
+ * @param string $value_type Type of value
+ * @param int $owner_guid Owner of annotation
+ * @param int $access_id Access level of annotation
+ *
+ * @return bool
*/
function update_annotation($annotation_id, $name, $value, $value_type, $owner_guid, $access_id) {
global $CONFIG;
@@ -230,8 +137,8 @@ function update_annotation($annotation_id, $name, $value, $value_type, $owner_gu
$value_type = detect_extender_valuetype($value, sanitise_string(trim($value_type)));
$owner_guid = (int)$owner_guid;
- if ($owner_guid==0) {
- $owner_guid = get_loggedin_userid();
+ if ($owner_guid == 0) {
+ $owner_guid = elgg_get_logged_in_user_guid();
}
$access_id = (int)$access_id;
@@ -251,201 +158,186 @@ function update_annotation($annotation_id, $name, $value, $value_type, $owner_gu
// If ok then add it
$result = update_data("UPDATE {$CONFIG->dbprefix}annotations
- set value_id='$value', value_type='$value_type', access_id=$access_id, owner_guid=$owner_guid
- where id=$annotation_id and name_id='$name' and $access");
-
- if ($result!==false) {
- $obj = get_annotation($annotation_id);
- if (trigger_elgg_event('update', 'annotation', $obj)) {
- return true;
- } else {
- delete_annotation($annotation_id);
- }
+ set name_id='$name', value_id='$value', value_type='$value_type', access_id=$access_id, owner_guid=$owner_guid
+ where id=$annotation_id and $access");
+
+ if ($result !== false) {
+ // @todo add plugin hook that sends old and new annotation information before db access
+ $obj = elgg_get_annotation_from_id($annotation_id);
+ elgg_trigger_event('update', 'annotation', $obj);
}
return $result;
}
/**
- * Get a list of annotations for a given object/user/annotation type.
+ * Returns annotations. Accepts all elgg_get_entities() options for entity
+ * restraints.
*
- * @param int|array $entity_guid
- * @param string $entity_type
- * @param string $entity_subtype
- * @param string $name
- * @param mixed $value
- * @param int|array $owner_guid
- * @param int $limit
- * @param int $offset
- * @param string $order_by
+ * @see elgg_get_entities
+ *
+ * @param array $options Array in format:
+ *
+ * annotation_names => NULL|ARR Annotation names
+ * annotation_values => NULL|ARR Annotation values
+ * annotation_ids => NULL|ARR annotation ids
+ * annotation_case_sensitive => BOOL Overall Case sensitive
+ * annotation_owner_guids => NULL|ARR guids for annotation owners
+ * annotation_created_time_lower => INT Lower limit for created time.
+ * annotation_created_time_upper => INT Upper limit for created time.
+ * annotation_calculation => STR Perform the MySQL function on the annotation values returned.
+ * Do not confuse this "annotation_calculation" option with the
+ * "calculation" option to elgg_get_entities_from_annotation_calculation().
+ * The "annotation_calculation" option causes this function to
+ * return the result of performing a mathematical calculation on
+ * all annotations that match the query instead of ElggAnnotation
+ * objects.
+ * See the docs for elgg_get_entities_from_annotation_calculation()
+ * for the proper use of the "calculation" option.
+ *
+ *
+ * @return ElggAnnotation[]|mixed
+ * @since 1.8.0
*/
-function get_annotations($entity_guid = 0, $entity_type = "", $entity_subtype = "", $name = "",
-$value = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "asc", $timelower = 0, $timeupper = 0, $entity_owner_guid = 0) {
- global $CONFIG;
-
- $timelower = (int) $timelower;
- $timeupper = (int) $timeupper;
+function elgg_get_annotations(array $options = array()) {
- if (is_array($entity_guid)) {
- if (sizeof($entity_guid) > 0) {
- foreach($entity_guid as $key => $val) {
- $entity_guid[$key] = (int) $val;
- }
- } else {
- $entity_guid = 0;
- }
+ // @todo remove support for count shortcut - see #4393
+ if (isset($options['__egefac']) && $options['__egefac']) {
+ unset($options['__egefac']);
} else {
- $entity_guid = (int)$entity_guid;
- }
-
- $entity_type = sanitise_string($entity_type);
-
- if ($entity_subtype) {
- if (!$entity_subtype = get_subtype_id($entity_type, $entity_subtype)) {
- // requesting a non-existing subtype: return false
- return FALSE;
- }
- }
-
- if ($name) {
- $name = get_metastring_id($name);
-
- if ($name === false) {
- $name = 0;
- }
- }
- if ($value != "") {
- $value = get_metastring_id($value);
- }
-
- if (is_array($owner_guid)) {
- if (sizeof($owner_guid) > 0) {
- foreach($owner_guid as $key => $val) {
- $owner_guid[$key] = (int) $val;
- }
- } else {
- $owner_guid = 0;
- }
- } else {
- $owner_guid = (int)$owner_guid;
- }
-
- if (is_array($entity_owner_guid)) {
- if (sizeof($entity_owner_guid) > 0) {
- foreach($entity_owner_guid as $key => $val) {
- $entity_owner_guid[$key] = (int) $val;
- }
- } else {
- $entity_owner_guid = 0;
- }
- } else {
- $entity_owner_guid = (int)$entity_owner_guid;
- }
-
- $limit = (int)$limit;
- $offset = (int)$offset;
- if($order_by == 'asc') {
- $order_by = "a.time_created asc";
- }
-
- if($order_by == 'desc') {
- $order_by = "a.time_created desc";
- }
-
- $where = array();
-
- if ($entity_guid != 0 && !is_array($entity_guid)) {
- $where[] = "a.entity_guid=$entity_guid";
- } else if (is_array($entity_guid)) {
- $where[] = "a.entity_guid in (". implode(",",$entity_guid) . ")";
- }
-
- if ($entity_type != "") {
- $where[] = "e.type='$entity_type'";
- }
-
- if ($entity_subtype != "") {
- $where[] = "e.subtype='$entity_subtype'";
- }
+ // support shortcut of 'count' => true for 'annotation_calculation' => 'count'
+ if (isset($options['count']) && $options['count']) {
+ $options['annotation_calculation'] = 'count';
+ unset($options['count']);
+ }
+ }
+
+ $options['metastring_type'] = 'annotations';
+ return elgg_get_metastring_based_objects($options);
+}
- if ($owner_guid != 0 && !is_array($owner_guid)) {
- $where[] = "a.owner_guid=$owner_guid";
- } else {
- if (is_array($owner_guid)) {
- $where[] = "a.owner_guid in (" . implode(",",$owner_guid) . ")";
- }
+/**
+ * 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 bool|null true on success, false on failure, null if no annotations to delete.
+ * @since 1.8.0
+ */
+function elgg_delete_annotations(array $options) {
+ if (!elgg_is_valid_options_for_batch_operation($options, 'annotations')) {
+ return false;
}
- if ($entity_owner_guid != 0 && !is_array($entity_owner_guid)) {
- $where[] = "e.owner_guid=$entity_owner_guid";
- } else {
- if (is_array($entity_owner_guid)) {
- $where[] = "e.owner_guid in (" . implode(",",$entity_owner_guid) . ")";
- }
- }
+ $options['metastring_type'] = 'annotations';
+ return elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false);
+}
- if ($name !== "") {
- $where[] = "a.name_id='$name'";
+/**
+ * Disables annotations based on $options.
+ *
+ * @warning Unlike elgg_get_annotations() this will not accept an empty options array!
+ *
+ * @param array $options An options array. {@See elgg_get_annotations()}
+ * @return bool|null true on success, false on failure, null if no annotations disabled.
+ * @since 1.8.0
+ */
+function elgg_disable_annotations(array $options) {
+ if (!elgg_is_valid_options_for_batch_operation($options, 'annotations')) {
+ return false;
}
+
+ // if we can see hidden (disabled) we need to use the offset
+ // otherwise we risk an infinite loop if there are more than 50
+ $inc_offset = access_get_show_hidden_status();
- if ($value != "") {
- $where[] = "a.value_id='$value'";
- }
+ $options['metastring_type'] = 'annotations';
+ return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset);
+}
- if ($timelower) {
- $where[] = "a.time_created >= {$timelower}";
+/**
+ * Enables annotations based on $options.
+ *
+ * @warning Unlike elgg_get_annotations() this will not accept an empty options array!
+ *
+ * @warning In order to enable annotations, you must first use
+ * {@link access_show_hidden_entities()}.
+ *
+ * @param array $options An options array. {@See elgg_get_annotations()}
+ * @return bool|null true on success, false on failure, null if no metadata enabled.
+ * @since 1.8.0
+ */
+function elgg_enable_annotations(array $options) {
+ if (!$options || !is_array($options)) {
+ return false;
}
- if ($timeupper) {
- $where[] = "a.time_created <= {$timeupper}";
- }
+ $options['metastring_type'] = 'annotations';
+ return elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback');
+}
- $query = "SELECT a.*, n.string as name, v.string as value
- FROM {$CONFIG->dbprefix}annotations a
- JOIN {$CONFIG->dbprefix}entities e on a.entity_guid = e.guid
- JOIN {$CONFIG->dbprefix}metastrings v on a.value_id=v.id
- JOIN {$CONFIG->dbprefix}metastrings n on a.name_id = n.id where ";
+/**
+ * Returns a rendered list of annotations with pagination.
+ *
+ * @param array $options Annotation getter and display options.
+ * {@see elgg_get_annotations()} and {@see elgg_list_entities()}.
+ *
+ * @return string The list of entities
+ * @since 1.8.0
+ */
+function elgg_list_annotations($options) {
+ $defaults = array(
+ 'limit' => 25,
+ 'offset' => (int) max(get_input('annoff', 0), 0),
+ );
- foreach ($where as $w) {
- $query .= " $w and ";
- }
- $query .= get_access_sql_suffix("a"); // Add access controls
- $query .= " order by $order_by limit $offset,$limit"; // Add order and limit
+ $options = array_merge($defaults, $options);
- return get_data($query, "row_to_elggannotation");
+ return elgg_list_entities($options, 'elgg_get_annotations', 'elgg_view_annotation_list');
}
+/**
+ * Entities interfaces
+ */
/**
- * Returns entities based upon annotations. Accepts the same values as
- * elgg_get_entities_from_metadata() but uses the annotations table.
+ * Returns entities based upon annotations. Also accepts all options available
+ * to elgg_get_entities() and elgg_get_entities_from_metadata().
*
- * NB: Entity creation time is selected as max_time. To sort based upon
+ * Entity creation time is selected as maxtime. To sort based upon
* this, pass 'order_by' => 'maxtime asc' || 'maxtime desc'
*
- * time_created in this case will be the time the annotation was created.
- *
* @see elgg_get_entities
* @see elgg_get_entities_from_metadata
+ *
* @param array $options Array in format:
*
* annotation_names => NULL|ARR annotations names
*
* annotation_values => NULL|ARR annotations values
*
- * annotation_name_value_pairs => NULL|ARR (name = 'name', value => 'value', 'operand' => '=', 'case_sensitive' => TRUE) entries.
- * Currently if multiple values are sent via an array (value => array('value1', 'value2') the pair's operand will be forced to "IN".
+ * annotation_name_value_pairs => NULL|ARR (name = 'name', value => 'value',
+ * 'operator' => '=', 'case_sensitive' => TRUE) entries.
+ * Currently if multiple values are sent via an array (value => array('value1', 'value2')
+ * the pair's operator will be forced to "IN".
*
- * annotation_name_value_pairs_operator => NULL|STR The operator to use for combining (name = value) OPERATOR (name = value); default AND
+ * annotation_name_value_pairs_operator => NULL|STR The operator to use for combining
+ * (name = value) OPERATOR (name = value); default AND
*
* annotation_case_sensitive => BOOL Overall Case sensitive
*
- * order_by_annotation => NULL|ARR (array('name' => 'annotation_text1', 'direction' => ASC|DESC, 'as' => text|integer),
+ * order_by_annotation => NULL|ARR (array('name' => 'annotation_text1', 'direction' => ASC|DESC,
+ * 'as' => text|integer),
+ *
* Also supports array('name' => 'annotation_text1')
*
* annotation_owner_guids => NULL|ARR guids for annotaiton owners
*
- * @return array
+ * @return mixed If count, int. If not count, array. false on errors.
+ * @since 1.7.0
*/
function elgg_get_entities_from_annotations(array $options = array()) {
$defaults = array(
@@ -457,6 +349,9 @@ function elgg_get_entities_from_annotations(array $options = array()) {
'annotation_case_sensitive' => TRUE,
'order_by_annotation' => array(),
+ 'annotation_created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
+ 'annotation_created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
+
'annotation_owner_guids' => ELGG_ENTITIES_ANY_VALUE,
'order_by' => 'maxtime desc',
@@ -465,11 +360,14 @@ function elgg_get_entities_from_annotations(array $options = array()) {
$options = array_merge($defaults, $options);
- $singulars = array('annotation_name', 'annotation_value', 'annotation_name_value_pair', 'annotation_owner_guid');
+ $singulars = array('annotation_name', 'annotation_value',
+ 'annotation_name_value_pair', 'annotation_owner_guid');
+
$options = elgg_normalise_plural_options_array($options, $singulars);
+ $options = elgg_entities_get_metastrings_options('annotation', $options);
- if (!$options = elgg_entities_get_metastrings_options('annotation', $options)) {
- return FALSE;
+ if (!$options) {
+ return false;
}
// special sorting for annotations
@@ -477,625 +375,116 @@ function elgg_get_entities_from_annotations(array $options = array()) {
$options['selects'][] = "max(n_table.time_created) as maxtime";
$options['group_by'] = 'n_table.entity_guid';
- return elgg_get_entities($options);
-}
-
-/**
- * @deprecated 1.7 Use elgg_get_entities_from_annotations()
- * @param $entity_type
- * @param $entity_subtype
- * @param $name
- * @param $value
- * @param $owner_guid
- * @param $group_guid
- * @param $limit
- * @param $offset
- * @param $order_by
- * @param $count
- * @param $timelower
- * @param $timeupper
- * @return unknown_type
- */
-function get_entities_from_annotations($entity_type = "", $entity_subtype = "", $name = "", $value = "",
-$owner_guid = 0, $group_guid = 0, $limit = 10, $offset = 0, $order_by = "asc", $count = false,
-$timelower = 0, $timeupper = 0) {
- elgg_deprecated_notice('get_entities_from_annotations() was deprecated by elgg_get_entities_from_annotations().', 1.7);
-
- $options = array();
-
- $options['annotation_names'] = $name;
-
- if ($value) {
- $options['annotation_values'] = $value;
- }
-
- if ($entity_type) {
- $options['types'] = $entity_type;
- }
-
- if ($entity_subtype) {
- $options['subtypes'] = $entity_subtype;
- }
-
- if ($owner_guid) {
- $options['annotation_owner_guid'] = $owner_guid;
- }
-
- if ($group_guid) {
- $options['container_guid'] = $group_guid;
- }
-
- if ($limit) {
- $options['limit'] = $limit;
- }
+ $time_wheres = elgg_get_entity_time_where_sql('a', $options['annotation_created_time_upper'],
+ $options['annotation_created_time_lower']);
- if ($offset) {
- $options['offset'] = $offset;
+ if ($time_wheres) {
+ $options['wheres'] = array_merge($options['wheres'], $time_wheres);
}
- if ($order_by) {
- $options['order_by'] = "maxtime $order_by";
- }
-
- if ($count) {
- $options['count'] = $count;
- }
-
- return elgg_get_entities_from_annotations($options);
-}
-
-/**
- * Lists entities
- *
- * @see elgg_view_entity_list
- *
- * @param string $entity_type Type of entity.
- * @param string $entity_subtype Subtype of entity.
- * @param string $name Name of annotation.
- * @param string $value Value of annotation.
- * @param int $limit Maximum number of results to return.
- * @param int $owner_guid Owner.
- * @param int $group_guid Group container. Currently this is only supported if $entity_type == 'object'
- * @param boolean $asc Whether to list in ascending or descending order (default: desc)
- * @param boolean $fullview Whether to display the entities in full
- * @param boolean $viewtypetoggle Determines whether or not the 'gallery' view can be displayed (default: no)
- * @return string Formatted entity list
- */
-function list_entities_from_annotations($entity_type = "", $entity_subtype = "", $name = "", $value = "", $limit = 10, $owner_guid = 0, $group_guid = 0, $asc = false, $fullview = true, $viewtypetoggle = false) {
- elgg_deprecated_notice('list_entities_from_annotations is deprecated by elgg_list_entities_from_annotations', 1.8);
-
- if ($entity_type) {
- $options['types'] = $entity_type;
- }
-
- if ($entity_subtype) {
- $options['subtypes'] = $entity_subtype;
- }
-
- if ($name) {
- $options['annotation_names'] = $name;
- }
-
- if ($value) {
- $options['annotation_values'] = $value;
- }
-
- if ($limit) {
- $options['limit'] = $limit;
- }
-
- if ($owner_guid) {
- $options['annotation_owner_guid'] = $owner_guid;
- }
-
- if ($group_guid) {
- $options['container_guid'] = $group_guid;
- }
-
- if ($asc) {
- $options['order_by'] = 'maxtime desc';
- }
-
- if ($offset = sanitise_int(get_input('offset', null))) {
- $options['offset'] = $offset;
- }
-
- $options['full_view'] = $fullview;
- $options['view_type_toggle'] = $viewtypetoggle;
- $options['pagination'] = $pagination;
-
- return elgg_list_entities_from_annotations($options);
+ return elgg_get_entities_from_metadata($options);
}
/**
* Returns a viewable list of entities from annotations.
*
- * @param array $options Any elgg_get_entity_from_annotation() options plus:
- *
- * offset => INT Start this many from the first result
- *
- * limit => INT Limit results to this
- *
- * full_view => BOOL Display full view entities
+ * @param array $options Options array
*
- * view_type_toggle => BOOL Display gallery / list switch
+ * @see elgg_get_entities_from_annotations()
+ * @see elgg_list_entities()
*
- * pagination => BOOL Display pagination links
- *
- * @return str
+ * @return string
*/
function elgg_list_entities_from_annotations($options = array()) {
- $defaults = array(
- 'offset' => (int) max(get_input('offset', 0), 0),
- 'limit' => (int) max(get_input('limit', 10), 0),
- 'full_view' => TRUE,
- 'view_type_toggle' => FALSE,
- 'pagination' => TRUE
- );
- $options = array_merge($defaults, $options);
-
- $count = elgg_get_entities_from_annotations(array_merge(array('count' => TRUE), $options));
- $entities = elgg_get_entities_from_annotations($options);
-
- return elgg_view_entity_list($entities, $count, $options['offset'],
- $options['limit'], $options['full_view'], $options['view_type_toggle'], $options['pagination']);
-}
-
-/**
- * Returns a human-readable list of annotations on a particular entity.
- *
- * @param int $entity_guid The entity GUID
- * @param string $name The name of the kind of annotation
- * @param int $limit The number of annotations to display at once
- * @param true|false $asc Whether or not the annotations are displayed in ascending order. (Default: true)
- * @return string HTML (etc) version of the annotation list
- */
-function list_annotations($entity_guid, $name = "", $limit = 25, $asc = true) {
- if ($asc) {
- $asc = "asc";
- } else {
- $asc = "desc";
- }
- $count = count_annotations($entity_guid, "", "", $name);
- $offset = (int) get_input("annoff",0);
- $annotations = get_annotations($entity_guid, "", "", $name, "", "", $limit, $offset, $asc);
-
- return elgg_view_annotation_list($annotations, $count, $offset, $limit);
-}
-
-/**
- * Return the sum of a given integer annotation.
- *
- * @param $entity_guid int
- * @param $entity_type string
- * @param $entity_subtype string
- * @param $name string
- */
-function get_annotations_sum($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", $value = "", $value_type = "", $owner_guid = 0) {
- return __get_annotations_calculate_x("sum", $entity_guid, $entity_type, $entity_subtype, $name, $value, $value_type, $owner_guid);
-}
-
-/**
- * Return the max of a given integer annotation.
- *
- * @param $entity_guid int
- * @param $entity_type string
- * @param $entity_subtype string
- * @param $name string
- */
-function get_annotations_max($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", $value = "", $value_type = "", $owner_guid = 0) {
- return __get_annotations_calculate_x("max", $entity_guid, $entity_type, $entity_subtype, $name, $value, $value_type, $owner_guid);
-}
-
-/**
- * Return the minumum of a given integer annotation.
- *
- * @param $entity_guid int
- * @param $entity_type string
- * @param $entity_subtype string
- * @param $name string
- */
-function get_annotations_min($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", $value = "", $value_type = "", $owner_guid = 0) {
- return __get_annotations_calculate_x("min", $entity_guid, $entity_type, $entity_subtype, $name, $value, $value_type, $owner_guid);
-}
-
-/**
- * Return the average of a given integer annotation.
- *
- * @param $entity_guid int
- * @param $entity_type string
- * @param $entity_subtype string
- * @param $name string
- */
-function get_annotations_avg($entity_guid, $entity_type = "", $entity_subtype = "", $name = "", $value = "", $value_type = "", $owner_guid = 0) {
- return __get_annotations_calculate_x("avg", $entity_guid, $entity_type, $entity_subtype, $name, $value, $value_type, $owner_guid);
-}
-
-/**
- * Count the number of annotations based on search parameters
- *
- * @param int $entity_guid
- * @param string $entity_type
- * @param string $entity_subtype
- * @param string $name
- */
-function count_annotations($entity_guid = 0, $entity_type = "", $entity_subtype = "", $name = "", $value = "", $value_type = "", $owner_guid = 0, $timelower = 0, $timeupper = 0) {
- return __get_annotations_calculate_x("count", $entity_guid, $entity_type, $entity_subtype, $name, $value, $value_type, $owner_guid, $timelower, $timeupper);
+ return elgg_list_entities($options, 'elgg_get_entities_from_annotations');
}
/**
- * Perform a mathmatical calculation on integer annotations.
- *
- * @param $sum string
- * @param $entity_id int
- * @param $entity_type string
- * @param $entity_subtype string
- * @param $name string
+ * Get entities ordered by a mathematical calculation on annotation values
+ *
+ * @param array $options An options array:
+ * 'calculation' => The calculation to use. Must be a valid MySQL function.
+ * Defaults to sum. Result selected as 'annotation_calculation'.
+ * Don't confuse this "calculation" option with the
+ * "annotation_calculation" option to elgg_get_annotations().
+ * This "calculation" option is applied to each entity's set of
+ * annotations and is selected as annotation_calculation for that row.
+ * See the docs for elgg_get_annotations() for proper use of the
+ * "annotation_calculation" option.
+ * 'order_by' => The order for the sorting. Defaults to 'annotation_calculation desc'.
+ * 'annotation_names' => The names of annotations on the entity.
+ * 'annotation_values' => The values of annotations on the entity.
+ *
+ * 'metadata_names' => The name of metadata on the entity.
+ * 'metadata_values' => The value of metadata on the entitiy.
+ *
+ * @return mixed If count, int. If not count, array. false on errors.
*/
-function __get_annotations_calculate_x($sum = "avg", $entity_guid, $entity_type = "", $entity_subtype = "", $name = "", $value = "", $value_type = "", $owner_guid = 0, $timelower = 0, $timeupper = 0) {
- global $CONFIG;
-
- $sum = sanitise_string($sum);
- $entity_guid = (int)$entity_guid;
- $entity_type = sanitise_string($entity_type);
- $timeupper = (int)$timeupper;
- $timelower = (int)$timelower;
-
- if ($entity_subtype) {
- if (!$entity_subtype = get_subtype_id($entity_type, $entity_subtype)) {
- // requesting a non-existing subtype: return false
- return FALSE;
- }
- }
-
- if ($name != '' AND !$name = get_metastring_id($name)) {
- return 0;
- }
-
- if ($value != '' AND !$value = get_metastring_id($value)) {
- return 0;
- }
- $value_type = sanitise_string($value_type);
- $owner_guid = (int)$owner_guid;
-
- // if (empty($name)) return 0;
-
- $where = array();
-
- if ($entity_guid) {
- $where[] = "e.guid=$entity_guid";
- }
-
- if ($entity_type!="") {
- $where[] = "e.type='$entity_type'";
- }
-
- if ($entity_subtype) {
- $where[] = "e.subtype=$entity_subtype";
- }
-
- if ($name!="") {
- $where[] = "a.name_id='$name'";
- }
-
- if ($value!="") {
- $where[] = "a.value_id='$value'";
- }
-
- if ($value_type!="") {
- $where[] = "a.value_type='$value_type'";
- }
-
- if ($owner_guid) {
- $where[] = "a.owner_guid='$owner_guid'";
- }
-
- if ($timelower) {
- $where[] = "a.time_created >= {$timelower}";
- }
-
- if ($timeupper) {
- $where[] = "a.time_created <= {$timeupper}";
- }
-
- if ($sum != "count") {
- $where[] = "a.value_type='integer'"; // Limit on integer types
- }
-
- $query = "SELECT $sum(ms.string) as sum
- FROM {$CONFIG->dbprefix}annotations a
- JOIN {$CONFIG->dbprefix}entities e on a.entity_guid = e.guid
- JOIN {$CONFIG->dbprefix}metastrings ms on a.value_id=ms.id WHERE ";
-
- foreach ($where as $w) {
- $query .= " $w and ";
- }
-
- $query .= get_access_sql_suffix("a"); // now add access
- $query .= ' and ' . get_access_sql_suffix("e"); // now add access
-
- $row = get_data_row($query);
- if ($row) {
- return $row->sum;
- }
-
- return false;
-}
-
-/**
- * Get entities ordered by a mathematical calculation
- *
- * @param $sum string
- * @param $entity_type string
- * @param $entity_subtype string
- * @param $name string
- * @param $mdname string
- * @param $mdvalue string
- * @param $limit int
- * @param string $orderdir Default: asc - the sort order
- * @return unknown
- */
-function __get_entities_from_annotations_calculate_x($sum = "sum", $entity_type = "", $entity_subtype = "", $name = "", $mdname = '', $mdvalue = '', $owner_guid = 0, $limit = 10, $offset = 0, $orderdir = 'desc', $count = false) {
- global $CONFIG;
-
- $sum = sanitise_string($sum);
- $entity_type = sanitise_string($entity_type);
-
- if ($entity_subtype) {
- if (!$entity_subtype = get_subtype_id($entity_type, $entity_subtype)) {
- // requesting a non-existing subtype: return false
- return FALSE;
- }
- }
-
- $name = get_metastring_id($name);
- $limit = (int) $limit;
- $offset = (int) $offset;
- $owner_guid = (int) $owner_guid;
- if (!empty($mdname) && !empty($mdvalue)) {
- $meta_n = get_metastring_id($mdname);
- $meta_v = get_metastring_id($mdvalue);
- }
-
- if (empty($name)) {
- return 0;
- }
-
- $where = array();
-
- if ($entity_type!="") {
- $where[] = "e.type='$entity_type'";
- }
-
- if ($owner_guid > 0) {
- $where[] = "e.container_guid = $owner_guid";
- }
-
- if ($entity_subtype) {
- $where[] = "e.subtype=$entity_subtype";
- }
+function elgg_get_entities_from_annotation_calculation($options) {
+ $db_prefix = elgg_get_config('dbprefix');
+ $defaults = array(
+ 'calculation' => 'sum',
+ 'order_by' => 'annotation_calculation desc'
+ );
- if ($name!="") {
- $where[] = "a.name_id='$name'";
- }
+ $options = array_merge($defaults, $options);
- if (!empty($mdname) && !empty($mdvalue)) {
- if ($mdname!="") {
- $where[] = "m.name_id='$meta_n'";
- }
+ $function = sanitize_string(elgg_extract('calculation', $options, 'sum', false));
- if ($mdvalue!="") {
- $where[] = "m.value_id='$meta_v'";
- }
- }
+ // you must cast this as an int or it sorts wrong.
+ $options['selects'][] = 'e.*';
+ $options['selects'][] = "$function(cast(a_msv.string as signed)) as annotation_calculation";
- if ($sum != "count") {
- // Limit on integer types
- $where[] = "a.value_type='integer'";
- }
+ // need our own join to get the values because the lower level functions don't
+ // add all the joins if it's a different callback.
+ $options['joins'][] = "JOIN {$db_prefix}metastrings a_msv ON n_table.value_id = a_msv.id";
- if (!$count) {
- $query = "SELECT distinct e.*, $sum(ms.string) as sum ";
- } else {
- $query = "SELECT count(distinct e.guid) as num, $sum(ms.string) as sum ";
- }
- $query .= " from {$CONFIG->dbprefix}entities e JOIN {$CONFIG->dbprefix}annotations a on a.entity_guid = e.guid JOIN {$CONFIG->dbprefix}metastrings ms on a.value_id=ms.id ";
+ // don't need access control because it's taken care of by elgg_get_annotations.
+ $options['group_by'] = 'n_table.entity_guid';
- if (!empty($mdname) && !empty($mdvalue)) {
- $query .= " JOIN {$CONFIG->dbprefix}metadata m on m.entity_guid = e.guid ";
- }
+ $options['callback'] = 'entity_row_to_elggstar';
- $query .= " WHERE ";
- foreach ($where as $w) {
- $query .= " $w and ";
- }
+ // see #4393
+ // @todo remove after the 'count' shortcut is removed from elgg_get_annotations()
+ $options['__egefac'] = true;
- $query .= get_access_sql_suffix("a"); // now add access
- $query .= ' and ' . get_access_sql_suffix("e"); // now add access
- if (!$count) {
- $query .= ' group by e.guid';
- }
-
- if (!$count) {
- $query .= ' order by sum ' . $orderdir;
- $query .= ' limit ' . $offset . ' , ' . $limit;
- return get_data($query, "entity_row_to_elggstar");
- } else {
- if ($row = get_data_row($query)) {
- return $row->num;
- }
- }
- return false;
+ return elgg_get_annotations($options);
}
/**
- * Returns entities ordered by the sum of an annotation
+ * List entities from an annotation calculation.
*
- * @param unknown_type $entity_type
- * @param unknown_type $entity_subtype
- * @param unknown_type $name
- * @param string $mdname
- * @param string $mdvalue
- * @param unknown_type $owner_guid
- * @param int $limit
- * @param int $offset
- * @param true|false $count
- * @return unknown
- */
-function get_entities_from_annotation_count($entity_type = "", $entity_subtype = "", $name = "", $mdname = '', $mdvalue = '', $owner_guid = 0, $limit = 10, $offset = 0, $orderdir = 'desc', $count = false) {
- return __get_entities_from_annotations_calculate_x('sum',$entity_type,$entity_subtype,$name,$mdname, $mdvalue, $owner_guid,$limit, $offset, $orderdir, $count);
-}
-
-/**
- * Lists entities by the totals of a particular kind of annotation
+ * @see elgg_get_entities_from_annotation_calculation()
*
- * @param string $entity_type Type of entity.
- * @param string $entity_subtype Subtype of entity.
- * @param string $name Name of annotation.
- * @param int $limit Maximum number of results to return.
- * @param int $owner_guid Owner.
- * @param int $group_guid Group container. Currently this is only supported if $entity_type == 'object'
- * @param boolean $asc Whether to list in ascending or descending order (default: desc)
- * @param boolean $fullview Whether to display the entities in full
- * @param boolean $viewtypetoggle Determines whether or not the 'gallery' view can be displayed (default: no)
- * @return string Formatted entity list
- */
-function list_entities_from_annotation_count($entity_type = "", $entity_subtype = "", $name = "", $limit = 10, $owner_guid = 0, $group_guid = 0, $asc = false, $fullview = true, $viewtypetoggle = false, $pagination = true, $orderdir = 'desc') {
- if ($asc) {
- $asc = "asc";
- } else {
- $asc = "desc";
- }
-
- $offset = (int) get_input("offset",0);
- $count = get_entities_from_annotation_count($entity_type, $entity_subtype, $name, '', '', $owner_guid, $limit, $offset, $orderdir, true);
- $entities = get_entities_from_annotation_count($entity_type, $entity_subtype, $name, '', '', $owner_guid, $limit, $offset, $orderdir, false);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
-}
-
-/**
- * Lists entities by the totals of a particular kind of annotation AND the value of a piece of metadata
+ * @param array $options An options array.
*
- * @param string $entity_type Type of entity.
- * @param string $entity_subtype Subtype of entity.
- * @param string $name Name of annotation.
- * @param string $mdname Metadata name
- * @param string $mdvalue Metadata value
- * @param int $limit Maximum number of results to return.
- * @param int $owner_guid Owner.
- * @param int $group_guid Group container. Currently this is only supported if $entity_type == 'object'
- * @param boolean $asc Whether to list in ascending or descending order (default: desc)
- * @param boolean $fullview Whether to display the entities in full
- * @param boolean $viewtypetoggle Determines whether or not the 'gallery' view can be displayed (default: no)
- * @return string Formatted entity list
+ * @return string
*/
-function list_entities_from_annotation_count_by_metadata($entity_type = "", $entity_subtype = "", $name = "", $mdname = '', $mdvalue = '', $limit = 10, $owner_guid = 0, $group_guid = 0, $asc = false, $fullview = true, $viewtypetoggle = false, $pagination = true, $orderdir = 'desc') {
- if ($asc) {
- $asc = "asc";
- } else {
- $asc = "desc";
- }
-
- $offset = (int) get_input("offset",0);
- $count = get_entities_from_annotation_count($entity_type, $entity_subtype, $name, $mdname, $mdvalue, $owner_guid, $limit, $offset, $orderdir, true);
- $entities = get_entities_from_annotation_count($entity_type, $entity_subtype, $name, $mdname, $mdvalue, $owner_guid, $limit, $offset, $orderdir, false);
+function elgg_list_entities_from_annotation_calculation($options) {
+ $defaults = array(
+ 'calculation' => 'sum',
+ 'order_by' => 'annotation_calculation desc'
+ );
+ $options = array_merge($defaults, $options);
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
+ return elgg_list_entities($options, 'elgg_get_entities_from_annotation_calculation');
}
/**
- * Delete a given annotation.
+ * Export the annotations for the specified entity
*
- * @param $id int The id
- */
-function delete_annotation($id) {
- global $CONFIG;
-
- $id = (int)$id;
-
- $access = get_access_sql_suffix();
- $annotation = get_annotation($id);
-
- if (trigger_elgg_event('delete', 'annotation', $annotation)) {
- remove_from_river_by_annotation($id);
- return delete_data("DELETE from {$CONFIG->dbprefix}annotations where id=$id and $access");
- }
-
- return FALSE;
-}
-
-/**
- * Clear all the annotations for a given entity, assuming you have access to that metadata.
+ * @param string $hook 'export'
+ * @param string $type 'all'
+ * @param mixed $returnvalue Default return value
+ * @param mixed $params Parameters determining what annotations to export
*
- * @param int $guid
- * @return number of annotations deleted or false if an error
- */
-function clear_annotations($guid, $name = "") {
- global $CONFIG;
-
- $guid = (int)$guid;
-
- if (!empty($name)) {
- $name = get_metastring_id($name);
- if ($name === false) {
- // name doesn't exist so 0 rows were deleted
- return 0;
- }
- }
-
- $entity_guid = (int) $guid;
- if ($entity = get_entity($entity_guid)) {
- if ($entity->canEdit()) {
- $where = array();
-
- if ($name != "") {
- $where[] = " name_id='$name'";
- }
-
- $query = "DELETE from {$CONFIG->dbprefix}annotations where entity_guid=$guid ";
- foreach ($where as $w) {
- $query .= " and $w";
- }
-
- return delete_data($query);
- }
- }
-
- return FALSE;
-}
-
-/**
- * Clear all annotations belonging to a given owner_guid
+ * @elgg_plugin_hook export all
*
- * @param int $owner_guid The owner
- */
-function clear_annotations_by_owner($owner_guid) {
- global $CONFIG;
-
- $owner_guid = (int)$owner_guid;
-
- $annotations = get_data("SELECT id from {$CONFIG->dbprefix}annotations WHERE owner_guid=$owner_guid");
- $deleted = 0;
-
- if (!$annotations) {
- return 0;
- }
-
- foreach ($annotations as $id) {
- // Is this the best way?
- if (delete_annotation($id->id)) {
- $deleted++;
- }
- }
-
- return $deleted;
-}
-
-/**
- * Handler called by trigger_plugin_hook on the "export" event.
+ * @return array
+ * @throws InvalidParameterException
+ * @access private
*/
-function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $params) {
+function export_annotation_plugin_hook($hook, $type, $returnvalue, $params) {
// Sanity check values
if ((!is_array($params)) && (!isset($params['guid']))) {
throw new InvalidParameterException(elgg_echo('InvalidParameterException:GUIDNotForExport'));
@@ -1106,9 +495,12 @@ function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $param
}
$guid = (int)$params['guid'];
- $name = $params['name'];
+ $options = array('guid' => $guid, 'limit' => 0);
+ if (isset($params['name'])) {
+ $options['annotation_name'] = $params['name'];
+ }
- $result = get_annotations($guid);
+ $result = elgg_get_annotations($options);
if ($result) {
foreach ($result as $r) {
@@ -1120,14 +512,17 @@ function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $param
}
/**
- * Get the URL for this item of metadata, by default this links to the export handler in the current view.
+ * Get the URL for this item of metadata, by default this links to the
+ * export handler in the current view.
+ *
+ * @param int $id Annotation id
*
- * @param int $id
+ * @return mixed
*/
function get_annotation_url($id) {
$id = (int)$id;
- if ($extender = get_annotation($id)) {
+ if ($extender = elgg_get_annotation_from_id($id)) {
return get_extender_url($extender);
}
return false;
@@ -1136,28 +531,30 @@ function get_annotation_url($id) {
/**
* Check to see if a user has already created an annotation on an object
*
- * @param int $entity_guid
- * @param string $annotation_type
- * @param int $owner_guid Defaults to logged in user.
+ * @param int $entity_guid Entity guid
+ * @param string $annotation_type Type of annotation
+ * @param int $owner_guid Defaults to logged in user.
*
- * @return true | false
+ * @return bool
+ * @since 1.8.0
*/
function elgg_annotation_exists($entity_guid, $annotation_type, $owner_guid = NULL) {
global $CONFIG;
- if (!$owner_guid && !($owner_guid = get_loggedin_userid())) {
+ if (!$owner_guid && !($owner_guid = elgg_get_logged_in_user_guid())) {
return FALSE;
}
- $entity_guid = (int)$entity_guid;
- $annotation_type = sanitise_string($annotation_type);
+ $entity_guid = sanitize_int($entity_guid);
+ $owner_guid = sanitize_int($owner_guid);
+ $annotation_type = sanitize_string($annotation_type);
- $sql = "select a.id" .
- " FROM {$CONFIG->dbprefix}annotations a, {$CONFIG->dbprefix}metastrings m " .
- " WHERE a.owner_guid={$owner_guid} AND a.entity_guid={$entity_guid} " .
- " AND a.name_id=m.id AND m.string='{$annotation_type}'";
+ $sql = "SELECT a.id FROM {$CONFIG->dbprefix}annotations a" .
+ " JOIN {$CONFIG->dbprefix}metastrings m ON a.name_id = m.id" .
+ " WHERE a.owner_guid = $owner_guid AND a.entity_guid = $entity_guid" .
+ " AND m.string = '$annotation_type'";
- if ($check_annotation = get_data_row($sql)) {
+ if (get_data_row($sql)) {
return TRUE;
}
@@ -1165,14 +562,57 @@ function elgg_annotation_exists($entity_guid, $annotation_type, $owner_guid = NU
}
/**
+ * Return the URL for a comment
+ *
+ * @param ElggAnnotation $comment The comment object
+ * @return string
+ * @access private
+ */
+function elgg_comment_url_handler(ElggAnnotation $comment) {
+ $entity = $comment->getEntity();
+ if ($entity) {
+ return $entity->getURL() . '#item-annotation-' . $comment->id;
+ }
+ return "";
+}
+
+/**
* Register an annotation url handler.
*
- * @param string $function_name The function.
* @param string $extender_name The name, default 'all'.
+ * @param string $function_name The function.
+ *
+ * @return string
*/
-function register_annotation_url_handler($function_name, $extender_name = "all") {
- return register_extender_url_handler($function_name, 'annotation', $extender_name);
+function elgg_register_annotation_url_handler($extender_name = "all", $function_name) {
+ return elgg_register_extender_url_handler('annotation', $extender_name, $function_name);
+}
+
+/**
+ * Register annotation unit tests
+ *
+ * @param string $hook
+ * @param string $type
+ * @param array $value
+ * @param array $params
+ * @return array
+ * @access private
+ */
+function annotations_test($hook, $type, $value, $params) {
+ global $CONFIG;
+ $value[] = $CONFIG->path . 'engine/tests/api/annotations.php';
+ return $value;
+}
+
+/**
+ * Initialize the annotation library
+ * @access private
+ */
+function elgg_annotations_init() {
+ elgg_register_annotation_url_handler('generic_comment', 'elgg_comment_url_handler');
+
+ elgg_register_plugin_hook_handler("export", "all", "export_annotation_plugin_hook", 2);
+ elgg_register_plugin_hook_handler('unit_test', 'system', 'annotations_test');
}
-/** Register the hook */
-register_plugin_hook("export", "all", "export_annotation_plugin_hook", 2);
+elgg_register_event_handler('init', 'system', 'elgg_annotations_init');
diff --git a/engine/lib/cache.php b/engine/lib/cache.php
index df7b1e525..3116c1a9b 100644
--- a/engine/lib/cache.php
+++ b/engine/lib/cache.php
@@ -3,435 +3,451 @@
* Elgg cache
* Cache file interface for caching data.
*
- * @package Elgg
- * @subpackage API
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Cache
*/
+/* Filepath Cache */
+
/**
- * ElggCache The elgg cache superclass.
- * This defines the interface for a cache (wherever that cache is stored).
+ * Returns an ElggCache object suitable for caching system information
+ *
+ * @todo Can this be done in a cleaner way?
+ * @todo Swap to memcache etc?
*
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage API
+ * @return ElggFileCache
*/
-abstract class ElggCache implements
- // Override for array access
- ArrayAccess {
- /**
- * Variables for the cache object.
- *
- * @var array
- */
- private $variables;
+function elgg_get_system_cache() {
+ global $CONFIG;
/**
- * Set the constructor.
+ * A default filestore cache using the dataroot.
*/
- function __construct() {
- $this->variables = array();
+ static $FILE_PATH_CACHE;
+
+ if (!$FILE_PATH_CACHE) {
+ $FILE_PATH_CACHE = new ElggFileCache($CONFIG->dataroot . 'system_cache/');
}
- /**
- * Set a cache variable.
- *
- * @param string $variable
- * @param string $value
- */
- public function set_variable($variable, $value) {
- if (!is_array($this->variables)) {
- $this->variables = array();
- }
+ return $FILE_PATH_CACHE;
+}
- $this->variables[$variable] = $value;
- }
+/**
+ * Reset the system cache by deleting the caches
+ *
+ * @return void
+ */
+function elgg_reset_system_cache() {
+ $cache = elgg_get_system_cache();
+ $cache->clear();
+}
- /**
- * Get variables for this cache.
- *
- * @param string $variable
- * @return mixed The variable or null;
- */
- public function get_variable($variable) {
- if (isset($this->variables[$variable])) {
- return $this->variables[$variable];
- }
+/**
+ * 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_save_system_cache($type, $data) {
+ global $CONFIG;
- return null;
+ if ($CONFIG->system_cache_enabled) {
+ $cache = elgg_get_system_cache();
+ return $cache->save($type, $data);
}
- /**
- * Class member get overloading, returning key using $this->load defaults.
- *
- * @param string $key
- * @return mixed
- */
- function __get($key) {
- return $this->load($key);
- }
+ return false;
+}
- /**
- * Class member set overloading, setting a key using $this->save defaults.
- *
- * @param string $key
- * @param mixed $value
- * @return mixed
- */
- function __set($key, $value) {
- return $this->save($key, $value);
- }
+/**
+ * Retrieve the contents of a system cache.
+ *
+ * @param string $type The type of cache to load
+ * @return string
+ */
+function elgg_load_system_cache($type) {
+ global $CONFIG;
- /**
- * Supporting isset, using $this->load() with default values.
- *
- * @param string $key The name of the attribute or metadata.
- * @return bool
- */
- function __isset($key) {
- return (bool)$this->load($key);
- }
+ if ($CONFIG->system_cache_enabled) {
+ $cache = elgg_get_system_cache();
+ $cached_data = $cache->load($type);
- /**
- * Supporting unsetting of magic attributes.
- *
- * @param string $key The name of the attribute or metadata.
- */
- function __unset($key) {
- return $this->delete($key);
+ if ($cached_data) {
+ return $cached_data;
+ }
}
- /**
- * Save data in a cache.
- *
- * @param string $key
- * @param string $data
- * @return bool
- */
- abstract public function save($key, $data);
+ return NULL;
+}
- /**
- * Load data from the cache using a given key.
- *
- * @param string $key
- * @param int $offset
- * @param int $limit
- * @return mixed The stored data or false.
- */
- abstract public function load($key, $offset = 0, $limit = null);
+/**
+ * Enables the system disk cache.
+ *
+ * Uses the 'system_cache_enabled' datalist with a boolean value.
+ * Resets the system cache.
+ *
+ * @return void
+ */
+function elgg_enable_system_cache() {
+ global $CONFIG;
- /**
- * Invalidate a key
- *
- * @param string $key
- * @return bool
- */
- abstract public function delete($key);
+ datalist_set('system_cache_enabled', 1);
+ $CONFIG->system_cache_enabled = 1;
+ elgg_reset_system_cache();
+}
- /**
- * Clear out all the contents of the cache.
- *
- */
- abstract public function clear();
+/**
+ * Disables the system disk cache.
+ *
+ * Uses the 'system_cache_enabled' datalist with a boolean value.
+ * Resets the system cache.
+ *
+ * @return void
+ */
+function elgg_disable_system_cache() {
+ global $CONFIG;
- /**
- * Add a key only if it doesn't already exist.
- * Implemented simply here, if you extend this class and your caching engine provides a better way then
- * override this accordingly.
- *
- * @param string $key
- * @param string $data
- * @return bool
- */
- public function add($key, $data) {
- if (!isset($this[$key])) {
- return $this->save($key, $data);
- }
+ datalist_set('system_cache_enabled', 0);
+ $CONFIG->system_cache_enabled = 0;
+ elgg_reset_system_cache();
+}
- return false;
- }
+/** @todo deprecate in Elgg 1.9 **/
- // ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
- function offsetSet($key, $value) {
- $this->save($key, $value);
- }
+/**
+ * @access private
+ */
+function elgg_get_filepath_cache() {
+ return elgg_get_system_cache();
+}
+/**
+ * @access private
+ */
+function elgg_filepath_cache_reset() {
+ 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() {
+ elgg_enable_system_cache();
+}
+/**
+ * @access private
+ */
+function elgg_disable_filepath_cache() {
+ elgg_disable_system_cache();
+}
- function offsetGet($key) {
- return $this->load($key);
- }
+/* Simplecache */
- function offsetUnset($key) {
- if ( isset($this->key) ) {
- unset($this->key);
- }
+/**
+ * Registers a view to simple cache.
+ *
+ * Simple cache is a caching mechanism that saves the output of
+ * views and its extensions into a file. If the view is called
+ * by the {@link simplecache/view.php} file, the Elgg framework will
+ * not be loaded and the contents of the view will returned
+ * from file.
+ *
+ * @warning Simple cached views must take no parameters and return
+ * the same content no matter who is logged in.
+ *
+ * @example
+ * $blog_js = elgg_get_simplecache_url('js', 'blog/save_draft');
+ * elgg_register_simplecache_view('js/blog/save_draft');
+ * elgg_register_js('elgg.blog', $blog_js);
+ * elgg_load_js('elgg.blog');
+ *
+ * @param string $viewname View name
+ *
+ * @return void
+ * @link http://docs.elgg.org/Views/Simplecache
+ * @see elgg_regenerate_simplecache()
+ * @since 1.8.0
+ */
+function elgg_register_simplecache_view($viewname) {
+ global $CONFIG;
+
+ if (!isset($CONFIG->views)) {
+ $CONFIG->views = new stdClass;
}
- function offsetExists($offset) {
- return isset($this->$offset);
+ if (!isset($CONFIG->views->simplecache)) {
+ $CONFIG->views->simplecache = array();
}
+
+ $CONFIG->views->simplecache[] = $viewname;
}
/**
- * Shared memory cache description.
- * Extends ElggCache with functions useful to shared memory style caches (static variables, memcache etc)
+ * Get the URL for the cached file
+ *
+ * @warning You must register the view with elgg_register_simplecache_view()
+ * for caching to work. See elgg_register_simplecache_view() for a full example.
+ *
+ * @param string $type The file type: css or js
+ * @param string $view The view name
+ * @return string
+ * @since 1.8.0
*/
-abstract class ElggSharedMemoryCache extends ElggCache {
- /**
- * Namespace variable used to keep various bits of the cache
- * separate.
- *
- * @var string
- */
- private $namespace;
-
- /**
- * Set the namespace of this cache.
- * This is useful for cache types (like memcache or static variables) where there is one large
- * flat area of memory shared across all instances of the cache.
- *
- * @param string $namespace
- */
- public function setNamespace($namespace = "default") {
- $this->namespace = $namespace;
- }
-
- /**
- * Get the namespace currently defined.
- *
- * @return string
- */
- public function getNamespace() {
- return $this->namespace;
+function elgg_get_simplecache_url($type, $view) {
+ global $CONFIG;
+ $lastcache = (int)$CONFIG->lastcache;
+ $viewtype = elgg_get_viewtype();
+ elgg_register_simplecache_view("$type/$view");// see #5302
+ if (elgg_is_simplecache_enabled()) {
+ $url = elgg_get_site_url() . "cache/$type/$viewtype/$view.$lastcache.$type";
+ } else {
+ $url = elgg_get_site_url() . "$type/$view.$lastcache.$type";
+ $elements = array("view" => $viewtype);
+ $url = elgg_http_add_url_query_elements($url, $elements);
}
+
+ return $url;
}
/**
- * ElggStaticVariableCache
- * Dummy cache which stores values in a static array. Using this makes future replacements to other caching back
- * ends (eg memcache) much easier.
+ * Regenerates the simple cache.
+ *
+ * @warning This does not invalidate the cache, but actively rebuilds it.
+ *
+ * @param string $viewtype Optional viewtype to regenerate. Defaults to all valid viewtypes.
*
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage API
+ * @return void
+ * @see elgg_register_simplecache_view()
+ * @since 1.8.0
*/
-class ElggStaticVariableCache extends ElggSharedMemoryCache {
- /**
- * The cache.
- *
- * @var unknown_type
- */
- private static $__cache;
+function elgg_regenerate_simplecache($viewtype = NULL) {
+ global $CONFIG;
- /**
- * Create the variable cache.
- *
- * This function creates a variable cache in a static variable in memory, optionally with a given namespace (to avoid overlap).
- *
- * @param string $namespace The namespace for this cache to write to - note, namespaces of the same name are shared!
- */
- function __construct($namespace = 'default') {
- $this->setNamespace($namespace);
- $this->clear();
+ if (!isset($CONFIG->views->simplecache) || !is_array($CONFIG->views->simplecache)) {
+ return;
}
- public function save($key, $data) {
- $namespace = $this->getNamespace();
+ $lastcached = time();
- ElggStaticVariableCache::$__cache[$namespace][$key] = $data;
+ // @todo elgg_view() checks if the page set is done (isset($CONFIG->pagesetupdone)) and
+ // triggers an event if it's not. Calling elgg_view() here breaks submenus
+ // (at least) because the page setup hook is called before any
+ // contexts can be correctly set (since this is called before page_handler()).
+ // To avoid this, lie about $CONFIG->pagehandlerdone to force
+ // the trigger correctly when the first view is actually being output.
+ $CONFIG->pagesetupdone = TRUE;
- return true;
+ if (!file_exists($CONFIG->dataroot . 'views_simplecache')) {
+ mkdir($CONFIG->dataroot . 'views_simplecache');
}
- public function load($key, $offset = 0, $limit = null) {
- $namespace = $this->getNamespace();
+ if (isset($viewtype)) {
+ $viewtypes = array($viewtype);
+ } else {
+ $viewtypes = $CONFIG->view_types;
+ }
+
+ $original_viewtype = elgg_get_viewtype();
+
+ // disable error reporting so we don't cache problems
+ $old_debug = elgg_get_config('debug');
+ elgg_set_config('debug', null);
- if (isset(ElggStaticVariableCache::$__cache[$namespace][$key])) {
- return ElggStaticVariableCache::$__cache[$namespace][$key];
+ foreach ($viewtypes as $viewtype) {
+ elgg_set_viewtype($viewtype);
+ foreach ($CONFIG->views->simplecache as $view) {
+ $viewcontents = elgg_view($view);
+ $viewname = md5(elgg_get_viewtype() . $view);
+ if ($handle = fopen($CONFIG->dataroot . 'views_simplecache/' . $viewname, 'w')) {
+ fwrite($handle, $viewcontents);
+ fclose($handle);
+ }
}
- return false;
+ datalist_set("simplecache_lastupdate_$viewtype", $lastcached);
+ datalist_set("simplecache_lastcached_$viewtype", $lastcached);
}
- public function delete($key) {
- $namespace = $this->getNamespace();
+ elgg_set_config('debug', $old_debug);
+ elgg_set_viewtype($original_viewtype);
+
+ // needs to be set for links in html head
+ $CONFIG->lastcache = $lastcached;
- unset(ElggStaticVariableCache::$__cache[$namespace][$key]);
+ unset($CONFIG->pagesetupdone);
+}
+/**
+ * Is simple cache enabled
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+function elgg_is_simplecache_enabled() {
+ if (elgg_get_config('simplecache_enabled')) {
return true;
}
- public function clear() {
- $namespace = $this->getNamespace();
+ return false;
+}
- if (!isset(ElggStaticVariableCache::$__cache)) {
- ElggStaticVariableCache::$__cache = array();
- }
+/**
+ * Enables the simple cache.
+ *
+ * @access private
+ * @see elgg_register_simplecache_view()
+ * @return void
+ * @since 1.8.0
+ */
+function elgg_enable_simplecache() {
+ global $CONFIG;
- ElggStaticVariableCache::$__cache[$namespace] = array();
- }
+ datalist_set('simplecache_enabled', 1);
+ $CONFIG->simplecache_enabled = 1;
+ elgg_regenerate_simplecache();
}
/**
- * ElggFileCache
- * Store cached data in a file store.
+ * Disables the simple cache.
+ *
+ * @warning Simplecache is also purged when disabled.
*
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage API
+ * @access private
+ * @see elgg_register_simplecache_view()
+ * @return void
+ * @since 1.8.0
*/
-class ElggFileCache extends ElggCache {
- /**
- * Set the Elgg cache.
- *
- * @param string $cache_path The cache path.
- * @param int $max_age Maximum age in seconds, 0 if no limit.
- * @param int $max_size Maximum size of cache in seconds, 0 if no limit.
- */
- function __construct($cache_path, $max_age = 0, $max_size = 0) {
- $this->set_variable("cache_path", $cache_path);
- $this->set_variable("max_age", $max_age);
- $this->set_variable("max_size", $max_size);
-
- if ($cache_path=="") {
- throw new ConfigurationException(elgg_echo('ConfigurationException:NoCachePath'));
+function elgg_disable_simplecache() {
+ global $CONFIG;
+ if ($CONFIG->simplecache_enabled) {
+ datalist_set('simplecache_enabled', 0);
+ $CONFIG->simplecache_enabled = 0;
+
+ // purge simple cache
+ if ($handle = opendir($CONFIG->dataroot . 'views_simplecache')) {
+ while (false !== ($file = readdir($handle))) {
+ if ($file != "." && $file != "..") {
+ unlink($CONFIG->dataroot . 'views_simplecache/' . $file);
+ }
+ }
+ closedir($handle);
}
}
+}
- /**
- * Create and return a handle to a file.
- *
- * @param string $filename
- * @param string $rw
- */
- protected function create_file($filename, $rw = "rb") {
- // Create a filename matrix
- $matrix = "";
- $depth = strlen($filename);
- if ($depth > 5) {
- $depth = 5;
- }
-
- // Create full path
- $path = $this->get_variable("cache_path") . $matrix;
- if (!is_dir($path)) {
- mkdir($path, 0700, true);
- }
-
- // Open the file
- if ((!file_exists($path . $filename)) && ($rw=="rb")) {
- return false;
- }
+/**
+ * Deletes all cached views in the simplecache and sets the lastcache and
+ * lastupdate time to 0 for every valid viewtype.
+ *
+ * @return bool
+ * @since 1.7.4
+ */
+function elgg_invalidate_simplecache() {
+ global $CONFIG;
- return fopen($path . $filename, $rw);
+ if (!isset($CONFIG->views->simplecache) || !is_array($CONFIG->views->simplecache)) {
+ return false;
}
- /**
- * Create a sanitised filename for the file.
- *
- * @param string $filename
- */
- protected function sanitise_filename($filename) {
- // TODO : Writeme
+ $handle = opendir($CONFIG->dataroot . 'views_simplecache');
- return $filename;
+ if (!$handle) {
+ return false;
}
- /**
- * Save a key
- *
- * @param string $key
- * @param string $data
- * @return boolean
- */
- public function save($key, $data) {
- $f = $this->create_file($this->sanitise_filename($key), "wb");
- if ($f) {
- $result = fwrite($f, $data);
- fclose($f);
-
- return $result;
+ // remove files.
+ $return = true;
+ while (false !== ($file = readdir($handle))) {
+ if ($file != "." && $file != "..") {
+ $return &= unlink($CONFIG->dataroot . 'views_simplecache/' . $file);
}
-
- return false;
}
+ closedir($handle);
- /**
- * Load a key
- *
- * @param string $key
- * @param int $offset
- * @param int $limit
- * @return string
- */
- public function load($key, $offset = 0, $limit = null) {
- $f = $this->create_file($this->sanitise_filename($key));
- if ($f) {
- //fseek($f, $offset);
- if (!$limit) {
- $limit = -1;
- }
- $data = stream_get_contents($f, $limit, $offset);
-
- fclose($f);
-
- return $data;
- }
+ // reset cache times
+ $viewtypes = $CONFIG->view_types;
+ if (!is_array($viewtypes)) {
return false;
}
- /**
- * Invalidate a given key.
- *
- * @param string $key
- * @return bool
- */
- public function delete($key) {
- $dir = $this->get_variable("cache_path");
-
- if (file_exists($dir.$key)) {
- return unlink($dir.$key);
- }
- return TRUE;
- }
-
- public function clear() {
- // TODO : writeme
+ foreach ($viewtypes as $viewtype) {
+ $return &= datalist_set("simplecache_lastupdate_$viewtype", 0);
+ $return &= datalist_set("simplecache_lastcached_$viewtype", 0);
}
- public function __destruct() {
- // TODO: Check size and age, clean up accordingly
- $size = 0;
- $dir = $this->get_variable("cache_path");
+ return $return;
+}
- // Short circuit if both size and age are unlimited
- if (($this->get_variable("max_age")==0) && ($this->get_variable("max_size")==0)) {
- return;
- }
+/**
+ * @see elgg_reset_system_cache()
+ * @access private
+ */
+function _elgg_load_cache() {
+ global $CONFIG;
- $exclude = array(".","..");
+ $CONFIG->system_cache_loaded = false;
- $files = scandir($dir);
- if (!$files) {
- throw new IOException(sprintf(elgg_echo('IOException:NotDirectory'), $dir));
- }
+ $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);
- // Perform cleanup
- foreach ($files as $f) {
- if (!in_array($f, $exclude)) {
- $stat = stat($dir.$f);
+ $CONFIG->system_cache_loaded = true;
+}
- // Add size
- $size .= $stat['size'];
+/**
+ * @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;
+ }
- // Is this older than my maximum date?
- if (($this->get_variable("max_age")>0) && (time() - $stat['mtime'] > $this->get_variable("max_age"))) {
- unlink($dir.$f);
- }
+ // 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));
+ }
- // TODO: Size
- }
+ if ($CONFIG->system_cache_enabled && !$CONFIG->i18n_loaded_from_cache) {
+ reload_all_translations();
+ foreach ($CONFIG->translations as $lang => $map) {
+ elgg_save_system_cache("$lang.lang", serialize($map));
}
}
}
+
+elgg_register_event_handler('ready', 'system', '_elgg_cache_init');
diff --git a/engine/lib/calendar.php b/engine/lib/calendar.php
index a9d6dfadf..e6f95934c 100644
--- a/engine/lib/calendar.php
+++ b/engine/lib/calendar.php
@@ -2,45 +2,21 @@
/**
* Elgg calendar / entity / event functions.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-
-/**
- * Calendar interface for events.
+ * @package Elgg.Core
+ * @subpackage Calendar
*
+ * @todo Implement or remove
*/
-interface Notable {
- /**
- * Calendar functionality.
- * This function sets the time of an object on a calendar listing.
- *
- * @param int $hour If ommitted, now is assumed.
- * @param int $minute If ommitted, now is assumed.
- * @param int $second If ommitted, now is assumed.
- * @param int $day If ommitted, now is assumed.
- * @param int $month If ommitted, now is assumed.
- * @param int $year If ommitted, now is assumed.
- * @param int $duration Duration of event, remainder of the day is assumed.
- */
- public function setCalendarTimeAndDuration($hour = NULL, $minute = NULL, $second = NULL, $day = NULL, $month = NULL, $year = NULL, $duration = NULL);
-
- /**
- * Return the start timestamp.
- */
- public function getCalendarStartTime();
-
- /**
- * Return the end timestamp.
- */
- public function getCalendarEndTime();
-}
/**
* 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);
@@ -49,6 +25,12 @@ function get_day_start($day = null, $month = null, $year = null) {
/**
* 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);
@@ -57,19 +39,26 @@ function get_day_end($day = null, $month = null, $year = null) {
/**
* 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 rather than the entities themselves (limits and offsets don't apply in this context). Defaults to false.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @param int|array $container_guid The container or containers to get entities from (default: all containers).
+ * @todo this function also accepts an array(type => subtypes) for 3rd arg. Should we document this?
+ *
+ * @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) {
+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) {
@@ -91,15 +80,17 @@ function get_notable_entities($start_time, $end_time, $type = "", $subtype = "",
if (is_array($type)) {
$tempwhere = "";
if (sizeof($type)) {
- foreach($type as $typekey => $subtypearray) {
- foreach($subtypearray as $subtypeval) {
+ 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 ";
+ if (!empty($tempwhere)) {
+ $tempwhere .= " or ";
+ }
$tempwhere .= "(e.type = '{$typekey}' and e.subtype = {$subtypeval})";
}
}
@@ -115,7 +106,7 @@ function get_notable_entities($start_time, $end_time, $type = "", $subtype = "",
$where[] = "e.type='$type'";
}
- if ($subtype!=="") {
+ if ($subtype !== "") {
$where[] = "e.subtype=$subtype";
}
}
@@ -128,8 +119,8 @@ function get_notable_entities($start_time, $end_time, $type = "", $subtype = "",
} 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})" ; //
+ $owner_guid = implode(",", $owner_guid);
+ $where[] = "e.owner_guid in ({$owner_guid})";
}
if (is_null($container_guid)) {
$container_guid = $owner_array;
@@ -142,8 +133,10 @@ function get_notable_entities($start_time, $end_time, $type = "", $subtype = "",
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) . ")";
+ 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}";
@@ -195,21 +188,26 @@ function get_notable_entities($start_time, $end_time, $type = "", $subtype = "",
/**
* 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
- * @param mixed $meta_value
- * @param string $entity_type The type of entity to look for, eg 'site' or 'object'
+ * @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 $limit
- * @param int $offset
- * @param string $order_by Optional ordering.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @param true|false $count If set to true, returns the total number of entities rather than a list. (Default: false)
+ * @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) {
+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);
@@ -227,7 +225,7 @@ function get_notable_entities_from_metadata($start_time, $end_time, $meta_name,
$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) {
+ foreach ($owner_guid as $key => $guid) {
$owner_guid[$key] = (int) $guid;
}
} else {
@@ -242,7 +240,7 @@ function get_notable_entities_from_metadata($start_time, $end_time, $meta_name,
$where = array();
- if ($entity_type!="") {
+ if ($entity_type != "") {
$where[] = "e.type='$entity_type'";
}
@@ -250,11 +248,11 @@ function get_notable_entities_from_metadata($start_time, $end_time, $meta_name,
$where[] = "e.subtype=$entity_subtype";
}
- if ($meta_name!="") {
+ if ($meta_name != "") {
$where[] = "m.name_id='$meta_n'";
}
- if ($meta_value!="") {
+ if ($meta_value != "") {
$where[] = "m.value_id='$meta_v'";
}
@@ -263,7 +261,7 @@ function get_notable_entities_from_metadata($start_time, $end_time, $meta_name,
}
if (is_array($owner_guid)) {
- $where[] = "e.container_guid in (".implode(",",$owner_guid).")";
+ $where[] = "e.container_guid in (" . implode(",", $owner_guid) . ")";
} else if ($owner_guid > 0) {
$where[] = "e.container_guid = {$owner_guid}";
}
@@ -290,7 +288,9 @@ function get_notable_entities_from_metadata($start_time, $end_time, $meta_name,
$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";
+ $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 ";
}
@@ -315,22 +315,29 @@ function get_notable_entities_from_metadata($start_time, $end_time, $meta_name,
/**
* 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 instead say "give me all entities for whome $relationship_guid is a $relationship of"
- * @param string $type
- * @param string $subtype
- * @param int $owner_guid
- * @param string $order_by
- * @param int $limit
- * @param int $offset
- * @param boolean $count Set to true if you want to count the number of entities instead (default false)
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
+ * @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) {
+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;
@@ -356,11 +363,12 @@ function get_noteable_entities_from_relationship($start_time, $end_time, $relati
$where = array();
- if ($relationship!="") {
+ if ($relationship != "") {
$where[] = "r.relationship='$relationship'";
}
if ($relationship_guid) {
- $where[] = ($inverse_relationship ? "r.guid_two='$relationship_guid'" : "r.guid_one='$relationship_guid'");
+ $where[] = $inverse_relationship ?
+ "r.guid_two='$relationship_guid'" : "r.guid_one='$relationship_guid'";
}
if ($type != "") {
$where[] = "e.type='$type'";
@@ -401,7 +409,9 @@ function get_noteable_entities_from_relationship($start_time, $end_time, $relati
} else {
$query = "SELECT distinct e.* ";
}
- $query .= " from {$CONFIG->dbprefix}entity_relationships r JOIN {$CONFIG->dbprefix}entities e on $joinon $cal_join where ";
+ $query .= " from {$CONFIG->dbprefix}entity_relationships r"
+ . " JOIN {$CONFIG->dbprefix}entities e on $joinon $cal_join where ";
+
foreach ($where as $w) {
$query .= " $w and ";
}
@@ -421,66 +431,87 @@ function get_noteable_entities_from_relationship($start_time, $end_time, $relati
/**
* 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 Set to true to get a count rather than the entities themselves (limits and offsets don't apply in this context). Defaults to false.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @param int|array $container_guid The container or containers to get entities from (default: all containers).
+ * @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) {
+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);
+ 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
- * @param mixed $meta_value
- * @param string $entity_type The type of entity to look for, eg 'site' or 'object'
+ * @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 $limit
- * @param int $offset
- * @param string $order_by Optional ordering.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @param true|false $count If set to true, returns the total number of entities rather than a list. (Default: false)
+ * @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) {
+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);
+ 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 instead say "give me all entities for whome $relationship_guid is a $relationship of"
- * @param string $type
- * @param string $subtype
- * @param int $owner_guid
- * @param string $order_by
- * @param int $limit
- * @param int $offset
- * @param boolean $count Set to true if you want to count the number of entities instead (default false)
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
+ * @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) {
+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);
+ 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);
}
/**
@@ -488,23 +519,31 @@ function get_todays_entities_from_relationship($relationship, $relationship_guid
*
* @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 display per page (default: 10)
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow gallery view
- * @param true|false $pagination Display pagination? Default: true
+ * @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, $viewtypetoggle = false, $navigation = true) {
+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);
+ $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, $viewtypetoggle, $navigation);
+ return elgg_view_entity_list($entities, $count, $offset, $limit,
+ $fullview, $listtypetoggle, $navigation);
}
/**
@@ -512,18 +551,23 @@ function list_notable_entities($start_time, $end_time, $type= "", $subtype = "",
*
* @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 display per page (default: 10)
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow gallery view
- * @param true|false $pagination Display pagination? Default: true
+ * @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, $viewtypetoggle = false, $navigation = true) {
+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, $viewtypetoggle, $navigation);
-} \ No newline at end of file
+ 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 7976f8d8b..55e5bbd36 100644
--- a/engine/lib/configuration.php
+++ b/engine/lib/configuration.php
@@ -1,45 +1,431 @@
<?php
/**
- * Elgg configuration library
- * Contains functions for managing system configuration
+ * Elgg configuration procedural code.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * Includes functions for manipulating the configuration values stored in the database
+ * 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
+ * - dbprefix_datalists
+ * - engine/settings.php (See {@link settings.example.php})
+ *
+ * Upon system boot, all values in dbprefix_config are read into $CONFIG.
+ *
+ * @package Elgg.Core
+ * @subpackage Configuration
+ */
+
+/**
+ * Get the URL for the current (or specified) site
+ *
+ * @param int $site_guid The GUID of the site whose URL we want to grab
+ * @return string
+ * @since 1.8.0
+ */
+function elgg_get_site_url($site_guid = 0) {
+ if ($site_guid == 0) {
+ global $CONFIG;
+ return $CONFIG->wwwroot;
+ }
+
+ $site = get_entity($site_guid);
+
+ if (!$site instanceof ElggSite) {
+ return false;
+ }
+ /* @var ElggSite $site */
+
+ return $site->url;
+}
+
+/**
+ * Get the plugin path for this installation
+ *
+ * @return string
+ * @since 1.8.0
+ */
+function elgg_get_plugins_path() {
+ global $CONFIG;
+ return $CONFIG->pluginspath;
+}
+
+/**
+ * Get the data directory path for this installation
+ *
+ * @return string
+ * @since 1.8.0
+ */
+function elgg_get_data_path() {
+ global $CONFIG;
+ return $CONFIG->dataroot;
+}
+
+/**
+ * Get the root directory path for this installation
+ *
+ * @return string
+ * @since 1.8.0
+ */
+function elgg_get_root_path() {
+ global $CONFIG;
+ return $CONFIG->path;
+}
+
+/**
+ * Get an Elgg configuration value
+ *
+ * @param string $name Name of the configuration value
+ * @param int $site_guid NULL for installation setting, 0 for default site
+ *
+ * @return mixed Configuration value or null if it does not exist
+ * @since 1.8.0
+ */
+function elgg_get_config($name, $site_guid = 0) {
+ global $CONFIG;
+
+ $name = trim($name);
+
+ if (isset($CONFIG->$name)) {
+ return $CONFIG->$name;
+ }
+
+ if ($site_guid === null) {
+ // installation wide setting
+ $value = datalist_get($name);
+ } else {
+ // hit DB only if we're not sure if value exists or not
+ if (!isset($CONFIG->site_config_loaded)) {
+ // site specific setting
+ if ($site_guid == 0) {
+ $site_guid = (int) $CONFIG->site_id;
+ }
+ $value = get_config($name, $site_guid);
+ } else {
+ $value = null;
+ }
+ }
+
+ // @todo document why we don't cache false
+ if ($value === false) {
+ return null;
+ }
+
+ $CONFIG->$name = $value;
+ return $value;
+}
+
+/**
+ * Set an Elgg configuration value
+ *
+ * @warning This does not persist the configuration setting. Use elgg_save_config()
+ *
+ * @param string $name Name of the configuration value
+ * @param mixed $value Value
+ *
+ * @return void
+ * @since 1.8.0
+ */
+function elgg_set_config($name, $value) {
+ global $CONFIG;
+
+ $name = trim($name);
+
+ $CONFIG->$name = $value;
+}
+
+/**
+ * Save a configuration setting
+ *
+ * @param string $name Configuration name (cannot be greater than 255 characters)
+ * @param mixed $value Configuration value. Should be string for installation setting
+ * @param int $site_guid NULL for installation setting, 0 for default site
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+function elgg_save_config($name, $value, $site_guid = 0) {
+ global $CONFIG;
+
+ $name = trim($name);
+
+ if (strlen($name) > 255) {
+ elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR");
+ return false;
+ }
+
+ elgg_set_config($name, $value);
+
+ if ($site_guid === NULL) {
+ if (is_array($value) || is_object($value)) {
+ return false;
+ }
+ return datalist_set($name, $value);
+ } else {
+ if ($site_guid == 0) {
+ $site_guid = (int) $CONFIG->site_id;
+ }
+ return set_config($name, $value, $site_guid);
+ }
+}
+
+/**
+ * Check that installation has completed and the database is populated.
+ *
+ * @throws InstallationException|DatabaseException
+ * @return void
+ * @access private
*/
+function verify_installation() {
+ global $CONFIG;
+
+ if (isset($CONFIG->installed)) {
+ return;
+ }
+
+ try {
+ $dblink = get_db_link('read');
+ if (!$dblink) {
+ throw new DatabaseException();
+ }
+
+ mysql_query("SELECT value FROM {$CONFIG->dbprefix}datalists WHERE name = 'installed'", $dblink);
+ if (mysql_errno($dblink) > 0) {
+ throw new DatabaseException();
+ }
+
+ $CONFIG->installed = true;
+
+ } catch (DatabaseException $e) {
+ throw new InstallationException(elgg_echo('InstallationException:SiteNotInstalled'));
+ }
+}
/**
- * Unset a config option.
+ * An array of key value pairs from the datalists table.
+ *
+ * Used as a cache in datalist functions.
*
- * @param string $name The name of the field.
- * @param int $site_guid Optionally, the GUID of the site (current site is assumed by default).
- * @return mixed
+ * @global array $DATALIST_CACHE
+ */
+$DATALIST_CACHE = array();
+
+/**
+ * Get the value of a datalist element.
+ *
+ * @internal Datalists are stored in the datalist table.
+ *
+ * @tip Use datalists to store information common to a full installation.
+ *
+ * @param string $name The name of the datalist
+ * @return string|null|false String if value exists, null if doesn't, false on error
+ * @access private
+ */
+function datalist_get($name) {
+ global $CONFIG, $DATALIST_CACHE;
+
+ $name = trim($name);
+
+ // cannot store anything longer than 255 characters in db, so catch here
+ if (elgg_strlen($name) > 255) {
+ elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR");
+ return false;
+ }
+
+ $name = sanitise_string($name);
+ if (isset($DATALIST_CACHE[$name])) {
+ return $DATALIST_CACHE[$name];
+ }
+
+ // If memcache enabled then cache value in memcache
+ $value = null;
+ static $datalist_memcache;
+ if ((!$datalist_memcache) && (is_memcache_available())) {
+ $datalist_memcache = new ElggMemcache('datalist_memcache');
+ }
+ if ($datalist_memcache) {
+ $value = $datalist_memcache->load($name);
+ }
+ if ($value) {
+ return $value;
+ }
+
+ // [Marcus Povey 20090217 : Now retrieving all datalist values on first
+ // load as this saves about 9 queries per page]
+ // This also causes OOM problems when the datalists table is large
+ // @todo make a list of datalists that we want to get in one grab
+ $result = get_data("SELECT * from {$CONFIG->dbprefix}datalists");
+ if ($result) {
+ foreach ($result as $row) {
+ $DATALIST_CACHE[$row->name] = $row->value;
+
+ // Cache it if memcache is available
+ if ($datalist_memcache) {
+ $datalist_memcache->save($row->name, $row->value);
+ }
+ }
+
+ if (isset($DATALIST_CACHE[$name])) {
+ return $DATALIST_CACHE[$name];
+ }
+ }
+
+ return null;
+}
+
+/**
+ * Set the value for a datalist element.
+ *
+ * @param string $name The name of the datalist
+ * @param string $value The new value
+ *
+ * @return bool
+ * @access private
+ */
+function datalist_set($name, $value) {
+ global $CONFIG, $DATALIST_CACHE;
+
+ // cannot store anything longer than 255 characters in db, so catch before we set
+ if (elgg_strlen($name) > 255) {
+ elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR");
+ return false;
+ }
+
+ $sanitised_name = sanitise_string($name);
+ $sanitised_value = sanitise_string($value);
+
+ // If memcache is available then invalidate the cached copy
+ static $datalist_memcache;
+ if ((!$datalist_memcache) && (is_memcache_available())) {
+ $datalist_memcache = new ElggMemcache('datalist_memcache');
+ }
+
+ if ($datalist_memcache) {
+ $datalist_memcache->delete($name);
+ }
+
+ $success = insert_data("INSERT into {$CONFIG->dbprefix}datalists"
+ . " set name = '{$sanitised_name}', value = '{$sanitised_value}'"
+ . " ON DUPLICATE KEY UPDATE value='{$sanitised_value}'");
+
+ if ($success !== FALSE) {
+ $DATALIST_CACHE[$name] = $value;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Run a function one time per installation.
+ *
+ * If you pass a timestamp as the second argument, it will run the function
+ * only if (i) it has never been run before or (ii) the timestamp is >=
+ * the last time it was run.
+ *
+ * @warning Functions are determined by their name. If you change the name of a function
+ * it will be run again.
+ *
+ * @tip Use $timelastupdatedcheck in your plugins init function to perform automated
+ * upgrades. Schedule a function to run once and pass the timestamp of the new release.
+ * This will cause the run once function to be run on all installations. To perform
+ * additional upgrades, create new functions for each release.
+ *
+ * @warning The function name cannot be longer than 255 characters long due to
+ * the current schema for the datalist table.
+ *
+ * @internal A datalist entry $functioname is created with the value of time().
+ *
+ * @param string $functionname The name of the function you want to run.
+ * @param int $timelastupdatedcheck A UNIX timestamp. If time() is > than this,
+ * this function will be run again.
+ *
+ * @return bool
+ */
+function run_function_once($functionname, $timelastupdatedcheck = 0) {
+ $lastupdated = datalist_get($functionname);
+ if ($lastupdated) {
+ $lastupdated = (int) $lastupdated;
+ } elseif ($lastupdated !== false) {
+ $lastupdated = 0;
+ } else {
+ // unable to check datalist
+ return false;
+ }
+ if (is_callable($functionname) && $lastupdated <= $timelastupdatedcheck) {
+ $functionname();
+ datalist_set($functionname, time());
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Removes a config setting.
+ *
+ * @internal
+ * These settings are stored in the dbprefix_config table and read during system
+ * boot into $CONFIG.
+ *
+ * @param string $name The name of the field.
+ * @param int $site_guid Optionally, the GUID of the site (current site is assumed by default).
+ *
+ * @return int|false The number of affected rows or false on error.
+ *
+ * @see get_config()
+ * @see set_config()
*/
function unset_config($name, $site_guid = 0) {
global $CONFIG;
+ if (isset($CONFIG->$name)) {
+ unset($CONFIG->$name);
+ }
+
$name = sanitise_string($name);
$site_guid = (int) $site_guid;
if ($site_guid == 0) {
$site_guid = (int) $CONFIG->site_id;
}
- return delete_data("delete from {$CONFIG->dbprefix}config where name='$name' and site_guid=$site_guid");
+ $query = "delete from {$CONFIG->dbprefix}config where name='$name' and site_guid=$site_guid";
+ return delete_data($query);
}
/**
- * Sets a configuration value
+ * Add or update a config setting.
*
- * @param string $name The name of the configuration value
- * @param string $value Its value
- * @param int $site_guid Optionally, the GUID of the site (current site is assumed by default)
- * @return 0
+ * If the config name already exists, it will be updated to the new value.
+ *
+ * @internal
+ * These settings are stored in the dbprefix_config table and read during system
+ * boot into $CONFIG.
+ *
+ * @param string $name The name of the configuration value
+ * @param string $value Its value
+ * @param int $site_guid Optionally, the GUID of the site (current site is assumed by default)
+ *
+ * @return bool
* @todo The config table doens't have numeric primary keys so insert_data returns 0.
+ * @todo Use "INSERT ... ON DUPLICATE KEY UPDATE" instead of trying to delete then add.
+ * @see unset_config()
+ * @see get_config()
+ * @access private
*/
function set_config($name, $value, $site_guid = 0) {
global $CONFIG;
+ $name = trim($name);
+
+ // cannot store anything longer than 255 characters in db, so catch before we set
+ if (elgg_strlen($name) > 255) {
+ elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR");
+ return false;
+ }
+
// Unset existing
unset_config($name, $site_guid);
@@ -50,42 +436,90 @@ function set_config($name, $value, $site_guid = 0) {
$CONFIG->$name = $value;
$value = sanitise_string(serialize($value));
- return insert_data("insert into {$CONFIG->dbprefix}config set name = '{$name}', value = '{$value}', site_guid = {$site_guid}");
+ $query = "insert into {$CONFIG->dbprefix}config"
+ . " set name = '{$name}', value = '{$value}', site_guid = {$site_guid}";
+ $result = insert_data($query);
+ return $result !== false;
}
/**
* Gets a configuration value
*
- * @param string $name The name of the config value
- * @param int $site_guid Optionally, the GUID of the site (current site is assumed by default)
- * @return mixed|false Depending on success
+ * @internal
+ * These settings are stored in the dbprefix_config table and read during system
+ * boot into $CONFIG.
+ *
+ * @param string $name The name of the config value
+ * @param int $site_guid Optionally, the GUID of the site (current site is assumed by default)
+ *
+ * @return mixed|null
+ * @see set_config()
+ * @see unset_config()
+ * @access private
*/
function get_config($name, $site_guid = 0) {
global $CONFIG;
+ $name = sanitise_string($name);
+ $site_guid = (int) $site_guid;
+
+ // check for deprecated values.
+ // @todo might be a better spot to define this?
+ $new_name = false;
+ switch($name) {
+ case 'viewpath':
+ $new_name = 'view_path';
+ $dep_version = 1.8;
+ break;
+
+ case 'pluginspath':
+ $new_name = 'plugins_path';
+ $dep_version = 1.8;
+ break;
+
+ case 'sitename':
+ $new_name = 'site_name';
+ $dep_version = 1.8;
+ break;
+ }
+
+ // @todo these haven't really been implemented in Elgg 1.8. Complete in 1.9.
+ // show dep message
+ if ($new_name) {
+ // $msg = "Config value $name has been renamed as $new_name";
+ $name = $new_name;
+ // elgg_deprecated_notice($msg, $dep_version);
+ }
+
+ // decide from where to return the value
if (isset($CONFIG->$name)) {
return $CONFIG->$name;
}
- $name = sanitise_string($name);
- $site_guid = (int) $site_guid;
+
if ($site_guid == 0) {
$site_guid = (int) $CONFIG->site_id;
}
- if ($result = get_data_row("SELECT value FROM {$CONFIG->dbprefix}config
- WHERE name = '{$name}' and site_guid = {$site_guid}")) {
+
+ $result = get_data_row("SELECT value FROM {$CONFIG->dbprefix}config
+ WHERE name = '{$name}' and site_guid = {$site_guid}");
+
+ if ($result) {
$result = $result->value;
$result = unserialize($result->value);
$CONFIG->$name = $result;
return $result;
}
- return false;
+ return null;
}
/**
- * Gets all the configuration details in the config database for a given site.
+ * Loads all configuration values from the dbprefix_config table into $CONFIG.
*
* @param int $site_guid Optionally, the GUID of the site (current site is assumed by default)
+ *
+ * @return bool
+ * @access private
*/
function get_all_config($site_guid = 0) {
global $CONFIG;
@@ -93,10 +527,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;
@@ -109,104 +543,90 @@ function get_all_config($site_guid = 0) {
}
/**
- * If certain configuration elements don't exist, autodetect sensible defaults
- *
- * @uses $CONFIG The main configuration global
+ * Loads configuration related to this site
*
+ * This loads from the config database table and the site entity
+ * @access private
*/
-function set_default_config() {
+function _elgg_load_site_config() {
global $CONFIG;
- if (empty($CONFIG->path)) {
- $CONFIG->path = str_replace("\\","/",dirname(dirname(dirname(__FILE__)))) . "/";
- }
-
- if (empty($CONFIG->viewpath)) {
- $CONFIG->viewpath = $CONFIG->path . "views/";
+ $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'));
}
- if (empty($CONFIG->pluginspath)) {
- $CONFIG->pluginspath = $CONFIG->path . "mod/";
- }
-
- if (empty($CONFIG->wwwroot)) {
- /*
- $CONFIG->wwwroot = "http://" . $_SERVER['SERVER_NAME'];
+ $CONFIG->wwwroot = $CONFIG->site->url;
+ $CONFIG->sitename = $CONFIG->site->name;
+ $CONFIG->sitedescription = $CONFIG->site->description;
+ $CONFIG->siteemail = $CONFIG->site->email;
+ $CONFIG->url = $CONFIG->wwwroot;
- $request = $_SERVER['REQUEST_URI'];
+ get_all_config();
+ // gives hint to elgg_get_config function how to approach missing values
+ $CONFIG->site_config_loaded = true;
+}
- if (strripos($request,"/") < (strlen($request) - 1)) {
- // addressing a file directly, not a dir
- $request = substr($request, 0, strripos($request,"/")+1);
- }
+/**
+ * Loads configuration related to Elgg as an application
+ *
+ * This loads from the datalists database table
+ * @access private
+ */
+function _elgg_load_application_config() {
+ global $CONFIG;
- $CONFIG->wwwroot .= $request;
- */
- $pathpart = str_replace("//","/",str_replace($_SERVER['DOCUMENT_ROOT'],"",$CONFIG->path));
- if (substr($pathpart,0,1) != "/") {
- $pathpart = "/" . $pathpart;
+ $install_root = str_replace("\\", "/", dirname(dirname(dirname(__FILE__))));
+ $defaults = array(
+ 'path' => "$install_root/",
+ 'view_path' => "$install_root/views/",
+ 'plugins_path' => "$install_root/mod/",
+ 'language' => 'en',
+
+ // compatibility with old names for plugins not using elgg_get_config()
+ 'viewpath' => "$install_root/views/",
+ 'pluginspath' => "$install_root/mod/",
+ );
+
+ foreach ($defaults as $name => $value) {
+ if (empty($CONFIG->$name)) {
+ $CONFIG->$name = $value;
}
- $CONFIG->wwwroot = "http://" . $_SERVER['HTTP_HOST'] . $pathpart;
}
- if (empty($CONFIG->url)) {
- $CONFIG->url = $CONFIG->wwwroot;
+ $path = datalist_get('path');
+ if (!empty($path)) {
+ $CONFIG->path = $path;
}
-
- if (empty($CONFIG->sitename)) {
- $CONFIG->sitename = "New Elgg site";
+ $dataroot = datalist_get('dataroot');
+ if (!empty($dataroot)) {
+ $CONFIG->dataroot = $dataroot;
}
-
- if (empty($CONFIG->language)) {
- $CONFIG->language = "en";
+ $simplecache_enabled = datalist_get('simplecache_enabled');
+ if ($simplecache_enabled !== false) {
+ $CONFIG->simplecache_enabled = $simplecache_enabled;
+ } else {
+ $CONFIG->simplecache_enabled = 1;
+ }
+ $system_cache_enabled = datalist_get('system_cache_enabled');
+ if ($system_cache_enabled !== false) {
+ $CONFIG->system_cache_enabled = $system_cache_enabled;
+ } else {
+ $CONFIG->system_cache_enabled = 1;
}
-}
-/**
- * Function that provides some config initialisation on system init
- *
- */
-function configuration_init() {
- global $CONFIG;
+ // initialize context here so it is set before the get_input call
+ $CONFIG->context = array();
- if (is_installed() || is_db_installed()) {
- $path = datalist_get('path');
- if (!empty($path)) {
- $CONFIG->path = $path;
- }
- $dataroot = datalist_get('dataroot');
- if (!empty($dataroot)) {
- $CONFIG->dataroot = $dataroot;
- }
- $simplecache_enabled = datalist_get('simplecache_enabled');
- if ($simplecache_enabled !== false) {
- $CONFIG->simplecache_enabled = $simplecache_enabled;
- } 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;
- } else {
- $CONFIG->viewpath_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;
+ // 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;
- // Load default settings from database
- get_all_config();
+ $CONFIG->i18n_loaded_from_cache = false;
- return true;
- }
+ // this must be synced with the enum for the entities table
+ $CONFIG->entity_types = array('group', 'object', 'site', 'user');
}
-
-/**
- * Register config_init
- */
-
-register_elgg_event_handler('boot', 'system', 'configuration_init', 10); \ No newline at end of file
diff --git a/engine/lib/cron.php b/engine/lib/cron.php
index 223b6774f..4f3d05b93 100644
--- a/engine/lib/cron.php
+++ b/engine/lib/cron.php
@@ -4,72 +4,86 @@
*
* @package Elgg
* @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
-/** The cron exception. */
-class CronException extends Exception {}
-
/**
- * Initialisation
+ * Cron initialization
*
+ * @return void
+ * @access private
*/
function cron_init() {
// Register a pagehandler for cron
- register_page_handler('cron','cron_page_handler');
-
+ elgg_register_page_handler('cron', 'cron_page_handler');
+
// register a hook for Walled Garden public pages
- register_plugin_hook('public_pages', 'walled_garden', 'cron_public_pages');
+ elgg_register_plugin_hook_handler('public_pages', 'walled_garden', 'cron_public_pages');
}
/**
- * Cron handler for redirecting pages.
+ * Cron handler
*
- * @param unknown_type $page
+ * @param array $page Pages
+ *
+ * @return bool
+ * @throws CronException
+ * @access private
*/
function cron_page_handler($page) {
- global $CONFIG;
+ if (!isset($page[0])) {
+ forward();
+ }
- if ($page[0]) {
- switch (strtolower($page[0])) {
- case 'minute' :
- case 'fiveminute' :
- case 'fifteenmin' :
- case 'halfhour' :
- case 'hourly' :
- case 'daily' :
- case 'weekly' :
- case 'monthly':
- case 'yearly' :
- case 'reboot' :
- set_input('period', $page[0]);
- break;
- default :
- throw new CronException(sprintf(elgg_echo('CronException:unknownperiod'), $page[0]));
- }
+ $period = strtolower($page[0]);
- // Include cron handler
- include($CONFIG->path . "engine/handlers/cron_handler.php");
- } else {
- forward();
+ $allowed_periods = array(
+ 'minute', 'fiveminute', 'fifteenmin', 'halfhour', 'hourly',
+ 'daily', 'weekly', 'monthly', 'yearly', 'reboot'
+ );
+
+ if (!in_array($period, $allowed_periods)) {
+ throw new CronException(elgg_echo('CronException:unknownperiod', array($period)));
}
+
+ // Get a list of parameters
+ $params = array();
+ $params['time'] = time();
+
+ // Data to return to
+ $old_stdout = "";
+ ob_start();
+
+ $old_stdout = elgg_trigger_plugin_hook('cron', $period, $params, $old_stdout);
+ $std_out = ob_get_clean();
+
+ echo $std_out . $old_stdout;
+ return true;
}
+/**
+ * Register cron's pages as public in case we're in Walled Garden mode
+ *
+ * @param string $hook public_pages
+ * @param string $type system
+ * @param array $return_value Array of pages to allow
+ * @param mixed $params Params
+ *
+ * @return array
+ * @access private
+ */
function cron_public_pages($hook, $type, $return_value, $params) {
- $return_value[] = 'pg/cron/minute';
- $return_value[] = 'pg/cron/fiveminute';
- $return_value[] = 'pg/cron/fifteenmin';
- $return_value[] = 'pg/cron/halfhour';
- $return_value[] = 'pg/cron/hourly';
- $return_value[] = 'pg/cron/daily';
- $return_value[] = 'pg/cron/weekly';
- $return_value[] = 'pg/cron/monthly';
- $return_value[] = 'pg/cron/yearly';
- $return_value[] = 'pg/cron/reboot';
-
+ $return_value[] = 'cron/minute';
+ $return_value[] = 'cron/fiveminute';
+ $return_value[] = 'cron/fifteenmin';
+ $return_value[] = 'cron/halfhour';
+ $return_value[] = 'cron/hourly';
+ $return_value[] = 'cron/daily';
+ $return_value[] = 'cron/weekly';
+ $return_value[] = 'cron/monthly';
+ $return_value[] = 'cron/yearly';
+ $return_value[] = 'cron/reboot';
+
return $return_value;
}
-// Register a startup event
-register_elgg_event_handler('init','system','cron_init'); \ No newline at end of file
+elgg_register_event_handler('init', 'system', 'cron_init');
diff --git a/engine/lib/database.php b/engine/lib/database.php
index 58685bb82..a7949788d 100644
--- a/engine/lib/database.php
+++ b/engine/lib/database.php
@@ -1,35 +1,94 @@
<?php
/**
- * Elgg database
- * Contains database connection and transfer functionality
+ * Elgg database procedural code.
*
- * @package Elgg
- * @subpackage Core
-
- * @author Curverider Ltd
+ * Includes functions for establishing and retrieving a database link,
+ * reading data, writing data, upgrading DB schemas, and sanitizing input.
+ *
+ * @package Elgg.Core
+ * @subpackage Database
+ */
- * @link http://elgg.org/
+/**
+ * Query cache for all queries.
+ *
+ * Each query and its results are stored in this cache as:
+ * <code>
+ * $DB_QUERY_CACHE[query hash] => array(result1, result2, ... resultN)
+ * </code>
+ * @see elgg_query_runner() for details on the hash.
+ *
+ * @warning Elgg used to set this as an empty array to turn off the cache
+ *
+ * @global ElggLRUCache|null $DB_QUERY_CACHE
+ * @access private
*/
+global $DB_QUERY_CACHE;
+$DB_QUERY_CACHE = null;
-$DB_QUERY_CACHE = array();
+/**
+ * Queries to be executed upon shutdown.
+ *
+ * These queries are saved to an array and executed using
+ * a function registered by register_shutdown_function().
+ *
+ * Queries are saved as an array in the format:
+ * <code>
+ * $DB_DELAYED_QUERIES[] = array(
+ * 'q' => str $query,
+ * 'l' => resource $dblink,
+ * 'h' => str $handler // a callback function
+ * );
+ * </code>
+ *
+ * @global array $DB_DELAYED_QUERIES
+ * @access private
+ */
+global $DB_DELAYED_QUERIES;
$DB_DELAYED_QUERIES = array();
/**
+ * Database connection resources.
+ *
+ * Each database link created with establish_db_link($name) is stored in
+ * $dblink as $dblink[$name] => resource. Use get_db_link($name) to retrieve it.
+ *
+ * @global resource[] $dblink
+ * @access private
+ */
+global $dblink;
+$dblink = array();
+
+/**
+ * Database call count
+ *
+ * Each call to the database increments this counter.
+ *
+ * @global integer $dbcalls
+ * @access private
+ */
+global $dbcalls;
+$dbcalls = 0;
+
+/**
+ * Establish a connection to the database servser
+ *
* Connect to the database server and use the Elgg database for a particular database link
*
- * @param string $dblinkname Default "readwrite"; you can change this to set up additional global database links, eg "read" and "write"
+ * @param string $dblinkname The type of database connection. Used to identify the
+ * resource. eg "read", "write", or "readwrite".
+ *
+ * @return void
+ * @throws DatabaseException
+ * @access private
*/
function establish_db_link($dblinkname = "readwrite") {
// Get configuration, and globalise database link
- global $CONFIG, $dblink, $DB_QUERY_CACHE, $dbcalls;
-
- if (!isset($dblink)) {
- $dblink = array();
- }
+ global $CONFIG, $dblink, $DB_QUERY_CACHE;
if ($dblinkname != "readwrite" && isset($CONFIG->db[$dblinkname])) {
if (is_array($CONFIG->db[$dblinkname])) {
- $index = rand(0,sizeof($CONFIG->db[$dblinkname]));
+ $index = rand(0, sizeof($CONFIG->db[$dblinkname]));
$dbhost = $CONFIG->db[$dblinkname][$index]->dbhost;
$dbuser = $CONFIG->db[$dblinkname][$index]->dbuser;
$dbpass = $CONFIG->db[$dblinkname][$index]->dbpass;
@@ -48,13 +107,14 @@ function establish_db_link($dblinkname = "readwrite") {
}
// Connect to database
- if (!$dblink[$dblinkname] = mysql_connect($CONFIG->dbhost, $CONFIG->dbuser, $CONFIG->dbpass, true)) {
- $msg = sprintf(elgg_echo('DatabaseException:WrongCredentials'),
- $CONFIG->dbuser, $CONFIG->dbhost, "****");
+ if (!$dblink[$dblinkname] = mysql_connect($dbhost, $dbuser, $dbpass, true)) {
+ $msg = elgg_echo('DatabaseException:WrongCredentials',
+ array($dbuser, $dbhost, "****"));
throw new DatabaseException($msg);
}
- if (!mysql_select_db($CONFIG->dbname, $dblink[$dblinkname])) {
- $msg = sprintf(elgg_echo('DatabaseException:NoConnect'), $CONFIG->dbname);
+
+ if (!mysql_select_db($dbname, $dblink[$dblinkname])) {
+ $msg = elgg_echo('DatabaseException:NoConnect', array($dbname));
throw new DatabaseException($msg);
}
@@ -68,21 +128,22 @@ function establish_db_link($dblinkname = "readwrite") {
// Set up cache if global not initialized and query cache not turned off
if ((!$DB_QUERY_CACHE) && (!$db_cache_off)) {
- $DB_QUERY_CACHE = new ElggStaticVariableCache('db_query_cache'); //array();
- //$DB_QUERY_CACHE = select_default_memcache('db_query_cache'); //array();
+ // @todo if we keep this cache in 1.9, expose the size as a config parameter
+ $DB_QUERY_CACHE = new ElggLRUCache(200);
}
}
/**
- * Establish all database connections
+ * Establish database connections
*
* If the configuration has been set up for multiple read/write databases, set those
- * links up separately; otherwise just create the one database link
+ * links up separately; otherwise just create the one database link.
*
+ * @return void
+ * @access private
*/
function setup_db_connections() {
- // Get configuration and globalise database link
- global $CONFIG, $dblink;
+ global $CONFIG;
if (!empty($CONFIG->db->split)) {
establish_db_link('read');
@@ -93,7 +154,10 @@ function setup_db_connections() {
}
/**
- * Shutdown hook to display profiling information about db (debug mode)
+ * Display profiling information about db at NOTICE debug level upon shutdown.
+ *
+ * @return void
+ * @access private
*/
function db_profiling_shutdown_hook() {
global $dbcalls;
@@ -103,44 +167,47 @@ function db_profiling_shutdown_hook() {
}
/**
- * Execute any delayed queries.
+ * Execute any delayed queries upon shutdown.
+ *
+ * @return void
+ * @access private
*/
function db_delayedexecution_shutdown_hook() {
- global $DB_DELAYED_QUERIES, $CONFIG;
+ global $DB_DELAYED_QUERIES;
foreach ($DB_DELAYED_QUERIES as $query_details) {
- // use one of our db functions so it is included in profiling.
- $result = execute_query($query_details['q'], $query_details['l']);
-
try {
+ $link = $query_details['l'];
+
+ if ($link == 'read' || $link == 'write') {
+ $link = get_db_link($link);
+ } elseif (!is_resource($link)) {
+ elgg_log("Link for delayed query not valid resource or db_link type. Query: {$query_details['q']}", 'WARNING');
+ }
+
+ $result = execute_query($query_details['q'], $link);
+
if ((isset($query_details['h'])) && (is_callable($query_details['h']))) {
$query_details['h']($result);
}
- } catch (Exception $e) { // Suppress all errors since these can't be delt with here
+ } catch (Exception $e) {
+ // Suppress all errors since these can't be dealt with here
elgg_log($e, 'WARNING');
}
}
}
/**
- * Alias to setup_db_connections, for use in the event handler
+ * Returns (if required, also creates) a database link resource.
*
- * @param string $event The event type
- * @param string $object_type The object type
- * @param mixed $object Used for nothing in this context
- */
-function init_db($event, $object_type, $object = null) {
- register_shutdown_function('db_delayedexecution_shutdown_hook');
- register_shutdown_function('db_profiling_shutdown_hook');
- // [Marcus Povey 20090213: Db connection moved to first db connection attempt]
- return true;
-}
-
-/**
- * Gets the appropriate db link for the operation mode requested
+ * Database link resources are stored in the {@link $dblink} global. These
+ * resources are created by {@link setup_db_connections()}, which is called if
+ * no links exist.
*
- * @param string $dblinktype The type of link we want - "read", "write" or "readwrite" (the default)
- * @return object Database link
+ * @param string $dblinktype The type of link we want: "read", "write" or "readwrite".
+ *
+ * @return resource Database link
+ * @access private
*/
function get_db_link($dblinktype) {
global $dblink;
@@ -149,40 +216,59 @@ function get_db_link($dblinktype) {
return $dblink[$dblinktype];
} else if (isset($dblink['readwrite'])) {
return $dblink['readwrite'];
- }
- else {
+ } else {
setup_db_connections();
return get_db_link($dblinktype);
}
}
/**
- * Explain a given query, useful for debug.
+ * Execute an EXPLAIN for $query.
+ *
+ * @param string $query The query to explain
+ * @param mixed $link The database link resource to user.
+ *
+ * @return mixed An object of the query's result, or FALSE
+ * @access private
*/
function explain_query($query, $link) {
if ($result = execute_query("explain " . $query, $link)) {
return mysql_fetch_object($result);
}
- return false;
+ return FALSE;
}
/**
* Execute a query.
*
- * @param string $query The query
- * @param link $dblink the DB link
- * @return Returns a the result of mysql_query
+ * $query is executed via {@link mysql_query()}. If there is an SQL error,
+ * a {@link DatabaseException} is thrown.
+ *
+ * @internal
+ * {@link $dbcalls} is incremented and the query is saved into the {@link $DB_QUERY_CACHE}.
+ *
+ * @param string $query The query
+ * @param resource $dblink The DB link
+ *
+ * @return resource result of mysql_query()
+ * @throws DatabaseException
+ * @access private
*/
function execute_query($query, $dblink) {
- global $CONFIG, $dbcalls, $DB_QUERY_CACHE;
+ global $dbcalls;
+
+ if ($query == NULL) {
+ throw new DatabaseException(elgg_echo('DatabaseException:InvalidQuery'));
+ }
+
+ if (!is_resource($dblink)) {
+ throw new DatabaseException(elgg_echo('DatabaseException:InvalidDBLink'));
+ }
$dbcalls++;
$result = mysql_query($query, $dblink);
- if ($DB_QUERY_CACHE) {
- $DB_QUERY_CACHE[$query] = -1; // Set initial cache to -1
- }
if (mysql_errno($dblink)) {
throw new DatabaseException(mysql_error($dblink) . "\n\n QUERY: " . $query);
@@ -192,14 +278,17 @@ function execute_query($query, $dblink) {
}
/**
- * Queue a query for execution after all output has been sent to the user.
+ * Queue a query for execution upon shutdown.
*
* You can specify a handler function if you care about the result. This function will accept
- * the raw result from mysql_query();
+ * the raw result from {@link mysql_query()}.
+ *
+ * @param string $query The query to execute
+ * @param resource|string $dblink The database link to use or the link type (read | write)
+ * @param string $handler A callback function to pass the results array to
*
- * @param string $query The query to execute
- * @param resource $dblink The database link to use
- * @param string $handler The handler
+ * @return true
+ * @access private
*/
function execute_delayed_query($query, $dblink, $handler = "") {
global $DB_DELAYED_QUERIES;
@@ -208,6 +297,10 @@ function execute_delayed_query($query, $dblink, $handler = "") {
$DB_DELAYED_QUERIES = array();
}
+ if (!is_resource($dblink) && $dblink != 'read' && $dblink != 'write') {
+ return false;
+ }
+
// Construct delayed query
$delayed_query = array();
$delayed_query['q'] = $query;
@@ -216,212 +309,243 @@ function execute_delayed_query($query, $dblink, $handler = "") {
$DB_DELAYED_QUERIES[] = $delayed_query;
- return true;
+ return TRUE;
}
/**
* Write wrapper for execute_delayed_query()
*
- * @param string $query The query to execute
+ * @param string $query The query to execute
* @param string $handler The handler if you care about the result.
+ *
+ * @return true
+ * @uses execute_delayed_query()
+ * @uses get_db_link()
+ * @access private
*/
function execute_delayed_write_query($query, $handler = "") {
- return execute_delayed_query($query, get_db_link('write'), $handler);
+ return execute_delayed_query($query, 'write', $handler);
}
/**
* Read wrapper for execute_delayed_query()
*
- * @param string $query The query to execute
+ * @param string $query The query to execute
* @param string $handler The handler if you care about the result.
+ *
+ * @return true
+ * @uses execute_delayed_query()
+ * @uses get_db_link()
+ * @access private
*/
function execute_delayed_read_query($query, $handler = "") {
- return execute_delayed_query($query, get_db_link('read'), $handler);
+ return execute_delayed_query($query, 'read', $handler);
}
/**
- * Use this function to get data from the database
- * @param mixed $query The query being passed.
- * @param string $call Optionally, the name of a function to call back to on each row (which takes $row as a single parameter)
- * @return array An array of database result objects
+ * Retrieve rows from the database.
+ *
+ * Queries are executed with {@link execute_query()} and results
+ * are retrieved with {@link mysql_fetch_object()}. If a callback
+ * function $callback is defined, each row will be passed as the single
+ * argument to $callback. If no callback function is defined, the
+ * entire result set is returned as an array.
+ *
+ * @param mixed $query The query being passed.
+ * @param string $callback Optionally, the name of a function to call back to on each row
+ *
+ * @return array An array of database result objects or callback function results. If the query
+ * returned nothing, an empty array.
+ * @access private
*/
function get_data($query, $callback = "") {
- global $CONFIG, $DB_QUERY_CACHE;
+ return elgg_query_runner($query, $callback, false);
+}
- // Is cached?
- if ($DB_QUERY_CACHE) {
- $cached_query = $DB_QUERY_CACHE[$query];
- }
+/**
+ * Retrieve a single row from the database.
+ *
+ * Similar to {@link get_data()} but returns only the first row
+ * matched. If a callback function $callback is specified, the row will be passed
+ * as the only argument to $callback.
+ *
+ * @param mixed $query The query to execute.
+ * @param string $callback A callback function
+ *
+ * @return mixed A single database result object or the result of the callback function.
+ * @access private
+ */
+function get_data_row($query, $callback = "") {
+ return elgg_query_runner($query, $callback, true);
+}
+
+/**
+ * Handles returning data from a query, running it through a callback function,
+ * and caching the results. This is for R queries (from CRUD).
+ *
+ * @access private
+ *
+ * @param string $query The query to execute
+ * @param string $callback An optional callback function to run on each row
+ * @param bool $single Return only a single result?
+ *
+ * @return array An array of database result objects or callback function results. If the query
+ * returned nothing, an empty array.
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_query_runner($query, $callback = null, $single = false) {
+ global $DB_QUERY_CACHE;
- if ((isset($cached_query)) && ($cached_query)) {
- elgg_log("$query results returned from cache");
+ // Since we want to cache results of running the callback, we need to
+ // need to namespace the query with the callback and single result request.
+ // https://github.com/elgg/elgg/issues/4049
+ $hash = (string)$callback . (int)$single . $query;
- if ($cached_query === -1) {
- // Last time this query returned nothing, so return an empty array
- return array();
+ // Is cached?
+ if ($DB_QUERY_CACHE) {
+ if (isset($DB_QUERY_CACHE[$hash])) {
+ elgg_log("DB query $query results returned from cache (hash: $hash)", 'NOTICE');
+ return $DB_QUERY_CACHE[$hash];
}
-
- return $cached_query;
}
$dblink = get_db_link('read');
- $resultarray = array();
+ $return = array();
if ($result = execute_query("$query", $dblink)) {
+
+ // test for callback once instead of on each iteration.
+ // @todo check profiling to see if this needs to be broken out into
+ // explicit cases instead of checking in the iteration.
+ $is_callable = is_callable($callback);
while ($row = mysql_fetch_object($result)) {
- if (!empty($callback) && is_callable($callback)) {
+ if ($is_callable) {
$row = $callback($row);
}
- if ($row) {
- $resultarray[] = $row;
+
+ if ($single) {
+ $return = $row;
+ break;
+ } else {
+ $return[] = $row;
}
}
}
- if (empty($resultarray)) {
- elgg_log("DB query \"$query\" returned no results.");
- return false;
+ if (empty($return)) {
+ elgg_log("DB query $query returned no results.", 'NOTICE');
}
// Cache result
if ($DB_QUERY_CACHE) {
- $DB_QUERY_CACHE[$query] = $resultarray;
- elgg_log("$query results cached");
- }
-
- return $resultarray;
-}
-
-/**
- * Use this function to get a single data row from the database
- * @param mixed $query The query to run.
- * @return object A single database result object
- */
-
-function get_data_row($query, $callback = "") {
- global $CONFIG, $DB_QUERY_CACHE;
-
- // Is cached
- if ($DB_QUERY_CACHE) {
- $cached_query = $DB_QUERY_CACHE[$query];
- }
-
- if ((isset($cached_query)) && ($cached_query)) {
- elgg_log("$query results returned from cache");
-
- if ($cached_query === -1) {
- // Last time this query returned nothing, so return false
- //@todo fix me this should return array().
- return false;
- }
-
- return $cached_query;
- }
-
- $dblink = get_db_link('read');
-
- if ($result = execute_query("$query", $dblink)) {
- $row = mysql_fetch_object($result);
-
- // Cache result (even if query returned no data)
- if ($DB_QUERY_CACHE) {
- $DB_QUERY_CACHE[$query] = $row;
- elgg_log("$query results cached");
- }
-
- if (!empty($callback) && is_callable($callback)) {
- $row = $callback($row);
- }
-
- if ($row) {
- return $row;
- }
+ $DB_QUERY_CACHE[$hash] = $return;
+ elgg_log("DB query $query results cached (hash: $hash)", 'NOTICE');
}
- elgg_log("$query returned no results.");
- return FALSE;
+ return $return;
}
/**
- * Use this function to insert database data; returns id or false
+ * Insert a row into the database.
+ *
+ * @note Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}.
+ *
+ * @param mixed $query The query to execute.
*
- * @param mixed $query The query to run.
- * @return int $id the database id of the inserted row.
+ * @return int|false The database id of the inserted row if a AUTO_INCREMENT field is
+ * defined, 0 if not, and false on failure.
+ * @access private
*/
function insert_data($query) {
- global $CONFIG, $DB_QUERY_CACHE;
+ elgg_log("DB query $query", 'NOTICE');
+
$dblink = get_db_link('write');
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- $DB_QUERY_CACHE->clear();
- }
-
- elgg_log("Query cache invalidated");
+ _elgg_invalidate_query_cache();
if (execute_query("$query", $dblink)) {
return mysql_insert_id($dblink);
}
- return false;
+ return FALSE;
}
/**
- * Update database data
+ * Update the database.
+ *
+ * @note Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}.
+ *
+ * @param string $query The query to run.
*
- * @param mixed $query The query to run.
- * @return Bool on success
+ * @return bool
+ * @access private
*/
function update_data($query) {
- global $CONFIG, $DB_QUERY_CACHE;
+
+ elgg_log("DB query $query", 'NOTICE');
$dblink = get_db_link('write');
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- $DB_QUERY_CACHE->clear();
- elgg_log("Query cache invalidated");
- }
+ _elgg_invalidate_query_cache();
if (execute_query("$query", $dblink)) {
- // @todo why is this comment out?
- //return mysql_affected_rows();
- return true;
+ return TRUE;
}
- return false;
+ return FALSE;
}
/**
- * Use this function to delete data
+ * Remove data from the database.
*
- * @param mixed $query The SQL query to run
- * @return int|false Either the number of affected rows, or false on failure
+ * @note Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}.
+ *
+ * @param string $query The SQL query to run
+ *
+ * @return int|false The number of affected rows or false on failure
+ * @access private
*/
function delete_data($query) {
- global $CONFIG, $DB_QUERY_CACHE;
+
+ elgg_log("DB query $query", 'NOTICE');
$dblink = get_db_link('write');
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- $DB_QUERY_CACHE->clear();
- elgg_log("Query cache invalidated");
- }
+ _elgg_invalidate_query_cache();
if (execute_query("$query", $dblink)) {
return mysql_affected_rows($dblink);
}
- return false;
+ return FALSE;
}
+/**
+ * Invalidate the query cache
+ *
+ * @access private
+ */
+function _elgg_invalidate_query_cache() {
+ global $DB_QUERY_CACHE;
+ if ($DB_QUERY_CACHE instanceof ElggLRUCache) {
+ $DB_QUERY_CACHE->clear();
+ elgg_log("Query cache invalidated", 'NOTICE');
+ } elseif ($DB_QUERY_CACHE) {
+ // In case someone sets the cache to an array and primes it with data
+ $DB_QUERY_CACHE = array();
+ elgg_log("Query cache invalidated", 'NOTICE');
+ }
+}
/**
- * Get the tables currently installed in the Elgg database
+ * Return tables matching the database prefix {@link $CONFIG->dbprefix}% in the currently
+ * selected database.
*
- * @return array List of tables
+ * @return array|false List of tables or false on failure
+ * @static array $tables Tables found matching the database prefix
+ * @access private
*/
function get_db_tables() {
global $CONFIG;
@@ -435,29 +559,36 @@ function get_db_tables() {
$result = get_data("show tables like '" . $CONFIG->dbprefix . "%'");
} catch (DatabaseException $d) {
// Likely we can't handle an exception here, so just return false.
- return false;
+ return FALSE;
}
$tables = array();
if (is_array($result) && !empty($result)) {
- foreach($result as $row) {
+ foreach ($result as $row) {
$row = (array) $row;
- if (is_array($row) && !empty($row))
- foreach($row as $element) {
+ if (is_array($row) && !empty($row)) {
+ foreach ($row as $element) {
$tables[] = $element;
}
+ }
}
} else {
- return false;
+ return FALSE;
}
return $tables;
}
/**
- * Run an optimize query on a mysql tables. Useful for executing after major data changes.
+ * Optimise a table.
+ *
+ * Executes an OPTIMIZE TABLE query on $table. Useful after large DB changes.
*
+ * @param string $table The name of the table to optimise
+ *
+ * @return bool
+ * @access private
*/
function optimize_table($table) {
$table = sanitise_string($table);
@@ -467,18 +598,35 @@ function optimize_table($table) {
/**
* Get the last database error for a particular database link
*
- * @param database link $dblink
+ * @param resource $dblink The DB link
+ *
* @return string Database error message
+ * @access private
*/
function get_db_error($dblink) {
return mysql_error($dblink);
}
/**
- * Runs a full database script from disk
+ * Runs a full database script from disk.
+ *
+ * The file specified should be a standard SQL file as created by
+ * mysqldump or similar. Statements must be terminated with ;
+ * and a newline character (\n or \r\n) with only one statement per line.
+ *
+ * The special string 'prefix_' is replaced with the database prefix
+ * as defined in {@link $CONFIG->dbprefix}.
+ *
+ * @warning Errors do not halt execution of the script. If a line
+ * generates an error, the error message is saved and the
+ * next line is executed. After the file is run, any errors
+ * are displayed as a {@link DatabaseException}
*
- * @uses $CONFIG
* @param string $scriptlocation The full path to the script
+ *
+ * @return void
+ * @throws DatabaseException
+ * @access private
*/
function run_sql_script($scriptlocation) {
if ($script = file_get_contents($scriptlocation)) {
@@ -486,14 +634,18 @@ function run_sql_script($scriptlocation) {
$errors = array();
+ // Remove MySQL -- style comments
$script = preg_replace('/\-\-.*\n/', '', $script);
- $sql_statements = preg_split('/;[\n\r]+/', $script);
- foreach($sql_statements as $statement) {
+
+ // Statements must end with ; and a newline
+ $sql_statements = preg_split('/;[\n\r]+/', $script);
+
+ foreach ($sql_statements as $statement) {
$statement = trim($statement);
- $statement = str_replace("prefix_",$CONFIG->dbprefix,$statement);
+ $statement = str_replace("prefix_", $CONFIG->dbprefix, $statement);
if (!empty($statement)) {
try {
- $result = update_data($statement);
+ update_data($statement);
} catch (DatabaseException $e) {
$errors[] = $e->getMessage();
}
@@ -501,99 +653,38 @@ function run_sql_script($scriptlocation) {
}
if (!empty($errors)) {
$errortxt = "";
- foreach($errors as $error)
+ foreach ($errors as $error) {
$errortxt .= " {$error};";
- throw new DatabaseException(elgg_echo('DatabaseException:DBSetupIssues') . $errortxt);
- }
- } else {
- throw new DatabaseException(sprintf(elgg_echo('DatabaseException:ScriptNotFound'), $scriptlocation));
- }
-}
-
-/**
- * Upgrade the database schema in an ordered sequence.
- *
- * Makes use of schema upgrade files
- *
- * This is a about as core as it comes, so don't start running this from your plugins!
- *
- * @param int $version The version you are upgrading from (usually given in the Elgg version format of YYYYMMDDXX - see version.php for example)
- * @param string $fromdir Optional directory to load upgrades from (default: engine/schema/upgrades/)
- * @param bool $quiet If true, will suppress all error messages. Don't use this.
- * @return bool
- */
-function db_upgrade($version, $fromdir = "", $quiet = FALSE) {
- global $CONFIG;
-
- // Elgg and its database must be installed to upgrade it!
- if (!is_db_installed() || !is_installed()) {
- return false;
- }
-
- $version = (int) $version;
-
- if (!$fromdir) {
- $fromdir = $CONFIG->path . 'engine/schema/upgrades/';
- }
-
- if ($handle = opendir($fromdir)) {
- $sqlupgrades = array();
-
- while ($sqlfile = readdir($handle)) {
- if (!is_dir($fromdir . $sqlfile)) {
- if (preg_match('/^([0-9]{10})\.(sql)$/', $sqlfile, $matches)) {
- $sql_version = (int) $matches[1];
- if ($sql_version > $version) {
- $sqlupgrades[] = $sqlfile;
- }
- }
}
- }
-
- asort($sqlupgrades);
- if (sizeof($sqlupgrades) > 0) {
- foreach($sqlupgrades as $sqlfile) {
-
- // hide all errors.
- if ($quiet) {
- try {
- run_sql_script($fromdir . $sqlfile);
- } catch (DatabaseException $e) {
- error_log($e->getmessage());
- }
- } else {
- run_sql_script($fromdir . $sqlfile);
- }
- }
+ $msg = elgg_echo('DatabaseException:DBSetupIssues') . $errortxt;
+ throw new DatabaseException($msg);
}
+ } else {
+ $msg = elgg_echo('DatabaseException:ScriptNotFound', array($scriptlocation));
+ throw new DatabaseException($msg);
}
-
- return TRUE;
}
/**
- * This function, called by validate_platform(), will check whether the installed version of
- * MySQL meets the minimum required.
+ * Format a query string for logging
*
- * TODO: If multiple dbs are supported check which db is supported and use the appropriate code to validate
- * the appropriate version.
- *
- * @return bool
+ * @param string $query Query string
+ * @return string
+ * @access private
*/
-function db_check_version() {
- $version = mysql_get_server_info();
- $points = explode('.', $version);
-
- if ($points[0] < 5) {
- return false;
- }
-
- return true;
+function elgg_format_query($query) {
+ // remove newlines and extra spaces so logs are easier to read
+ return preg_replace('/\s\s+/', ' ', $query);
}
/**
* Sanitise a string for database use, but with the option of escaping extra characters.
+ *
+ * @param string $string The string to sanitise
+ * @param string $extra_escapeable Extra characters to escape with '\\'
+ *
+ * @return string The escaped string
*/
function sanitise_string_special($string, $extra_escapeable = '') {
$string = sanitise_string($string);
@@ -606,9 +697,10 @@ function sanitise_string_special($string, $extra_escapeable = '') {
}
/**
- * Sanitise a string for database use
+ * Sanitise a string for database use.
*
* @param string $string The string to sanitise
+ *
* @return string Sanitised string
*/
function sanitise_string($string) {
@@ -621,34 +713,52 @@ function sanitise_string($string) {
* Wrapper function for alternate English spelling
*
* @param string $string The string to sanitise
+ *
* @return string Sanitised string
- * @uses sanitise_string
*/
function sanitize_string($string) {
return sanitise_string($string);
}
/**
- * Sanitises an integer for database use
+ * Sanitises an integer for database use.
*
- * @param int $int
- * @return int Sanitised integer
+ * @param int $int Value to be sanitized
+ * @param bool $signed Whether negative values should be allowed (true)
+ * @return int
*/
-function sanitise_int($int) {
+function sanitise_int($int, $signed = true) {
+ $int = (int) $int;
+
+ if ($signed === false) {
+ if ($int < 0) {
+ $int = 0;
+ }
+ }
+
return (int) $int;
}
/**
- * Wrapper function for alternate English spelling
+ * Sanitizes an integer for database use.
+ * Wrapper function for alternate English spelling (@see sanitise_int)
*
- * @param int $int
- * @return int Sanitised integer
- * @uses sanitise_string
+ * @param int $int Value to be sanitized
+ * @param bool $signed Whether negative values should be allowed (true)
+ * @return int
*/
-function sanitize_int($int) {
- return (int) $int;
+function sanitize_int($int, $signed = true) {
+ return sanitise_int($int, $signed);
}
-// Stuff for initialisation
+/**
+ * Registers shutdown functions for database profiling and delayed queries.
+ *
+ * @access private
+ */
+function init_db() {
+ register_shutdown_function('db_delayedexecution_shutdown_hook');
+ register_shutdown_function('db_profiling_shutdown_hook');
+}
-register_elgg_event_handler('boot','system','init_db',0); \ No newline at end of file
+elgg_register_event_handler('init', 'system', 'init_db');
diff --git a/engine/lib/deprecated-1.7.php b/engine/lib/deprecated-1.7.php
new file mode 100644
index 000000000..ee95b5611
--- /dev/null
+++ b/engine/lib/deprecated-1.7.php
@@ -0,0 +1,1164 @@
+<?php
+/**
+ * Get entities with the specified access collection id.
+ *
+ * @deprecated 1.7. Use elgg_get_entities_from_access_id()
+ *
+ * @param int $collection_id ID of collection
+ * @param string $entity_type Type of entities
+ * @param string $entity_subtype Subtype of entities
+ * @param int $owner_guid Guid of owner
+ * @param int $limit Limit of number of entities to return
+ * @param int $offset Skip this many entities
+ * @param string $order_by Column to order by
+ * @param int $site_guid The site guid
+ * @param bool $count Return a count or entities
+ *
+ * @return array
+ */
+function get_entities_from_access_id($collection_id, $entity_type = "", $entity_subtype = "",
+ $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) {
+ // log deprecated warning
+ elgg_deprecated_notice('get_entities_from_access_id() was deprecated by elgg_get_entities()', 1.7);
+
+ if (!$collection_id) {
+ return FALSE;
+ }
+
+ // build the options using given parameters
+ $options = array();
+ $options['limit'] = $limit;
+ $options['offset'] = $offset;
+ $options['count'] = $count;
+
+ if ($entity_type) {
+ $options['type'] = sanitise_string($entity_type);
+ }
+
+ if ($entity_subtype) {
+ $options['subtype'] = $entity_subtype;
+ }
+
+ if ($site_guid) {
+ $options['site_guid'] = $site_guid;
+ }
+
+ if ($order_by) {
+ $options['order_by'] = sanitise_string("e.time_created, $order_by");
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ if ($site_guid) {
+ $options['site_guid'] = $site_guid;
+ }
+
+ $options['access_id'] = $collection_id;
+
+ return elgg_get_entities_from_access_id($options);
+}
+
+/**
+ * @deprecated 1.7
+ */
+function get_entities_from_access_collection($collection_id, $entity_type = "", $entity_subtype = "",
+ $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) {
+
+ elgg_deprecated_notice('get_entities_from_access_collection() was deprecated by elgg_get_entities()', 1.7);
+
+ return get_entities_from_access_id($collection_id, $entity_type, $entity_subtype,
+ $owner_guid, $limit, $offset, $order_by, $site_guid, $count);
+}
+
+/**
+ * Get entities from annotations
+ *
+ * No longer used.
+ *
+ * @deprecated 1.7 Use elgg_get_entities_from_annotations()
+ *
+ * @param mixed $entity_type Type of entity
+ * @param mixed $entity_subtype Subtype of entity
+ * @param string $name Name of annotation
+ * @param string $value Value of annotation
+ * @param int $owner_guid Guid of owner of annotation
+ * @param int $group_guid Guid of group
+ * @param int $limit Limit
+ * @param int $offset Offset
+ * @param string $order_by SQL order by string
+ * @param bool $count Count or return entities
+ * @param int $timelower Lower time limit
+ * @param int $timeupper Upper time limit
+ *
+ * @return unknown_type
+ */
+function get_entities_from_annotations($entity_type = "", $entity_subtype = "", $name = "",
+$value = "", $owner_guid = 0, $group_guid = 0, $limit = 10, $offset = 0, $order_by = "asc",
+$count = false, $timelower = 0, $timeupper = 0) {
+ $msg = 'get_entities_from_annotations() is deprecated by elgg_get_entities_from_annotations().';
+ elgg_deprecated_notice($msg, 1.7);
+
+ $options = array();
+
+ if ($entity_type) {
+ $options['types'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtypes'] = $entity_subtype;
+ }
+
+ $options['annotation_names'] = $name;
+
+ if ($value) {
+ $options['annotation_values'] = $value;
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['annotation_owner_guids'] = $owner_guid;
+ } else {
+ $options['annotation_owner_guid'] = $owner_guid;
+ }
+ }
+
+ if ($group_guid) {
+ $options['container_guid'] = $group_guid;
+ }
+
+ if ($limit) {
+ $options['limit'] = $limit;
+ }
+
+ if ($offset) {
+ $options['offset'] = $offset;
+ }
+
+ if ($order_by) {
+ $options['order_by'] = "maxtime $order_by";
+ }
+
+ if ($count) {
+ $options['count'] = $count;
+ }
+
+ if ($timelower) {
+ $options['annotation_created_time_lower'] = $timelower;
+ }
+
+ if ($timeupper) {
+ $options['annotation_created_time_upper'] = $timeupper;
+ }
+
+ return elgg_get_entities_from_annotations($options);
+}
+
+/**
+ * Lists entities
+ *
+ * @see elgg_view_entity_list
+ *
+ * @param string $entity_type Type of entity.
+ * @param string $entity_subtype Subtype of entity.
+ * @param string $name Name of annotation.
+ * @param string $value Value of annotation.
+ * @param int $limit Maximum number of results to return.
+ * @param int $owner_guid Owner.
+ * @param int $group_guid Group container. Currently only supported if entity_type is object
+ * @param boolean $asc Whether to list in ascending or descending order (default: desc)
+ * @param boolean $fullview Whether to display the entities in full
+ * @param boolean $listtypetoggle Can 'gallery' view can be displayed (default: no)
+ *
+ * @deprecated 1.7 Use elgg_list_entities_from_annotations()
+ * @return string Formatted entity list
+ */
+function list_entities_from_annotations($entity_type = "", $entity_subtype = "", $name = "",
+$value = "", $limit = 10, $owner_guid = 0, $group_guid = 0, $asc = false, $fullview = true,
+$listtypetoggle = false) {
+
+ $msg = 'list_entities_from_annotations is deprecated by elgg_list_entities_from_annotations';
+ elgg_deprecated_notice($msg, 1.8);
+
+ $options = array();
+
+ if ($entity_type) {
+ $options['types'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtypes'] = $entity_subtype;
+ }
+
+ if ($name) {
+ $options['annotation_names'] = $name;
+ }
+
+ if ($value) {
+ $options['annotation_values'] = $value;
+ }
+
+ if ($limit) {
+ $options['limit'] = $limit;
+ }
+
+ if ($owner_guid) {
+ $options['annotation_owner_guid'] = $owner_guid;
+ }
+
+ if ($group_guid) {
+ $options['container_guid'] = $group_guid;
+ }
+
+ if ($asc) {
+ $options['order_by'] = 'maxtime desc';
+ }
+
+ if ($offset = sanitise_int(get_input('offset', null))) {
+ $options['offset'] = $offset;
+ }
+
+ $options['full_view'] = $fullview;
+ $options['list_type_toggle'] = $listtypetoggle;
+ $options['pagination'] = $pagination;
+
+ return elgg_list_entities_from_annotations($options);
+}
+
+/**
+ * Returns all php files in a directory.
+ *
+ * @deprecated 1.7 Use elgg_get_file_list() instead
+ *
+ * @param string $directory Directory to look in
+ * @param array $exceptions Array of extensions (with .!) to ignore
+ * @param array $list A list files to include in the return
+ *
+ * @return array
+ */
+function get_library_files($directory, $exceptions = array(), $list = array()) {
+ elgg_deprecated_notice('get_library_files() deprecated by elgg_get_file_list()', 1.7);
+ return elgg_get_file_list($directory, $exceptions, $list, array('.php'));
+}
+
+/**
+ * Add action tokens to URL.
+ *
+ * @param string $url URL
+ *
+ * @return string
+ *
+ * @deprecated 1.7 final
+ */
+function elgg_validate_action_url($url) {
+ elgg_deprecated_notice('elgg_validate_action_url() deprecated by elgg_add_action_tokens_to_url().',
+ 1.7);
+
+ return elgg_add_action_tokens_to_url($url);
+}
+
+/**
+ * Does nothing.
+ *
+ * @deprecated 1.7
+ * @return 0
+ */
+function test_ip() {
+ elgg_deprecated_notice('test_ip() was removed because of licensing issues.', 1.7);
+
+ return 0;
+}
+
+/**
+ * Does nothing.
+ *
+ * @return bool
+ * @deprecated 1.7
+ */
+function is_ip_in_array() {
+ elgg_deprecated_notice('is_ip_in_array() was removed because of licensing issues.', 1.7);
+
+ return false;
+}
+
+/**
+ * Returns entities.
+ *
+ * @deprecated 1.7. Use elgg_get_entities().
+ *
+ * @param string $type Entity type
+ * @param string $subtype Entity subtype
+ * @param int $owner_guid Owner GUID
+ * @param string $order_by Order by clause
+ * @param int $limit Limit
+ * @param int $offset Offset
+ * @param bool $count Return a count or an array of entities
+ * @param int $site_guid Site GUID
+ * @param int $container_guid Container GUID
+ * @param int $timelower Lower time limit
+ * @param int $timeupper Upper time limit
+ *
+ * @return array
+ */
+function get_entities($type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10,
+$offset = 0, $count = false, $site_guid = 0, $container_guid = null, $timelower = 0,
+$timeupper = 0) {
+
+ elgg_deprecated_notice('get_entities() was deprecated by elgg_get_entities().', 1.7);
+
+ // rewrite owner_guid to container_guid to emulate old functionality
+ if ($owner_guid != "") {
+ if (is_null($container_guid)) {
+ $container_guid = $owner_guid;
+ $owner_guid = NULL;
+ }
+ }
+
+ $options = array();
+ if ($type) {
+ if (is_array($type)) {
+ $options['types'] = $type;
+ } else {
+ $options['type'] = $type;
+ }
+ }
+
+ if ($subtype) {
+ if (is_array($subtype)) {
+ $options['subtypes'] = $subtype;
+ } else {
+ $options['subtype'] = $subtype;
+ }
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ if ($order_by) {
+ $options['order_by'] = $order_by;
+ }
+
+ // need to pass 0 for all option
+ $options['limit'] = $limit;
+
+ if ($offset) {
+ $options['offset'] = $offset;
+ }
+
+ if ($count) {
+ $options['count'] = $count;
+ }
+
+ if ($site_guid) {
+ $options['site_guids'] = $site_guid;
+ }
+
+ if ($container_guid) {
+ $options['container_guids'] = $container_guid;
+ }
+
+ if ($timeupper) {
+ $options['created_time_upper'] = $timeupper;
+ }
+
+ if ($timelower) {
+ $options['created_time_lower'] = $timelower;
+ }
+
+ $r = elgg_get_entities($options);
+ return $r;
+}
+
+/**
+ * Delete multiple entities that match a given query.
+ * This function iterates through and calls delete_entity on
+ * each one, this is somewhat inefficient but lets
+ * the 'delete' event be called for each entity.
+ *
+ * @deprecated 1.7. This is a dangerous function as it defaults to deleting everything.
+ *
+ * @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
+ *
+ * @return false
+ */
+function delete_entities($type = "", $subtype = "", $owner_guid = 0) {
+ elgg_deprecated_notice('delete_entities() was deprecated because no one should use it.', 1.7);
+ return false;
+}
+
+/**
+ * Lists entities.
+ *
+ * @param int $owner_guid Owner GUID
+ * @param int $limit Limit
+ * @param bool $fullview Show entity full views
+ * @param bool $listtypetoggle Show list type toggle
+ * @param bool $allowedtypes A string of the allowed types
+ *
+ * @return string
+ * @deprecated 1.7. Use elgg_list_registered_entities().
+ */
+function list_registered_entities($owner_guid = 0, $limit = 10, $fullview = true,
+$listtypetoggle = false, $allowedtypes = true) {
+
+ elgg_deprecated_notice('list_registered_entities() was deprecated by elgg_list_registered_entities().', 1.7);
+
+ $options = array();
+
+ // don't want to send anything if not being used.
+ if ($owner_guid) {
+ $options['owner_guid'] = $owner_guid;
+ }
+
+ if ($limit) {
+ $options['limit'] = $limit;
+ }
+
+ if ($allowedtypes) {
+ $options['allowed_types'] = $allowedtypes;
+ }
+
+ // need to send because might be BOOL
+ $options['full_view'] = $fullview;
+ $options['list_type_toggle'] = $listtypetoggle;
+
+ $options['offset'] = get_input('offset', 0);
+
+ return elgg_list_registered_entities($options);
+}
+
+/**
+ * Lists entities
+ *
+ * @deprecated 1.7. Use elgg_list_entities().
+ *
+ * @param string $type Entity type
+ * @param string $subtype Entity subtype
+ * @param int $owner_guid Owner GUID
+ * @param int $limit Limit
+ * @param bool $fullview Display entity full views?
+ * @param bool $listtypetoggle Allow switching to gallery mode?
+ * @param bool $pagination Show pagination?
+ *
+ * @return string
+ */
+function list_entities($type= "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true,
+$listtypetoggle = false, $pagination = true) {
+
+ elgg_deprecated_notice('list_entities() was deprecated by elgg_list_entities()!', 1.7);
+
+ $options = array();
+
+ // rewrite owner_guid to container_guid to emulate old functionality
+ if ($owner_guid) {
+ $options['container_guids'] = $owner_guid;
+ }
+
+ if ($type) {
+ $options['types'] = $type;
+ }
+
+ if ($subtype) {
+ $options['subtypes'] = $subtype;
+ }
+
+ if ($limit) {
+ $options['limit'] = $limit;
+ }
+
+ if ($offset = sanitise_int(get_input('offset', null))) {
+ $options['offset'] = $offset;
+ }
+
+ $options['full_view'] = $fullview;
+ $options['list_type_toggle'] = $listtypetoggle;
+ $options['pagination'] = $pagination;
+
+ return elgg_list_entities($options);
+}
+
+/**
+ * Searches for a group based on a complete or partial name or description
+ *
+ * @param string $criteria The partial or full name or description
+ * @param int $limit Limit of the search.
+ * @param int $offset Offset.
+ * @param string $order_by The order.
+ * @param boolean $count Whether to return the count of results or just the results.
+ *
+ * @return mixed
+ * @deprecated 1.7
+ */
+function search_for_group($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) {
+ elgg_deprecated_notice('search_for_group() was deprecated by new search plugin.', 1.7);
+ global $CONFIG;
+
+ $criteria = sanitise_string($criteria);
+ $limit = (int)$limit;
+ $offset = (int)$offset;
+ $order_by = sanitise_string($order_by);
+
+ $access = get_access_sql_suffix("e");
+
+ if ($order_by == "") {
+ $order_by = "e.time_created desc";
+ }
+
+ if ($count) {
+ $query = "SELECT count(e.guid) as total ";
+ } else {
+ $query = "SELECT e.* ";
+ }
+ $query .= "from {$CONFIG->dbprefix}entities e"
+ . " JOIN {$CONFIG->dbprefix}groups_entity g on e.guid=g.guid where ";
+
+ $query .= "(g.name like \"%{$criteria}%\" or g.description like \"%{$criteria}%\")";
+ $query .= " and $access";
+
+ 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;
+}
+
+/**
+ * Returns a formatted list of groups suitable for injecting into search.
+ *
+ * @deprecated 1.7
+ *
+ * @param string $hook Hook name
+ * @param string $user User
+ * @param mixed $returnvalue Previous hook's return value
+ * @param string $tag Tag to search on
+ *
+ * @return string
+ */
+function search_list_groups_by_name($hook, $user, $returnvalue, $tag) {
+ elgg_deprecated_notice('search_list_groups_by_name() was deprecated by new search plugin', 1.7);
+ // Change this to set the number of groups that display on the search page
+ $threshold = 4;
+
+ $object = get_input('object');
+
+ if (!get_input('offset') && (empty($object) || $object == 'group')) {
+ if ($groups = search_for_group($tag, $threshold)) {
+ $countgroups = search_for_group($tag, 0, 0, "", true);
+
+ $return = elgg_view('group/search/startblurb', array('count' => $countgroups, 'tag' => $tag));
+ foreach ($groups as $group) {
+ $return .= elgg_view_entity($group);
+ }
+ $vars = array('count' => $countgroups, 'threshold' => $threshold, 'tag' => $tag);
+ $return .= elgg_view('group/search/finishblurb', $vars);
+ return $return;
+ }
+ }
+}
+
+/**
+ * Displays a list of group objects that have been searched for.
+ *
+ * @see elgg_view_entity_list
+ *
+ * @param string $tag Search criteria
+ * @param int $limit The number of entities to display on a page
+ *
+ * @return string The list in a form suitable to display
+ * @deprecated 1.7
+ */
+function list_group_search($tag, $limit = 10) {
+ elgg_deprecated_notice('list_group_search() was deprecated by new search plugin.', 1.7);
+ $offset = (int) get_input('offset');
+ $limit = (int) $limit;
+ $count = (int) search_for_group($tag, 10, 0, '', true);
+ $entities = search_for_group($tag, $limit, $offset);
+
+ return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, false);
+
+}
+
+/**
+ * Return a list of entities based on the given search criteria.
+ *
+ * @deprecated 1.7 use elgg_get_entities_from_metadata().
+ *
+ * @param mixed $meta_name Metadat 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 GUID. 0 for current, -1 for any.
+ * @param bool $count Return a count instead of entities
+ * @param bool $case_sensitive Metadata names case sensitivity
+ *
+ * @return int|array A list of entities, or a count if $count is set to true
+ */
+function get_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, $case_sensitive = TRUE) {
+
+ elgg_deprecated_notice('get_entities_from_metadata() was deprecated by elgg_get_entities_from_metadata()!', 1.7);
+
+ $options = array();
+
+ $options['metadata_names'] = $meta_name;
+
+ if ($meta_value) {
+ $options['metadata_values'] = $meta_value;
+ }
+
+ if ($entity_type) {
+ $options['types'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtypes'] = $entity_subtype;
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ if ($limit) {
+ $options['limit'] = $limit;
+ }
+
+ if ($offset) {
+ $options['offset'] = $offset;
+ }
+
+ if ($order_by) {
+ $options['order_by'];
+ }
+
+ if ($site_guid) {
+ $options['site_guid'];
+ }
+
+ if ($count) {
+ $options['count'] = $count;
+ }
+
+ // need to be able to pass false
+ $options['metadata_case_sensitive'] = $case_sensitive;
+
+ return elgg_get_entities_from_metadata($options);
+}
+
+/**
+ * Return entities from metadata
+ *
+ * @deprecated 1.7. Use elgg_get_entities_from_metadata().
+ *
+ * @param mixed $meta_array Metadata name
+ * @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 GUID. 0 for current, -1 for any.
+ * @param bool $count Return a count instead of entities
+ * @param bool $meta_array_operator Operator for metadata values
+ *
+ * @return int|array A list of entities, or a count if $count is set to true
+ */
+function get_entities_from_metadata_multi($meta_array, $entity_type = "", $entity_subtype = "",
+$owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0,
+$count = false, $meta_array_operator = 'and') {
+
+ elgg_deprecated_notice('get_entities_from_metadata_multi() was deprecated by elgg_get_entities_from_metadata()!', 1.7);
+
+ if (!is_array($meta_array) || sizeof($meta_array) == 0) {
+ return false;
+ }
+
+ $options = array();
+
+ $options['metadata_name_value_pairs'] = $meta_array;
+
+ if ($entity_type) {
+ $options['types'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtypes'] = $entity_subtype;
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ if ($limit) {
+ $options['limit'] = $limit;
+ }
+
+ if ($offset) {
+ $options['offset'] = $offset;
+ }
+
+ if ($order_by) {
+ $options['order_by'];
+ }
+
+ if ($site_guid) {
+ $options['site_guid'];
+ }
+
+ if ($count) {
+ $options['count'] = $count;
+ }
+
+ $options['metadata_name_value_pairs_operator'] = $meta_array_operator;
+
+ return elgg_get_entities_from_metadata($options);
+}
+
+/**
+ * Returns a menu item for use in the children section of add_menu()
+ * This is not currently used in the Elgg core.
+ *
+ * @param string $menu_name The name of the menu item
+ * @param string $menu_url Its URL
+ *
+ * @return stdClass|false Depending on success
+ * @deprecated 1.7
+ */
+function menu_item($menu_name, $menu_url) {
+ elgg_deprecated_notice('menu_item() is deprecated by add_submenu_item', 1.7);
+ return make_register_object($menu_name, $menu_url);
+}
+
+/**
+ * Searches for an object based on a complete or partial title
+ * or description using full text searching.
+ *
+ * IMPORTANT NOTE: With MySQL's default setup:
+ * 1) $criteria must be 4 or more characters long
+ * 2) If $criteria matches greater than 50% of results NO RESULTS ARE RETURNED!
+ *
+ * @param string $criteria The partial or full name or username.
+ * @param int $limit Limit of the search.
+ * @param int $offset Offset.
+ * @param string $order_by The order.
+ * @param boolean $count Whether to return the count of results or just the results.
+ *
+ * @return int|false
+ * @deprecated 1.7
+ */
+function search_for_object($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) {
+ elgg_deprecated_notice('search_for_object() was deprecated by new search plugin.', 1.7);
+ global $CONFIG;
+
+ $criteria = sanitise_string($criteria);
+ $limit = (int)$limit;
+ $offset = (int)$offset;
+ $order_by = sanitise_string($order_by);
+ $container_guid = (int)$container_guid;
+
+ $access = get_access_sql_suffix("e");
+
+ if ($order_by == "") {
+ $order_by = "e.time_created desc";
+ }
+
+ if ($count) {
+ $query = "SELECT count(e.guid) as total ";
+ } else {
+ $query = "SELECT e.* ";
+ }
+ $query .= "from {$CONFIG->dbprefix}entities e
+ join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid
+ where match(o.title,o.description) against ('$criteria') and $access";
+
+ 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;
+}
+
+/**
+ * Returns a formatted list of objects suitable for injecting into search.
+ *
+ * @deprecated 1.7
+ *
+ * @param sting $hook Hook
+ * @param string $user user
+ * @param mixed $returnvalue Previous return value
+ * @param mixed $tag Search term
+ *
+ * @return array
+ */
+function search_list_objects_by_name($hook, $user, $returnvalue, $tag) {
+ elgg_deprecated_notice('search_list_objects_by_name was deprecated by new search plugin.', 1.7);
+
+ // Change this to set the number of users that display on the search page
+ $threshold = 4;
+
+ $object = get_input('object');
+
+ if (!get_input('offset') && (empty($object) || $object == 'user')) {
+ if ($users = search_for_user($tag, $threshold)) {
+ $countusers = search_for_user($tag, 0, 0, "", true);
+
+ $return = elgg_view('user/search/startblurb', array('count' => $countusers, 'tag' => $tag));
+ foreach ($users as $user) {
+ $return .= elgg_view_entity($user);
+ }
+ $return .= elgg_view('user/search/finishblurb',
+ array('count' => $countusers, 'threshold' => $threshold, 'tag' => $tag));
+
+ return $return;
+
+ }
+ }
+}
+
+/**
+ * Return entities from relationships
+ *
+ * @deprecated 1.7 Use elgg_get_entities_from_relationship()
+ *
+ * @param string $relationship The relationship type
+ * @param int $relationship_guid The GUID of the relationship owner
+ * @param bool $inverse_relationship Invert relationship?
+ * @param string $type Entity type
+ * @param string $subtype Entity subtype
+ * @param int $owner_guid Entity owner GUID
+ * @param string $order_by Order by clause
+ * @param int $limit Limit
+ * @param int $offset Offset
+ * @param bool $count Return a count instead of entities?
+ * @param int $site_guid Site GUID
+ *
+ * @return mixed
+ */
+function get_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) {
+
+ elgg_deprecated_notice('get_entities_from_relationship() was deprecated by elgg_get_entities_from_relationship()!', 1.7);
+
+ $options = array();
+
+ $options['relationship'] = $relationship;
+ $options['relationship_guid'] = $relationship_guid;
+ $options['inverse_relationship'] = $inverse_relationship;
+
+ if ($type) {
+ $options['types'] = $type;
+ }
+
+ if ($subtype) {
+ $options['subtypes'] = $subtype;
+ }
+
+ if ($owner_guid) {
+ $options['owner_guid'] = $owner_guid;
+ }
+
+ $options['limit'] = $limit;
+
+ if ($offset) {
+ $options['offset'] = $offset;
+ }
+
+ if ($order_by) {
+ $options['order_by'];
+ }
+
+ if ($site_guid) {
+ $options['site_guid'];
+ }
+
+ if ($count) {
+ $options['count'] = $count;
+ }
+
+ return elgg_get_entities_from_relationship($options);
+}
+
+/**
+ * Searches for a site based on a complete or partial name
+ * or description or url using full text searching.
+ *
+ * IMPORTANT NOTE: With MySQL's default setup:
+ * 1) $criteria must be 4 or more characters long
+ * 2) If $criteria matches greater than 50% of results NO RESULTS ARE RETURNED!
+ *
+ * @param string $criteria The partial or full name or username.
+ * @param int $limit Limit of the search.
+ * @param int $offset Offset.
+ * @param string $order_by The order.
+ * @param boolean $count Whether to return the count of results or just the results.
+ *
+ * @return mixed
+ * @deprecated 1.7
+ */
+function search_for_site($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) {
+ elgg_deprecated_notice('search_for_site() was deprecated by new search plugin.', 1.7);
+ global $CONFIG;
+
+ $criteria = sanitise_string($criteria);
+ $limit = (int)$limit;
+ $offset = (int)$offset;
+ $order_by = sanitise_string($order_by);
+
+ $access = get_access_sql_suffix("e");
+
+ if ($order_by == "") {
+ $order_by = "e.time_created desc";
+ }
+
+ if ($count) {
+ $query = "SELECT count(e.guid) as total ";
+ } else {
+ $query = "SELECT e.* ";
+ }
+ $query .= "from {$CONFIG->dbprefix}entities e
+ join {$CONFIG->dbprefix}sites_entity s on e.guid=s.guid
+ where match(s.name, s.description, s.url) against ('$criteria') and $access";
+
+ 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;
+}
+
+/**
+ * Searches for a user based on a complete or partial name or username.
+ *
+ * @param string $criteria The partial or full name or username.
+ * @param int $limit Limit of the search.
+ * @param int $offset Offset.
+ * @param string $order_by The order.
+ * @param boolean $count Whether to return the count of results or just the results.
+ *
+ * @return mixed
+ * @deprecated 1.7
+ */
+function search_for_user($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) {
+ elgg_deprecated_notice('search_for_user() was deprecated by new search.', 1.7);
+ global $CONFIG;
+
+ $criteria = sanitise_string($criteria);
+ $limit = (int)$limit;
+ $offset = (int)$offset;
+ $order_by = sanitise_string($order_by);
+
+ $access = get_access_sql_suffix("e");
+
+ if ($order_by == "") {
+ $order_by = "e.time_created desc";
+ }
+
+ if ($count) {
+ $query = "SELECT count(e.guid) as total ";
+ } else {
+ $query = "SELECT e.* ";
+ }
+ $query .= "from {$CONFIG->dbprefix}entities e
+ join {$CONFIG->dbprefix}users_entity u on e.guid=u.guid where ";
+
+ $query .= "(u.name like \"%{$criteria}%\" or u.username like \"%{$criteria}%\")";
+ $query .= " and $access";
+
+ if (!$count) {
+ $query .= " order by $order_by limit $offset, $limit";
+ return get_data($query, "entity_row_to_elggstar");
+ } else {
+ if ($count = get_data_row($query)) {
+ return $count->total;
+ }
+ }
+ return false;
+}
+
+/**
+ * Displays a list of user objects that have been searched for.
+ *
+ * @see elgg_view_entity_list
+ *
+ * @param string $tag Search criteria
+ * @param int $limit The number of entities to display on a page
+ *
+ * @return string The list in a form suitable to display
+ *
+ * @deprecated 1.7
+ */
+function list_user_search($tag, $limit = 10) {
+ elgg_deprecated_notice('list_user_search() deprecated by new search', 1.7);
+ $offset = (int) get_input('offset');
+ $limit = (int) $limit;
+ $count = (int) search_for_user($tag, 10, 0, '', true);
+ $entities = search_for_user($tag, $limit, $offset);
+
+ return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, false);
+}
+
+/**
+ * Returns a formatted list of users suitable for injecting into search.
+ *
+ * @deprecated 1.7
+ *
+ * @param string $hook Hook name
+ * @param string $user User?
+ * @param mixed $returnvalue Previous hook's return value
+ * @param mixed $tag Tag to search against
+ *
+ * @return void
+ */
+function search_list_users_by_name($hook, $user, $returnvalue, $tag) {
+ elgg_deprecated_notice('search_list_users_by_name() was deprecated by new search', 1.7);
+ // Change this to set the number of users that display on the search page
+ $threshold = 4;
+
+ $object = get_input('object');
+
+ if (!get_input('offset') && (empty($object) || $object == 'user')) {
+ if ($users = search_for_user($tag, $threshold)) {
+ $countusers = search_for_user($tag, 0, 0, "", true);
+
+ $return = elgg_view('user/search/startblurb', array('count' => $countusers, 'tag' => $tag));
+ foreach ($users as $user) {
+ $return .= elgg_view_entity($user);
+ }
+
+ $vars = array('count' => $countusers, 'threshold' => $threshold, 'tag' => $tag);
+ $return .= elgg_view('user/search/finishblurb', $vars);
+ return $return;
+
+ }
+ }
+}
+
+/**
+ * Extend a view
+ *
+ * @deprecated 1.7. Use elgg_extend_view().
+ *
+ * @param string $view The view to extend.
+ * @param string $view_name This view is added to $view
+ * @param int $priority The priority, from 0 to 1000,
+ * to add at (lowest numbers displayed first)
+ * @param string $viewtype Not used
+ *
+ * @return void
+ */
+function extend_view($view, $view_name, $priority = 501, $viewtype = '') {
+ elgg_deprecated_notice('extend_view() was deprecated by elgg_extend_view()!', 1.7);
+ elgg_extend_view($view, $view_name, $priority, $viewtype);
+}
+
+/**
+ * Get views in a dir
+ *
+ * @deprecated 1.7. Use elgg_get_views().
+ *
+ * @param string $dir Dir
+ * @param string $base Base view
+ *
+ * @return array
+ */
+function get_views($dir, $base) {
+ elgg_deprecated_notice('get_views() was deprecated by elgg_get_views()!', 1.7);
+ elgg_get_views($dir, $base);
+}
+
+/**
+ * Constructs and returns a register object.
+ *
+ * @param string $register_name The name of the register
+ * @param mixed $register_value The value of the register
+ * @param array $children_array Optionally, an array of children
+ *
+ * @return false|stdClass Depending on success
+ * @deprecated 1.7 Use {@link add_submenu_item()}
+ */
+function make_register_object($register_name, $register_value, $children_array = array()) {
+ elgg_deprecated_notice('make_register_object() is deprecated by add_submenu_item()', 1.7);
+ if (empty($register_name) || empty($register_value)) {
+ return false;
+ }
+
+ $register = new stdClass;
+ $register->name = $register_name;
+ $register->value = $register_value;
+ $register->children = $children_array;
+
+ return $register;
+}
+
+/**
+ * THIS FUNCTION IS DEPRECATED.
+ *
+ * Delete a object's extra data.
+ *
+ * @todo - this should be removed - was deprecated in 1.5 or earlier
+ *
+ * @param int $guid GUID
+ *
+ * @return 1
+ * @deprecated 1.7
+ */
+function delete_object_entity($guid) {
+ system_message(elgg_echo('deprecatedfunction', array('delete_user_entity')));
+
+ return 1; // Always return that we have deleted one row in order to not break existing code.
+}
+
+/**
+ * THIS FUNCTION IS DEPRECATED.
+ *
+ * Delete a user's extra data.
+ *
+ * @todo remove
+ *
+ * @param int $guid User GUID
+ *
+ * @return 1
+ * @deprecated 1.7
+ */
+function delete_user_entity($guid) {
+ system_message(elgg_echo('deprecatedfunction', array('delete_user_entity')));
+
+ return 1; // Always return that we have deleted one row in order to not break existing code.
+} \ No newline at end of file
diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php
new file mode 100644
index 000000000..91068d047
--- /dev/null
+++ b/engine/lib/deprecated-1.8.php
@@ -0,0 +1,4820 @@
+<?php
+/**
+ * ***************************************************************************
+ * NOTE: If this is ever removed from Elgg, sites lose the ability to upgrade
+ * from 1.7.x and earlier to the latest version of Elgg without upgrading to
+ * 1.8 first.
+ * ***************************************************************************
+ *
+ * Upgrade the database schema in an ordered sequence.
+ *
+ * Executes all upgrade files in elgg/engine/schema/upgrades/ in sequential order.
+ * Upgrade files must be in the standard Elgg release format of YYYYMMDDII.sql
+ * where II is an incrementor starting from 01.
+ *
+ * Files that are < $version will be ignored.
+ *
+ * @warning Plugin authors should not call this function directly.
+ *
+ * @param int $version The version you are upgrading from in the format YYYYMMDDII.
+ * @param string $fromdir Optional directory to load upgrades from. default: engine/schema/upgrades/
+ * @param bool $quiet If true, suppress all error messages. Only use for the upgrade from <=1.6.
+ *
+ * @return int The number of upgrades run.
+ * @see upgrade.php
+ * @see version.php
+ * @deprecated 1.8 Use PHP upgrades for sql changes.
+ */
+function db_upgrade($version, $fromdir = "", $quiet = FALSE) {
+ global $CONFIG;
+
+ elgg_deprecated_notice('db_upgrade() is deprecated by using PHP upgrades.', 1.8);
+
+ $version = (int) $version;
+
+ if (!$fromdir) {
+ $fromdir = $CONFIG->path . 'engine/schema/upgrades/';
+ }
+
+ $i = 0;
+
+ if ($handle = opendir($fromdir)) {
+ $sqlupgrades = array();
+
+ while ($sqlfile = readdir($handle)) {
+ if (!is_dir($fromdir . $sqlfile)) {
+ if (preg_match('/^([0-9]{10})\.(sql)$/', $sqlfile, $matches)) {
+ $sql_version = (int) $matches[1];
+ if ($sql_version > $version) {
+ $sqlupgrades[] = $sqlfile;
+ }
+ }
+ }
+ }
+
+ asort($sqlupgrades);
+
+ if (sizeof($sqlupgrades) > 0) {
+ foreach ($sqlupgrades as $sqlfile) {
+
+ // hide all errors.
+ if ($quiet) {
+ try {
+ run_sql_script($fromdir . $sqlfile);
+ } catch (DatabaseException $e) {
+ error_log($e->getmessage());
+ }
+ } else {
+ run_sql_script($fromdir . $sqlfile);
+ }
+ $i++;
+ }
+ }
+ }
+
+ return $i;
+}
+
+/**
+ * Lists entities from an access collection
+ *
+ * @deprecated 1.8 Use elgg_list_entities_from_access_id()
+ *
+ * @return str
+ */
+function list_entities_from_access_id($access_id, $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true) {
+
+ elgg_deprecated_notice("All list_entities* functions were deprecated in 1.8. Use elgg_list_entities* instead.", 1.8);
+
+ echo elgg_list_entities_from_access_id(array('access_id' => $access_id,
+ 'type' => $entity_type, 'subtype' => $entity_subtype, 'owner_guids' => $owner_guid,
+ 'limit' => $limit, 'full_view' => $fullview, 'list_type_toggle' => $listtypetoggle,
+ 'pagination' => $pagination,));
+}
+
+/**
+ * Registers a particular action in memory
+ *
+ * @deprecated 1.8 Use {@link elgg_register_action()} instead
+ *
+ * @param string $action The name of the action (eg "register", "account/settings/save")
+ * @param boolean $public Can this action be accessed by people not logged into the system?
+ * @param string $filename Optionally, the filename where this action is located
+ * @param boolean $admin_only Whether this action is only available to admin users.
+ */
+function register_action($action, $public = false, $filename = "", $admin_only = false) {
+ elgg_deprecated_notice("register_action() was deprecated by elgg_register_action()", 1.8);
+
+ if ($admin_only) {
+ $access = 'admin';
+ } elseif ($public) {
+ $access = 'public';
+ } else {
+ $access = 'logged_in';
+ }
+
+ return elgg_register_action($action, $filename, $access);
+}
+
+/**
+ * Register an admin page with the admin panel.
+ * This function extends the view "admin/main" with the provided view.
+ * This view should provide a description and either a control or a link to.
+ *
+ * @deprecated 1.8 Extend admin views manually
+ *
+ * Usage:
+ * - To add a control to the main admin panel then extend admin/main
+ * - To add a control to a new page create a page which renders a view admin/subpage
+ * (where subpage is your new page -
+ * nb. some pages already exist that you can extend), extend the main view to point to it,
+ * and add controls to your new view.
+ *
+ * At the moment this is essentially a wrapper around elgg_extend_view().
+ *
+ * @param string $new_admin_view The view associated with the control you're adding
+ * @param string $view The view to extend, by default this is 'admin/main'.
+ * @param int $priority Optional priority to govern the appearance in the list.
+ *
+ * @return void
+ */
+function extend_elgg_admin_page($new_admin_view, $view = 'admin/main', $priority = 500) {
+ elgg_deprecated_notice('extend_elgg_admin_page() does nothing. Extend admin views manually.', 1.8);
+}
+
+/**
+ * Get entities ordered by a mathematical calculation
+ *
+ * @deprecated 1.8 Use elgg_get_entities_from_annotation_calculation()
+ *
+ * @param string $sum What sort of calculation to perform
+ * @param string $entity_type Type of Entity
+ * @param string $entity_subtype Subtype of Entity
+ * @param string $name Name of annotation
+ * @param string $mdname Metadata name
+ * @param string $mdvalue Metadata value
+ * @param int $owner_guid GUID of owner of annotation
+ * @param int $limit Limit of results
+ * @param int $offset Offset of results
+ * @param string $orderdir Order of results
+ * @param bool $count Return count or entities
+ *
+ * @return mixed
+ */
+function get_entities_from_annotations_calculate_x($sum = "sum", $entity_type = "", $entity_subtype = "", $name = "", $mdname = '', $mdvalue = '', $owner_guid = 0, $limit = 10, $offset = 0, $orderdir = 'desc', $count = false) {
+
+ $msg = 'get_entities_from_annotations_calculate_x() is deprecated by elgg_get_entities_from_annotation_calculation().';
+
+ elgg_deprecated_notice($msg, 1.8);
+
+ $options = array();
+
+ $options['calculation'] = $sum;
+
+ if ($entity_type) {
+ $options['types'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtypes'] = $entity_subtype;
+ }
+
+ $options['annotation_names'] = $name;
+
+ if ($mdname) {
+ $options['metadata_names'] = $mdname;
+ }
+
+ if ($mdvalue) {
+ $options['metadata_values'] = $mdvalue;
+ }
+
+ // original function rewrote this to container guid.
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['container_guids'] = $owner_guid;
+ } else {
+ $options['container_guid'] = $owner_guid;
+ }
+ }
+
+ $options['limit'] = $limit;
+ $options['offset'] = $offset;
+
+ $options['order_by'] = "annotation_calculation $orderdir";
+
+ $options['count'] = $count;
+
+ return elgg_get_entities_from_annotation_calculation($options);
+}
+
+/**
+ * Returns entities ordered by the sum of an annotation
+ *
+ * @warning This is function uses sum instead of count. THIS IS SLOW. See #3366.
+ * This should be used when you have annotations with different values and you
+ * want a list of entities ordered by the sum of all of those values.
+ * If you want a list of entities ordered by the number of annotations on each entity,
+ * use __get_entities_from_annotations_calculate_x() and pass 'count' as the first param.
+ *
+ * @deprecated 1.8 Use elgg_get_entities_from_annotation_calculation()
+ *
+ * @param string $entity_type Type of Entity
+ * @param string $entity_subtype Subtype of Entity
+ * @param string $name Name of annotation
+ * @param string $mdname Metadata name
+ * @param string $mdvalue Metadata value
+ * @param int $owner_guid GUID of owner of annotation
+ * @param int $limit Limit of results
+ * @param int $offset Offset of results
+ * @param string $orderdir Order of results
+ * @param bool $count Return count or entities
+ *
+ * @return unknown
+ */
+function get_entities_from_annotation_count($entity_type = "", $entity_subtype = "", $name = "", $mdname = '', $mdvalue = '', $owner_guid = 0, $limit = 10, $offset = 0, $orderdir = 'desc', $count = false) {
+
+ $msg = 'get_entities_from_annotation_count() is deprecated by elgg_get_entities_from_annotation_calculation().';
+
+ elgg_deprecated_notice($msg, 1.8);
+
+ $options = array();
+
+ $options['calculation'] = 'sum';
+
+ if ($entity_type) {
+ $options['types'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtypes'] = $entity_subtype;
+ }
+
+ $options['annotation_names'] = $name;
+
+ if ($mdname) {
+ $options['metadata_names'] = $mdname;
+ }
+
+ if ($mdvalue) {
+ $options['metadata_values'] = $mdvalue;
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ $options['limit'] = $limit;
+ $options['offset'] = $offset;
+
+ $options['order_by'] = "annotation_calculation $orderdir";
+
+ $options['count'] = $count;
+
+ return elgg_get_entities_from_annotation_calculation($options);
+}
+
+/**
+ * Lists entities by the totals of a particular kind of annotation
+ *
+ * @deprecated 1.8 Use elgg_list_entities_from_annotation_calculation()
+ *
+ * @param string $entity_type Type of entity.
+ * @param string $entity_subtype Subtype of entity.
+ * @param string $name Name of annotation.
+ * @param int $limit Maximum number of results to return.
+ * @param int $owner_guid Owner.
+ * @param int $group_guid Group container. Currently only supported if entity_type is object
+ * @param boolean $asc Whether to list in ascending or descending order (default: desc)
+ * @param boolean $fullview Whether to display the entities in full
+ * @param boolean $listtypetoggle Can the 'gallery' view can be displayed (default: no)
+ * @param boolean $pagination Add pagination
+ * @param string $orderdir Order desc or asc
+ *
+ * @return string Formatted entity list
+ */
+function list_entities_from_annotation_count($entity_type = "", $entity_subtype = "", $name = "", $limit = 10, $owner_guid = 0, $group_guid = 0, $asc = false, $fullview = true, $listtypetoggle = false, $pagination = true, $orderdir = 'desc') {
+
+ $msg = 'list_entities_from_annotation_count() is deprecated by elgg_list_entities_from_annotation_calculation().';
+
+ elgg_deprecated_notice($msg, 1.8);
+
+ $options = array();
+
+ $options['calculation'] = 'sum';
+
+ if ($entity_type) {
+ $options['types'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtypes'] = $entity_subtype;
+ }
+
+ $options['annotation_names'] = $name;
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ $options['full_view'] = $fullview;
+
+ $options['list_type_toggle'] = $listtypetoggle;
+
+ $options['pagination'] = $pagination;
+
+ $options['limit'] = $limit;
+
+ $options['order_by'] = "annotation_calculation $orderdir";
+
+ return elgg_get_entities_from_annotation_calculation($options);
+}
+
+/**
+ * Adds an entry in $CONFIG[$register_name][$subregister_name].
+ *
+ * @deprecated 1.8 Use the new menu system.
+ *
+ * This is only used for the site-wide menu. See {@link add_menu()}.
+ *
+ * @param string $register_name The name of the top-level register
+ * @param string $subregister_name The name of the subregister
+ * @param mixed $subregister_value The value of the subregister
+ * @param array $children_array Optionally, an array of children
+ *
+ * @return true|false Depending on success
+ */
+function add_to_register($register_name, $subregister_name, $subregister_value, $children_array = array()) {
+ elgg_deprecated_notice("add_to_register() has been deprecated", 1.8);
+ global $CONFIG;
+
+ if (empty($register_name) || empty($subregister_name)) {
+ return false;
+ }
+
+ if (!isset($CONFIG->registers)) {
+ $CONFIG->registers = array();
+ }
+
+ if (!isset($CONFIG->registers[$register_name])) {
+ $CONFIG->registers[$register_name] = array();
+ }
+
+ $subregister = new stdClass;
+ $subregister->name = $subregister_name;
+ $subregister->value = $subregister_value;
+
+ if (is_array($children_array)) {
+ $subregister->children = $children_array;
+ }
+
+ $CONFIG->registers[$register_name][$subregister_name] = $subregister;
+ return true;
+}
+
+/**
+ * Removes a register entry from $CONFIG[register_name][subregister_name]
+ *
+ * @deprecated 1.8 Use the new menu system.
+ *
+ * This is used to by {@link remove_menu()} to remove site-wide menu items.
+ *
+ * @param string $register_name The name of the top-level register
+ * @param string $subregister_name The name of the subregister
+ *
+ * @return true|false Depending on success
+ * @since 1.7.0
+ */
+function remove_from_register($register_name, $subregister_name) {
+ elgg_deprecated_notice("remove_from_register() has been deprecated", 1.8);
+ global $CONFIG;
+
+ if (empty($register_name) || empty($subregister_name)) {
+ return false;
+ }
+
+ if (!isset($CONFIG->registers)) {
+ return false;
+ }
+
+ if (!isset($CONFIG->registers[$register_name])) {
+ return false;
+ }
+
+ if (isset($CONFIG->registers[$register_name][$subregister_name])) {
+ unset($CONFIG->registers[$register_name][$subregister_name]);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * If it exists, returns a particular register as an array
+ *
+ * @deprecated 1.8 Use the new menu system
+ *
+ * @param string $register_name The name of the register
+ *
+ * @return array|false Depending on success
+ */
+function get_register($register_name) {
+ elgg_deprecated_notice("get_register() has been deprecated", 1.8);
+ global $CONFIG;
+
+ if ($register_name == 'menu') {
+ // backward compatible code for site menu
+ $menu = $CONFIG->menus['site'];
+ $builder = new ElggMenuBuilder($menu);
+ $menu_items = $builder->getMenu('text');
+ $menu_items = $menu_items['default'];
+
+ $menu = array();
+ foreach ($menu_items as $item) {
+ $subregister = new stdClass;
+ $subregister->name = $item->getText();
+ $subregister->value = $item->getHref();
+ $menu[$subregister->name] = $subregister;
+ }
+ return $menu;
+ }
+
+ if (isset($CONFIG->registers[$register_name])) {
+ return $CONFIG->registers[$register_name];
+ }
+
+ return false;
+}
+
+/**
+ * Deprecated events core function. Code divided between elgg_register_event_handler()
+ * and trigger_elgg_event().
+ *
+ * @deprecated 1.8 Use explicit register/trigger event functions
+ *
+ * @param string $event The type of event (eg 'init', 'update', 'delete')
+ * @param string $object_type The type of object (eg 'system', 'blog', 'user')
+ * @param string $function The name of the function that will handle the event
+ * @param int $priority Priority to call handler. Lower numbers called first (default 500)
+ * @param boolean $call Set to true to call the event rather than add to it (default false)
+ * @param mixed $object Optionally, the object the event is being performed on (eg a user)
+ *
+ * @return true|false Depending on success
+ */
+function events($event = "", $object_type = "", $function = "", $priority = 500, $call = false, $object = null) {
+
+ elgg_deprecated_notice('events() has been deprecated.', 1.8);
+
+ // leaving this here just in case someone was directly calling this internal function
+ if (!$call) {
+ return elgg_register_event_handler($event, $object_type, $function, $priority);
+ } else {
+ return trigger_elgg_event($event, $object_type, $object);
+ }
+}
+
+/**
+ * Alias function for events, that registers a function to a particular kind of event
+ *
+ * @deprecated 1.8 Use elgg_register_event_handler() instead
+ *
+ * @param string $event The event type
+ * @param string $object_type The object type
+ * @param string $function The function name
+ * @return true|false Depending on success
+ */
+function register_elgg_event_handler($event, $object_type, $callback, $priority = 500) {
+ elgg_deprecated_notice("register_elgg_event_handler() was deprecated by elgg_register_event_handler()", 1.8);
+ return elgg_register_event_handler($event, $object_type, $callback, $priority);
+}
+
+/**
+ * Unregisters a function to a particular kind of event
+ *
+ * @deprecated 1.8 Use elgg_unregister_event_handler instead
+ *
+ * @param string $event The event type
+ * @param string $object_type The object type
+ * @param string $function The function name
+ * @since 1.7.0
+ */
+function unregister_elgg_event_handler($event, $object_type, $callback) {
+ elgg_deprecated_notice('unregister_elgg_event_handler => elgg_unregister_event_handler', 1.8);
+ elgg_unregister_event_handler($event, $object_type, $callback);
+}
+
+/**
+ * Alias function for events, that triggers a particular kind of event
+ *
+ * @deprecated 1.8 Use elgg_trigger_event() instead
+ *
+ * @param string $event The event type
+ * @param string $object_type The object type
+ * @param string $function The function name
+ * @return true|false Depending on success
+ */
+function trigger_elgg_event($event, $object_type, $object = null) {
+ elgg_deprecated_notice('trigger_elgg_event() was deprecated by elgg_trigger_event()', 1.8);
+ return elgg_trigger_event($event, $object_type, $object);
+}
+
+/**
+ * Register a function to a plugin hook for a particular entity type, with a given priority.
+ *
+ * @deprecated 1.8 Use elgg_register_plugin_hook_handler() instead
+ *
+ * eg if you want the function "export_user" to be called when the hook "export" for "user" entities
+ * is run, use:
+ *
+ * register_plugin_hook("export", "user", "export_user");
+ *
+ * "all" is a valid value for both $hook and $entity_type. "none" is a valid value for $entity_type.
+ *
+ * The export_user function would then be defined as:
+ *
+ * function export_user($hook, $entity_type, $returnvalue, $params);
+ *
+ * Where $returnvalue is the return value returned by the last function returned by the hook, and
+ * $params is an array containing a set of parameters (or nothing).
+ *
+ * @param string $hook The name of the hook
+ * @param string $entity_type The name of the type of entity (eg "user", "object" etc)
+ * @param string $function The name of a valid function to be run
+ * @param string $priority The priority - 0 is first, 1000 last, default is 500
+ * @return true|false Depending on success
+ */
+function register_plugin_hook($hook, $type, $callback, $priority = 500) {
+ elgg_deprecated_notice("register_plugin_hook() was deprecated by elgg_register_plugin_hook_handler()", 1.8);
+ return elgg_register_plugin_hook_handler($hook, $type, $callback, $priority);
+}
+
+/**
+ * Unregister a function to a plugin hook for a particular entity type
+ *
+ * @deprecated 1.8 Use elgg_unregister_plugin_hook_handler() instead
+ *
+ * @param string $hook The name of the hook
+ * @param string $entity_type The name of the type of entity (eg "user", "object" etc)
+ * @param string $function The name of a valid function to be run
+ * @since 1.7.0
+ */
+function unregister_plugin_hook($hook, $entity_type, $callback) {
+ elgg_deprecated_notice("unregister_plugin_hook() was deprecated by elgg_unregister_plugin_hook_handler()", 1.8);
+ elgg_unregister_plugin_hook_handler($hook, $entity_type, $callback);
+}
+
+/**
+ * Triggers a plugin hook, with various parameters as an array. For example, to provide
+ * a 'foo' hook that concerns an entity of type 'bar', with a parameter called 'param1'
+ * with value 'value1', that by default returns true, you'd call:
+ *
+ * @deprecated 1.8 Use elgg_trigger_plugin_hook() instead
+ *
+ * trigger_plugin_hook('foo', 'bar', array('param1' => 'value1'), true);
+ *
+ * @see register_plugin_hook
+ * @param string $hook The name of the hook to trigger
+ * @param string $entity_type The name of the entity type to trigger it for (or "all", or "none")
+ * @param array $params Any parameters. It's good practice to name the keys, i.e. by using array('name' => 'value', 'name2' => 'value2')
+ * @param mixed $returnvalue An initial return value
+ * @return mixed|null The cumulative return value for the plugin hook functions
+ */
+function trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null) {
+ elgg_deprecated_notice("trigger_plugin_hook() was deprecated by elgg_trigger_plugin_hook()", 1.8);
+ return elgg_trigger_plugin_hook($hook, $type, $params, $returnvalue);
+}
+
+/**
+ * Checks if code is being called from a certain function.
+ *
+ * To use, call this function with the function name (and optional
+ * file location) that it has to be called from, it will either
+ * return true or false.
+ *
+ * e.g.
+ *
+ * function my_secure_function()
+ * {
+ * if (!call_gatekeeper("my_call_function"))
+ * return false;
+ *
+ * ... do secure stuff ...
+ * }
+ *
+ * function my_call_function()
+ * {
+ * // will work
+ * my_secure_function();
+ * }
+ *
+ * function bad_function()
+ * {
+ * // Will not work
+ * my_secure_function();
+ * }
+ *
+ * @param mixed $function The function that this function must have in its call stack,
+ * to test against a method pass an array containing a class and
+ * method name.
+ * @param string $file Optional file that the function must reside in.
+ *
+ * @return bool
+ *
+ * @deprecated 1.8 A neat but pointless function
+ */
+function call_gatekeeper($function, $file = "") {
+ elgg_deprecated_notice("call_gatekeeper() is neat but pointless", 1.8);
+ // Sanity check
+ if (!$function) {
+ return false;
+ }
+
+ // Check against call stack to see if this is being called from the correct location
+ $callstack = debug_backtrace();
+ $stack_element = false;
+
+ foreach ($callstack as $call) {
+ if (is_array($function)) {
+ if ((strcmp($call['class'], $function[0]) == 0) && (strcmp($call['function'], $function[1]) == 0)) {
+ $stack_element = $call;
+ }
+ } else {
+ if (strcmp($call['function'], $function) == 0) {
+ $stack_element = $call;
+ }
+ }
+ }
+
+ if (!$stack_element) {
+ return false;
+ }
+
+ // If file then check that this it is being called from this function
+ if ($file) {
+ $mirror = null;
+
+ if (is_array($function)) {
+ $mirror = new ReflectionMethod($function[0], $function[1]);
+ } else {
+ $mirror = new ReflectionFunction($function);
+ }
+
+ if ((!$mirror) || (strcmp($file, $mirror->getFileName()) != 0)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * This function checks to see if it is being called at somepoint by a function defined somewhere
+ * on a given path (optionally including subdirectories).
+ *
+ * This function is similar to call_gatekeeper() but returns true if it is being called
+ * by a method or function which has been defined on a given path or by a specified file.
+ *
+ * @param string $path The full path and filename that this function must have
+ * in its call stack If a partial path is given and
+ * $include_subdirs is true, then the function will return
+ * true if called by any function in or below the specified path.
+ * @param bool $include_subdirs Are subdirectories of the path ok, or must you specify an
+ * absolute path and filename.
+ * @param bool $strict_mode If true then the calling method or function must be directly
+ * called by something on $path, if false the whole call stack is
+ * searched.
+ *
+ * @return void
+ *
+ * @deprecated 1.8 A neat but pointless function
+ */
+function callpath_gatekeeper($path, $include_subdirs = true, $strict_mode = false) {
+ elgg_deprecated_notice("callpath_gatekeeper() is neat but pointless", 1.8);
+
+ global $CONFIG;
+
+ $path = sanitise_string($path);
+
+ if ($path) {
+ $callstack = debug_backtrace();
+
+ foreach ($callstack as $call) {
+ $call['file'] = str_replace("\\", "/", $call['file']);
+
+ if ($include_subdirs) {
+ if (strpos($call['file'], $path) === 0) {
+
+ if ($strict_mode) {
+ $callstack[1]['file'] = str_replace("\\", "/", $callstack[1]['file']);
+ if ($callstack[1] === $call) {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+ } else {
+ if (strcmp($path, $call['file']) == 0) {
+ if ($strict_mode) {
+ if ($callstack[1] === $call) {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+ }
+
+ }
+ return false;
+ }
+
+ if (isset($CONFIG->debug)) {
+ system_message("Gatekeeper'd function called from {$callstack[1]['file']}:" . "{$callstack[1]['line']}\n\nStack trace:\n\n" . print_r($callstack, true));
+ }
+
+ return false;
+}
+
+/**
+ * Returns SQL where clause for owner and containers.
+ *
+ * @deprecated 1.8 Use elgg_get_guid_based_where_sql();
+ *
+ * @param string $table Entity table prefix as defined in SELECT...FROM entities $table
+ * @param NULL|array $owner_guids Owner GUIDs
+ *
+ * @return FALSE|str
+ * @since 1.7.0
+ * @access private
+ */
+function elgg_get_entity_owner_where_sql($table, $owner_guids) {
+ elgg_deprecated_notice('elgg_get_entity_owner_where_sql() is deprecated by elgg_get_guid_based_where_sql().', 1.8);
+
+ return elgg_get_guid_based_where_sql("{$table}.owner_guid", $owner_guids);
+}
+
+/**
+ * Returns SQL where clause for containers.
+ *
+ * @deprecated 1.8 Use elgg_get_guid_based_where_sql();
+ *
+ * @param string $table Entity table prefix as defined in
+ * SELECT...FROM entities $table
+ * @param NULL|array $container_guids Array of container guids
+ *
+ * @return FALSE|string
+ * @since 1.7.0
+ * @access private
+ */
+function elgg_get_entity_container_where_sql($table, $container_guids) {
+ elgg_deprecated_notice('elgg_get_entity_container_where_sql() is deprecated by elgg_get_guid_based_where_sql().', 1.8);
+
+ return elgg_get_guid_based_where_sql("{$table}.container_guid", $container_guids);
+}
+
+/**
+ * Returns SQL where clause for site entities
+ *
+ * @deprecated 1.8 Use elgg_get_guid_based_where_sql()
+ *
+ * @param string $table Entity table prefix as defined in SELECT...FROM entities $table
+ * @param NULL|array $site_guids Array of site guids
+ *
+ * @return FALSE|string
+ * @since 1.7.0
+ * @access private
+ */
+function elgg_get_entity_site_where_sql($table, $site_guids) {
+ elgg_deprecated_notice('elgg_get_entity_site_where_sql() is deprecated by elgg_get_guid_based_where_sql().', 1.8);
+
+ return elgg_get_guid_based_where_sql("{$table}.site_guid", $site_guids);
+}
+
+/**
+ * Return an array of objects in a given container.
+ *
+ * @see get_entities()
+ *
+ * @param int $group_guid The container (defaults to current page owner)
+ * @param string $subtype The subtype
+ * @param int $owner_guid Owner
+ * @param int $site_guid The site
+ * @param string $order_by Order
+ * @param int $limit Limit on number of elements to return, by default 10.
+ * @param int $offset Where to start, by default 0.
+ * @param bool $count Whether to return the entities or a count of them.
+ *
+ * @return array|false
+ * @deprecated 1.8 Use elgg_get_entities() instead
+ */
+function get_objects_in_group($group_guid, $subtype = "", $owner_guid = 0, $site_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = FALSE) {
+ elgg_deprecated_notice("get_objects_in_group was deprected in 1.8. Use elgg_get_entities() instead", 1.8);
+
+ global $CONFIG;
+
+ if ($subtype === FALSE || $subtype === null || $subtype === 0) {
+ return FALSE;
+ }
+
+ if ($order_by == "") {
+ $order_by = "e.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;
+ }
+
+ $container_guid = (int)$group_guid;
+ if ($container_guid == 0) {
+ $container_guid = elgg_get_page_owner_guid();
+ }
+
+ $where = array();
+
+ $where[] = "e.type='object'";
+
+ if (!empty($subtype)) {
+ if (!$subtype = get_subtype_id('object', $subtype)) {
+ return FALSE;
+ }
+ $where[] = "e.subtype=$subtype";
+ }
+ if ($owner_guid != "") {
+ if (!is_array($owner_guid)) {
+ $owner_guid = (int)$owner_guid;
+ $where[] = "e.container_guid = '$owner_guid'";
+ } else if (sizeof($owner_guid) > 0) {
+ // Cast every element to the owner_guid array to int
+ $owner_guid = array_map("sanitise_int", $owner_guid);
+ $owner_guid = implode(",", $owner_guid);
+ $where[] = "e.container_guid in ({$owner_guid})";
+ }
+ }
+ if ($site_guid > 0) {
+ $where[] = "e.site_guid = {$site_guid}";
+ }
+
+ if ($container_guid > 0) {
+ $where[] = "e.container_guid = {$container_guid}";
+ }
+
+ if (!$count) {
+ $query = "SELECT * from {$CONFIG->dbprefix}entities e" . " join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid where ";
+ } else {
+ $query = "SELECT count(e.guid) as total from {$CONFIG->dbprefix}entities e" . " join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid where ";
+ }
+ foreach ($where as $w) {
+ $query .= " $w and ";
+ }
+
+ // Add access controls
+ $query .= get_access_sql_suffix('e');
+ if (!$count) {
+ $query .= " order by $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;
+ }
+}
+
+/**
+ * Lists entities that belong to a group.
+ *
+ * @param string $subtype The arbitrary subtype of the entity
+ * @param int $owner_guid The GUID of the owning user
+ * @param int $container_guid The GUID of the containing group
+ * @param int $limit The number of entities to display per page (default: 10)
+ * @param bool $fullview Whether or not to display the full view (default: true)
+ * @param bool $listtypetoggle Whether or not to allow gallery view (default: true)
+ * @param bool $pagination Whether to display pagination (default: true)
+ *
+ * @return string List of parsed entities
+ *
+ * @see elgg_list_entities()
+ * @deprecated 1.8 Use elgg_list_entities() instead
+ */
+function list_entities_groups($subtype = "", $owner_guid = 0, $container_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true) {
+ elgg_deprecated_notice("list_entities_groups was deprecated in 1.8. Use elgg_list_entities() instead.", 1.8);
+ $offset = (int)get_input('offset');
+ $count = get_objects_in_group($container_guid, $subtype, $owner_guid, 0, "", $limit, $offset, true);
+ $entities = get_objects_in_group($container_guid, $subtype, $owner_guid, 0, "", $limit, $offset);
+
+ return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $listtypetoggle, $pagination);
+}
+
+/**
+ * Get all the entities from metadata from a group.
+ *
+ * @param int $group_guid The ID of the group.
+ * @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 GUID. 0 for current, -1 for any
+ * @param bool $count Return count instead of entities
+ *
+ * @return array|false
+ * @deprecated 1.8 Use elgg_get_entities_from_metadata()
+ */
+function get_entities_from_metadata_groups($group_guid, $meta_name, $meta_value = "", $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) {
+ elgg_deprecated_notice("get_entities_from_metadata_groups was deprecated in 1.8.", 1.8);
+ global $CONFIG;
+
+ $meta_n = get_metastring_id($meta_name);
+ $meta_v = get_metastring_id($meta_value);
+
+ $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)) {
+ 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;
+ }
+
+ $container_guid = (int)$group_guid;
+ if ($container_guid == 0) {
+ $container_guid = elgg_get_page_owner_guid();
+ }
+
+ $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 ($container_guid > 0) {
+ $where[] = "e.container_guid = {$container_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}";
+ }
+
+ if (!$count) {
+ $query = "SELECT distinct e.* ";
+ } else {
+ $query = "SELECT count(e.guid) as total ";
+ }
+
+ $query .= "from {$CONFIG->dbprefix}entities e" . " JOIN {$CONFIG->dbprefix}metadata m on e.guid = m.entity_guid " . " JOIN {$CONFIG->dbprefix}objects_entity o on e.guid = o.guid 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 ($row = get_data_row($query)) {
+ return $row->total;
+ }
+ }
+ return false;
+}
+
+/**
+ * As get_entities_from_metadata_groups() but with multiple entities.
+ *
+ * @param int $group_guid The ID of the group.
+ * @param array $meta_array Array of 'name' => 'value' pairs
+ * @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 GUID. 0 for current, -1 for any
+ * @param bool $count Return count of entities instead of entities
+ *
+ * @return int|array List of ElggEntities, or the total number if count is set to false
+ * @deprecated 1.8 Use elgg_get_entities_from_metadata()
+ */
+function get_entities_from_metadata_groups_multi($group_guid, $meta_array, $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) {
+ elgg_deprecated_notice("get_entities_from_metadata_groups_multi was deprecated in 1.8.", 1.8);
+
+ global $CONFIG;
+
+ if (!is_array($meta_array) || sizeof($meta_array) == 0) {
+ return false;
+ }
+
+ $where = array();
+
+ $mindex = 1;
+ $join = "";
+ foreach ($meta_array as $meta_name => $meta_value) {
+ $meta_n = get_metastring_id($meta_name);
+ $meta_v = get_metastring_id($meta_value);
+ $join .= " JOIN {$CONFIG->dbprefix}metadata m{$mindex} on e.guid = m{$mindex}.entity_guid" . " JOIN {$CONFIG->dbprefix}objects_entity o on e.guid = o.guid ";
+
+ if ($meta_name != "") {
+ $where[] = "m{$mindex}.name_id='$meta_n'";
+ }
+
+ if ($meta_value != "") {
+ $where[] = "m{$mindex}.value_id='$meta_v'";
+ }
+
+ $mindex++;
+ }
+
+ $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);
+ $owner_guid = (int)$owner_guid;
+
+ $site_guid = (int)$site_guid;
+ if ($site_guid == 0) {
+ $site_guid = $CONFIG->site_guid;
+ }
+
+ //$access = get_access_list();
+
+ if ($entity_type != "") {
+ $where[] = "e.type = '{$entity_type}'";
+ }
+
+ if ($entity_subtype) {
+ $where[] = "e.subtype = {$entity_subtype}";
+ }
+
+ if ($site_guid > 0) {
+ $where[] = "e.site_guid = {$site_guid}";
+ }
+
+ if ($owner_guid > 0) {
+ $where[] = "e.owner_guid = {$owner_guid}";
+ }
+
+ if ($container_guid > 0) {
+ $where[] = "e.container_guid = {$container_guid}";
+ }
+
+ if ($count) {
+ $query = "SELECT count(e.guid) as total ";
+ } else {
+ $query = "SELECT distinct e.* ";
+ }
+
+ $query .= " from {$CONFIG->dbprefix}entities e {$join} where";
+ foreach ($where as $w) {
+ $query .= " $w and ";
+ }
+ $query .= get_access_sql_suffix("e"); // Add access controls
+
+ 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;
+}
+
+/**
+ * List items within a given geographic area.
+ *
+ * @param real $lat Latitude
+ * @param real $long Longitude
+ * @param real $radius The radius
+ * @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 display per page (default: 10)
+ * @param bool $fullview Whether or not to display the full view (default: true)
+ * @param bool $listtypetoggle Whether or not to allow gallery view
+ * @param bool $navigation Display pagination? Default: true
+ *
+ * @return string A viewable list of entities
+ * @deprecated 1.8 Use elgg_get_entities_from_location()
+ */
+function list_entities_in_area($lat, $long, $radius, $type = "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) {
+ elgg_deprecated_notice('list_entities_in_area() was deprecated. Use elgg_list_entities_from_location()', 1.8);
+
+ $options = array();
+
+ $options['latitude'] = $lat;
+ $options['longitude'] = $long;
+ $options['distance'] = $radius;
+
+ if ($type) {
+ $options['types'] = $type;
+ }
+
+ if ($subtype) {
+ $options['subtypes'] = $subtype;
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ $options['limit'] = $limit;
+
+ $options['full_view'] = $fullview;
+ $options['list_type_toggle'] = $listtypetoggle;
+ $options['pagination'] = $pagination;
+
+ return elgg_list_entities_from_location($options);
+}
+
+/**
+ * List entities in a given location
+ *
+ * @param string $location Location
+ * @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 display per page (default: 10)
+ * @param bool $fullview Whether or not to display the full view (default: true)
+ * @param bool $listtypetoggle Whether or not to allow gallery view
+ * @param bool $navigation Display pagination? Default: true
+ *
+ * @return string A viewable list of entities
+ * @deprecated 1.8 Use elgg_list_entities_from_location()
+ */
+function list_entities_location($location, $type = "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) {
+ elgg_deprecated_notice('list_entities_location() was deprecated. Use elgg_list_entities_from_metadata()', 1.8);
+
+ return list_entities_from_metadata('location', $location, $type, $subtype, $owner_guid, $limit, $fullview, $listtypetoggle, $navigation);
+}
+
+/**
+ * Return entities within a given geographic area.
+ *
+ * @param float $lat Latitude
+ * @param float $long Longitude
+ * @param float $radius The radius
+ * @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 Count entities
+ * @param int $site_guid Site GUID. 0 for current, -1 for any
+ * @param int|array $container_guid Container GUID
+ *
+ * @return array A list of entities.
+ * @deprecated 1.8 Use elgg_get_entities_from_location()
+ */
+function get_entities_in_area($lat, $long, $radius, $type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = NULL) {
+ elgg_deprecated_notice('get_entities_in_area() was deprecated by elgg_get_entities_from_location()!', 1.8);
+
+ $options = array();
+
+ $options['latitude'] = $lat;
+ $options['longitude'] = $long;
+ $options['distance'] = $radius;
+
+ // set container_guid to owner_guid to emulate old functionality
+ if ($owner_guid != "") {
+ if (is_null($container_guid)) {
+ $container_guid = $owner_guid;
+ }
+ }
+
+ if ($type) {
+ $options['types'] = $type;
+ }
+
+ if ($subtype) {
+ $options['subtypes'] = $subtype;
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ if ($container_guid) {
+ if (is_array($container_guid)) {
+ $options['container_guids'] = $container_guid;
+ } else {
+ $options['container_guid'] = $container_guid;
+ }
+ }
+
+ $options['limit'] = $limit;
+
+ if ($offset) {
+ $options['offset'] = $offset;
+ }
+
+ if ($order_by) {
+ $options['order_by'];
+ }
+
+ if ($site_guid) {
+ $options['site_guid'];
+ }
+
+ if ($count) {
+ $options['count'] = $count;
+ }
+
+ return elgg_get_entities_from_location($options);
+}
+
+/**
+ * Return a list of entities suitable for display based on the given search criteria.
+ *
+ * @see elgg_view_entity_list
+ *
+ * @deprecated 1.8 Use elgg_list_entities_from_metadata
+ *
+ * @param mixed $meta_name Metadata name to search on
+ * @param mixed $meta_value The value to match, optionally
+ * @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 Number of entities to display per page
+ * @param bool $fullview WDisplay the full view (default: true)
+ * @param bool $listtypetoggle Allow users to toggle to the gallery view. Default: true
+ * @param bool $pagination Display pagination? Default: true
+ * @param bool $case_sensitive Case sensitive metadata names?
+ *
+ * @return string
+ *
+ * @return string A list of entities suitable for display
+ */
+function list_entities_from_metadata($meta_name, $meta_value = "", $entity_type = ELGG_ENTITIES_ANY_VALUE, $entity_subtype = ELGG_ENTITIES_ANY_VALUE, $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true, $case_sensitive = true) {
+
+ elgg_deprecated_notice('list_entities_from_metadata() was deprecated by elgg_list_entities_from_metadata()!', 1.8);
+
+ $offset = (int)get_input('offset');
+ $limit = (int)$limit;
+ $options = array(
+ 'metadata_name' => $meta_name,
+ 'metadata_value' => $meta_value,
+ 'type' => $entity_type,
+ 'subtype' => $entity_subtype,
+ 'limit' => $limit,
+ 'offset' => $offset,
+ 'count' => TRUE,
+ 'metadata_case_sensitive' => $case_sensitive
+ );
+
+ // previous function allowed falsy $owner_guid for anything
+ if ($owner_guid) {
+ $options['owner_guid'] = $owner_guid;
+ }
+
+ $count = elgg_get_entities_from_metadata($options);
+
+ $options['count'] = FALSE;
+ $entities = elgg_get_entities_from_metadata($options);
+
+ return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $listtypetoggle, $pagination);
+}
+
+/**
+ * Returns a viewable list of entities based on the given search criteria.
+ *
+ * @see elgg_view_entity_list
+ *
+ * @param array $meta_array Array of 'name' => 'value' pairs
+ * @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 bool $fullview WDisplay the full view (default: true)
+ * @param bool $listtypetoggle Allow users to toggle to the gallery view. Default: true
+ * @param bool $pagination Display pagination? Default: true
+ *
+ * @return string List of ElggEntities suitable for display
+ *
+ * @deprecated 1.8 Use elgg_list_entities_from_metadata() instead
+ */
+function list_entities_from_metadata_multi($meta_array, $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $listtypetoggle = true, $pagination = true) {
+ elgg_deprecated_notice(elgg_echo('deprecated:function', array(
+ 'list_entities_from_metadata_multi', 'elgg_get_entities_from_metadata')), 1.8);
+
+ $offset = (int)get_input('offset');
+ $limit = (int)$limit;
+ $count = get_entities_from_metadata_multi($meta_array, $entity_type, $entity_subtype, $owner_guid, $limit, $offset, "", $site_guid, true);
+ $entities = get_entities_from_metadata_multi($meta_array, $entity_type, $entity_subtype, $owner_guid, $limit, $offset, "", $site_guid, false);
+
+ return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $listtypetoggle, $pagination);
+}
+
+/**
+ * Deprecated by elgg_register_menu_item(). Set $menu_name to 'page'.
+ *
+ * @see elgg_register_menu_item()
+ * @deprecated 1.8 Use the new menu system
+ *
+ * @param string $label The label
+ * @param string $link The link
+ * @param string $group The group to store item in
+ * @param boolean $onclick Add a confirmation when clicked?
+ * @param boolean $selected Is menu item selected
+ *
+ * @return bool
+ */
+function add_submenu_item($label, $link, $group = 'default', $onclick = false, $selected = NULL) {
+ elgg_deprecated_notice('add_submenu_item was deprecated by elgg_register_menu_item', 1.8);
+
+ // submenu items were added in the page setup hook usually by checking
+ // the context. We'll pass in the current context here, which will
+ // emulate that effect.
+ // if context == 'main' (default) it probably means they always wanted
+ // the menu item to show up everywhere.
+ $context = elgg_get_context();
+
+ if ($context == 'main') {
+ $context = 'all';
+ }
+
+ $item = array('name' => $label, 'text' => $label, 'href' => $link, 'context' => $context,
+ 'section' => $group,);
+
+ if ($selected) {
+ $item['selected'] = true;
+ }
+
+ if ($onclick) {
+ $js = "onclick=\"javascript:return confirm('" . elgg_echo('deleteconfirm') . "')\"";
+ $item['vars'] = array('js' => $js);
+ }
+
+ return elgg_register_menu_item('page', $item);
+}
+
+/**
+ * Remove an item from submenu by label
+ *
+ * @deprecated 1.8 Use the new menu system
+ * @see elgg_unregister_menu_item()
+ *
+ * @param string $label The item label
+ * @param string $group The submenu group (default "a")
+ * @return bool whether the item was removed or not
+ * @since 1.7.8
+ */
+function remove_submenu_item($label, $group = 'a') {
+ elgg_deprecated_notice('remove_submenu_item was deprecated by elgg_unregister_menu_item', 1.8);
+
+ return elgg_unregister_menu_item('page', $label);
+}
+
+/**
+ * Use elgg_view_menu(). Set $menu_name to 'owner_block'.
+ *
+ * @see elgg_view_menu()
+ * @deprecated 1.8 Use the new menu system. elgg_view_menu()
+ *
+ * @return string
+ */
+function get_submenu() {
+ elgg_deprecated_notice("get_submenu() has been deprecated by elgg_view_menu()", 1.8);
+ return elgg_view_menu('owner_block', array('entity' => $owner,
+ 'class' => 'elgg-menu-owner-block',));
+}
+
+/**
+ * Adds an item to the site-wide menu.
+ *
+ * You can obtain the menu array by calling {@link get_register('menu')}
+ *
+ * @param string $menu_name The name of the menu item
+ * @param string $menu_url The URL of the page
+ * @param array $menu_children Optionally, an array of submenu items (not used)
+ * @param string $context (not used)
+ *
+ * @return true|false Depending on success
+ * @deprecated 1.8 use elgg_register_menu_item() for the menu 'site'
+ */
+function add_menu($menu_name, $menu_url, $menu_children = array(), $context = "") {
+ elgg_deprecated_notice('add_menu() deprecated by elgg_register_menu_item()', 1.8);
+
+ return elgg_register_menu_item('site', array('name' => $menu_name, 'text' => $menu_name,
+ 'href' => $menu_url,));
+}
+
+/**
+ * Removes an item from the menu register
+ *
+ * @param string $menu_name The name of the menu item
+ *
+ * @return true|false Depending on success
+ * @deprecated 1.8 Use the new menu system
+ */
+function remove_menu($menu_name) {
+ elgg_deprecated_notice("remove_menu() deprecated by elgg_unregister_menu_item()", 1.8);
+ return elgg_unregister_menu_item('site', $menu_name);
+}
+
+/**
+ * When given a title, returns a version suitable for inclusion in a URL
+ *
+ * @param string $title The title
+ *
+ * @return string The optimised title
+ * @deprecated 1.8 Use elgg_get_friendly_title()
+ */
+function friendly_title($title) {
+ elgg_deprecated_notice('friendly_title was deprecated by elgg_get_friendly_title', 1.8);
+ return elgg_get_friendly_title($title);
+}
+
+/**
+ * Displays a UNIX timestamp in a friendly way (eg "less than a minute ago")
+ *
+ * @param int $time A UNIX epoch timestamp
+ *
+ * @return string The friendly time
+ * @deprecated 1.8 Use elgg_view_friendly_time()
+ */
+function friendly_time($time) {
+ elgg_deprecated_notice('friendly_time was deprecated by elgg_view_friendly_time', 1.8);
+ return elgg_view_friendly_time($time);
+}
+
+/**
+ * Filters a string into an array of significant words
+ *
+ * @deprecated 1.8 Don't use this.
+ *
+ * @param string $string A string
+ *
+ * @return array
+ */
+function filter_string($string) {
+ elgg_deprecated_notice('filter_string() was deprecated!', 1.8);
+
+ // Convert it to lower and trim
+ $string = strtolower($string);
+ $string = trim($string);
+
+ // Remove links and email addresses
+ // match protocol://address/path/file.extension?some=variable&another=asf%
+ $string = preg_replace("/\s([a-zA-Z]+:\/\/[a-z][a-z0-9\_\.\-]*[a-z]{2,6}" . "[a-zA-Z0-9\/\*\-\?\&\%\=]*)([\s|\.|\,])/iu", " ", $string);
+
+ // match www.something.domain/path/file.extension?some=variable&another=asf%
+ $string = preg_replace("/\s(www\.[a-z][a-z0-9\_\.\-]*[a-z]{2,6}" . "[a-zA-Z0-9\/\*\-\?\&\%\=]*)([\s|\.|\,])/iu", " ", $string);
+
+ // match name@address
+ $string = preg_replace("/\s([a-zA-Z][a-zA-Z0-9\_\.\-]*[a-zA-Z]" . "*\@[a-zA-Z][a-zA-Z0-9\_\.\-]*[a-zA-Z]{2,6})([\s|\.|\,])/iu", " ", $string);
+
+ // Sanitise the string; remove unwanted characters
+ $string = preg_replace('/\W/ui', ' ', $string);
+
+ // Explode it into an array
+ $terms = explode(' ', $string);
+
+ // Remove any blacklist terms
+ //$terms = array_filter($terms, 'remove_blacklist');
+
+ return $terms;
+}
+
+/**
+ * Returns true if the word in $input is considered significant
+ *
+ * @deprecated 1.8 Don't use this.
+ *
+ * @param string $input A word
+ *
+ * @return true|false
+ */
+function remove_blacklist($input) {
+ elgg_deprecated_notice('remove_blacklist() was deprecated!', 1.8);
+
+ global $CONFIG;
+
+ if (!is_array($CONFIG->wordblacklist)) {
+ return $input;
+ }
+
+ if (strlen($input) < 3 || in_array($input, $CONFIG->wordblacklist)) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Gets the guid of the entity that owns the current page.
+ *
+ * @deprecated 1.8 Use elgg_get_page_owner_guid()
+ *
+ * @return int The current page owner guid (0 if none).
+ */
+function page_owner() {
+ elgg_deprecated_notice('page_owner() was deprecated by elgg_get_page_owner_guid().', 1.8);
+ return elgg_get_page_owner_guid();
+}
+
+/**
+ * Gets the owner entity for the current page.
+ *
+ * @deprecated 1.8 Use elgg_get_page_owner_entity()
+ * @return ElggEntity|false The current page owner or false if none.
+ */
+function page_owner_entity() {
+ elgg_deprecated_notice('page_owner_entity() was deprecated by elgg_get_page_owner_entity().', 1.8);
+ return elgg_get_page_owner_entity();
+}
+
+/**
+ * Registers a page owner handler function
+ *
+ * @param string $functionname The callback function
+ *
+ * @deprecated 1.8 Use the 'page_owner', 'system' plugin hook
+ * @return void
+ */
+function add_page_owner_handler($functionname) {
+ elgg_deprecated_notice("add_page_owner_handler() was deprecated by the plugin hook 'page_owner', 'system'.", 1.8);
+}
+
+/**
+ * Set a page owner entity
+ *
+ * @param int $entitytoset The GUID of the entity
+ *
+ * @deprecated 1.8 Use elgg_set_page_owner_guid()
+ * @return void
+ */
+function set_page_owner($entitytoset = -1) {
+ elgg_deprecated_notice('set_page_owner() was deprecated by elgg_set_page_owner_guid().', 1.8);
+ elgg_set_page_owner_guid($entitytoset);
+}
+
+/**
+ * Sets the functional context of a page
+ *
+ * @deprecated 1.8 Use elgg_set_context()
+ *
+ * @param string $context The context of the page
+ *
+ * @return mixed Either the context string, or false on failure
+ */
+function set_context($context) {
+ elgg_deprecated_notice('set_context() was deprecated by elgg_set_context().', 1.8);
+ elgg_set_context($context);
+ if (empty($context)) {
+ return false;
+ }
+ return $context;
+}
+
+/**
+ * Returns the functional context of a page
+ *
+ * @deprecated 1.8 Use elgg_get_context()
+ *
+ * @return string The context, or 'main' if no context has been provided
+ */
+function get_context() {
+ elgg_deprecated_notice('get_context() was deprecated by elgg_get_context().', 1.8);
+ return elgg_get_context();
+
+ // @todo - used to set context based on calling script
+ // $context = get_plugin_name(true)
+}
+
+/**
+ * Returns a list of plugins to load, in the order that they should be loaded.
+ *
+ * @deprecated 1.8 Use elgg_get_plugin_ids_in_dir() or elgg_get_plugins()
+ *
+ * @return array List of plugins
+ */
+function get_plugin_list() {
+ elgg_deprecated_notice('get_plugin_list() is deprecated by elgg_get_plugin_ids_in_dir() or elgg_get_plugins()', 1.8);
+
+ $plugins = elgg_get_plugins('any');
+
+ $list = array();
+ if ($plugins) {
+ foreach ($plugins as $i => $plugin) {
+ // in <=1.7 this returned indexed by multiples of 10.
+ // uh...sure...why not.
+ $index = ($i + 1) * 10;
+ $list[$index] = $plugin->getID();
+ }
+ }
+
+ return $list;
+}
+
+/**
+ * Regenerates the list of known plugins and saves it to the current site
+ *
+ * Important: You should regenerate simplecache and the viewpath cache after executing this function
+ * otherwise you may experience view display artifacts. Do this with the following code:
+ *
+ * elgg_regenerate_simplecache();
+ * elgg_reset_system_cache();
+ *
+ * @deprecated 1.8 Use elgg_generate_plugin_entities() and elgg_set_plugin_priorities()
+ *
+ * @param array $pluginorder Optionally, a list of existing plugins and their orders
+ *
+ * @return array The new list of plugins and their orders
+ */
+function regenerate_plugin_list($pluginorder = FALSE) {
+ $msg = 'regenerate_plugin_list() is (sorta) deprecated by elgg_generate_plugin_entities() and'
+ . ' elgg_set_plugin_priorities().';
+ elgg_deprecated_notice($msg, 1.8);
+
+ // they're probably trying to set it?
+ if ($pluginorder) {
+ if (elgg_generate_plugin_entities()) {
+ // sort the plugins by the index numerically since we used
+ // weird indexes in the old system.
+ ksort($pluginorder, SORT_NUMERIC);
+ return elgg_set_plugin_priorities($pluginorder);
+ }
+ return false;
+ } else {
+ // they're probably trying to regenerate from disk?
+ return elgg_generate_plugin_entities();
+ }
+}
+
+/**
+ * Get the name of the most recent plugin to be called in the
+ * call stack (or the plugin that owns the current page, if any).
+ *
+ * i.e., if the last plugin was in /mod/foobar/, get_plugin_name would return foo_bar.
+ *
+ * @deprecated 1.8 Use elgg_get_calling_plugin_id()
+ *
+ * @param boolean $mainfilename If set to true, this will instead determine the
+ * context from the main script filename called by
+ * the browser. Default = false.
+ *
+ * @return string|false Plugin name, or false if no plugin name was called
+ */
+function get_plugin_name($mainfilename = false) {
+ elgg_deprecated_notice('get_plugin_name() is deprecated by elgg_get_calling_plugin_id()', 1.8);
+
+ return elgg_get_calling_plugin_id($mainfilename);
+}
+
+/**
+ * Load and parse a plugin manifest from a plugin XML file.
+ *
+ * @example plugins/manifest.xml Example 1.8-style manifest file.
+ *
+ * @deprecated 1.8 Use ElggPlugin->getManifest()
+ *
+ * @param string $plugin Plugin name.
+ * @return array of values
+ */
+function load_plugin_manifest($plugin) {
+ elgg_deprecated_notice('load_plugin_manifest() is deprecated by ElggPlugin->getManifest()', 1.8);
+
+ $xml_file = elgg_get_plugins_path() . "$plugin/manifest.xml";
+
+ try {
+ $manifest = new ElggPluginManifest($xml_file, $plugin);
+ } catch(Exception $e) {
+ return false;
+ }
+
+ return $manifest->getManifest();
+}
+
+/**
+ * This function checks a plugin manifest 'elgg_version' value against the current install
+ * returning TRUE if the elgg_version is >= the current install's version.
+ *
+ * @deprecated 1.8 Use ElggPlugin->canActivate()
+ *
+ * @param string $manifest_elgg_version_string The build version (eg 2009010201).
+ * @return bool
+ */
+function check_plugin_compatibility($manifest_elgg_version_string) {
+ elgg_deprecated_notice('check_plugin_compatibility() is deprecated by ElggPlugin->canActivate()', 1.8);
+
+ $version = get_version();
+
+ if (strpos($manifest_elgg_version_string, '.') === false) {
+ // Using version
+ $req_version = (int)$manifest_elgg_version_string;
+
+ return ($version >= $req_version);
+ }
+
+ return false;
+}
+
+/**
+ * Shorthand function for finding the plugin settings.
+ *
+ * @deprecated 1.8 Use elgg_get_calling_plugin_entity() or elgg_get_plugin_from_id()
+ *
+ * @param string $plugin_id Optional plugin id, if not specified
+ * then it is detected from where you are calling.
+ *
+ * @return mixed
+ */
+function find_plugin_settings($plugin_id = null) {
+ elgg_deprecated_notice('find_plugin_setting() is deprecated by elgg_get_calling_plugin_entity() or elgg_get_plugin_from_id()', 1.8);
+ if ($plugin_id) {
+ return elgg_get_plugin_from_id($plugin_id);
+ } else {
+ return elgg_get_calling_plugin_entity();
+ }
+}
+
+/**
+ * Return an array of installed plugins.
+ *
+ * @deprecated 1.8 use elgg_get_plugins()
+ *
+ * @param string $status any|enabled|disabled
+ * @return array
+ */
+function get_installed_plugins($status = 'all') {
+ global $CONFIG;
+
+ elgg_deprecated_notice('get_installed_plugins() was deprecated by elgg_get_plugins()', 1.8);
+
+ $plugins = elgg_get_plugins($status);
+
+ if (!$plugins) {
+ return array();
+ }
+
+ $installed_plugins = array();
+
+ foreach ($plugins as $plugin) {
+ if (!$plugin->isValid()) {
+ continue;
+ }
+
+ $include = true;
+
+ if ($status == 'enabled' && !$plugin->isActive()) {
+ $include = false;
+ } elseif ($status == 'disabled' && $plugin->isActive()) {
+ $include = true;
+ }
+
+ if ($include) {
+ $installed_plugins[$plugin->getID()] = array(
+ 'active' => $plugin->isActive(),
+ 'manifest' => $plugin->getManifest()->getManifest()
+ );
+ }
+ }
+
+ return $installed_plugins;
+}
+
+/**
+ * Enable a plugin for a site (default current site)
+ *
+ * Important: You should regenerate simplecache and the viewpath cache after executing this function
+ * otherwise you may experience view display artifacts. Do this with the following code:
+ *
+ * elgg_regenerate_simplecache();
+ * elgg_reset_system_cache();
+ *
+ * @deprecated 1.8 Use ElggPlugin->activate()
+ *
+ * @param string $plugin The plugin name.
+ * @param int $site_guid The site id, if not specified then this is detected.
+ *
+ * @return array
+ * @throws InvalidClassException
+ */
+function enable_plugin($plugin, $site_guid = null) {
+ elgg_deprecated_notice('enable_plugin() was deprecated by ElggPlugin->activate()', 1.8);
+
+ $plugin = sanitise_string($plugin);
+
+ $site_guid = (int) $site_guid;
+ if (!$site_guid) {
+ $site = get_config('site');
+ $site_guid = $site->guid;
+ }
+
+ try {
+ $plugin = new ElggPlugin($plugin);
+ } catch(Exception $e) {
+ return false;
+ }
+
+ if (!$plugin->canActivate($site_guid)) {
+ return false;
+ }
+
+ return $plugin->activate($site_guid);
+}
+
+/**
+ * Disable a plugin for a site (default current site)
+ *
+ * Important: You should regenerate simplecache and the viewpath cache after executing this function
+ * otherwise you may experience view display artifacts. Do this with the following code:
+ *
+ * elgg_regenerate_simplecache();
+ * elgg_reset_system_cache();
+ *
+ * @deprecated 1.8 Use ElggPlugin->deactivate()
+ *
+ * @param string $plugin The plugin name.
+ * @param int $site_guid The site id, if not specified then this is detected.
+ *
+ * @return bool
+ * @throws InvalidClassException
+ */
+function disable_plugin($plugin, $site_guid = 0) {
+ elgg_deprecated_notice('disable_plugin() was deprecated by ElggPlugin->deactivate()', 1.8);
+
+ $plugin = sanitise_string($plugin);
+
+ $site_guid = (int) $site_guid;
+ if (!$site_guid) {
+ $site = get_config('site');
+ $site_guid = $site->guid;
+ }
+
+ try {
+ $plugin = new ElggPlugin($plugin);
+ } catch(Exception $e) {
+ return false;
+ }
+
+ return $plugin->deactivate($site_guid);
+}
+
+/**
+ * Return whether a plugin is enabled or not.
+ *
+ * @deprecated 1.8 Use elgg_is_active_plugin()
+ *
+ * @param string $plugin The plugin name.
+ * @param int $site_guid The site id, if not specified then this is detected.
+ *
+ * @return bool
+ */
+function is_plugin_enabled($plugin, $site_guid = 0) {
+ elgg_deprecated_notice('is_plugin_enabled() was deprecated by elgg_is_active_plugin()', 1.8);
+ return elgg_is_active_plugin($plugin, $site_guid);
+}
+
+/**
+ * Get entities based on their private data.
+ *
+ * @param string $name The name of the setting
+ * @param string $value The value of the setting
+ * @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 Return a count of entities
+ * @param int $site_guid The site to get entities for. 0 for current, -1 for any
+ * @param mixed $container_guid The container(s) GUIDs
+ *
+ * @return array A list of entities.
+ * @deprecated 1.8 Use elgg_get_entities_from_private_settings()
+ */
+function get_entities_from_private_setting($name = "", $value = "", $type = "", $subtype = "",
+$owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0,
+$container_guid = null) {
+ elgg_deprecated_notice('get_entities_from_private_setting() was deprecated by elgg_get_entities_from_private_setting()!', 1.8);
+
+ $options = array();
+
+ $options['private_setting_name'] = $name;
+ $options['private_setting_value'] = $value;
+
+ // set container_guid to owner_guid to emulate old functionality
+ if ($owner_guid != "") {
+ if (is_null($container_guid)) {
+ $container_guid = $owner_guid;
+ }
+ }
+
+ if ($type) {
+ $options['types'] = $type;
+ }
+
+ if ($subtype) {
+ $options['subtypes'] = $subtype;
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ if ($container_guid) {
+ if (is_array($container_guid)) {
+ $options['container_guids'] = $container_guid;
+ } else {
+ $options['container_guid'] = $container_guid;
+ }
+ }
+
+ $options['limit'] = $limit;
+
+ if ($offset) {
+ $options['offset'] = $offset;
+ }
+
+ if ($order_by) {
+ $options['order_by'];
+ }
+
+ if ($site_guid) {
+ $options['site_guid'];
+ }
+
+ if ($count) {
+ $options['count'] = $count;
+ }
+
+ return elgg_get_entities_from_private_settings($options);
+}
+
+/**
+ * Get entities based on their private data by multiple keys.
+ *
+ * @param string $name The name of the setting
+ * @param mixed $type Entity type
+ * @param string $subtype Entity subtype
+ * @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 bool $count Count entities
+ * @param int $site_guid Site GUID. 0 for current, -1 for any.
+ * @param mixed $container_guid Container GUID
+ *
+ * @return array A list of entities.
+ * @deprecated 1.8 Use elgg_get_entities_from_private_settings()
+ */
+function get_entities_from_private_setting_multi(array $name, $type = "", $subtype = "",
+$owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false,
+$site_guid = 0, $container_guid = null) {
+
+ elgg_deprecated_notice('get_entities_from_private_setting_multi() was deprecated by elgg_get_entities_from_private_settings()!', 1.8);
+
+ $options = array();
+
+ $pairs = array();
+ foreach ($name as $setting_name => $setting_value) {
+ $pairs[] = array('name' => $setting_name, 'value' => $setting_value);
+ }
+ $options['private_setting_name_value_pairs'] = $pairs;
+
+ // set container_guid to owner_guid to emulate old functionality
+ if ($owner_guid != "") {
+ if (is_null($container_guid)) {
+ $container_guid = $owner_guid;
+ }
+ }
+
+ if ($type) {
+ $options['types'] = $type;
+ }
+
+ if ($subtype) {
+ $options['subtypes'] = $subtype;
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ if ($container_guid) {
+ if (is_array($container_guid)) {
+ $options['container_guids'] = $container_guid;
+ } else {
+ $options['container_guid'] = $container_guid;
+ }
+ }
+
+ $options['limit'] = $limit;
+
+ if ($offset) {
+ $options['offset'] = $offset;
+ }
+
+ if ($order_by) {
+ $options['order_by'];
+ }
+
+ if ($site_guid) {
+ $options['site_guid'];
+ }
+
+ if ($count) {
+ $options['count'] = $count;
+ }
+
+ return elgg_get_entities_from_private_settings($options);
+}
+
+/**
+ * Returns a viewable list of entities by relationship
+ *
+ * @see elgg_view_entity_list
+ *
+ * @deprecated 1.8 Use elgg_list_entities_from_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 instead say "give me all entities for whome $relationship_guid is a $relationship of"
+ * @param string $type The type of entity (eg 'object')
+ * @param string $subtype The entity subtype
+ * @param int $owner_guid The owner (default: all)
+ * @param int $limit The number of entities to display on a page
+ * @param true|false $fullview Whether or not to display the full view (default: true)
+ * @param true|false $viewtypetoggle Whether or not to allow gallery view
+ * @param true|false $pagination Whether to display pagination (default: true)
+ * @param bool $order_by SQL order by clause
+ * @return string The viewable list of entities
+ */
+function list_entities_from_relationship($relationship, $relationship_guid,
+$inverse_relationship = false, $type = ELGG_ENTITIES_ANY_VALUE,
+$subtype = ELGG_ENTITIES_ANY_VALUE, $owner_guid = 0, $limit = 10,
+$fullview = true, $listtypetoggle = false, $pagination = true, $order_by = '') {
+
+ elgg_deprecated_notice("list_entities_from_relationship was deprecated by elgg_list_entities_from_relationship()!", 1.8);
+ return elgg_list_entities_from_relationship(array(
+ 'relationship' => $relationship,
+ 'relationship_guid' => $relationship_guid,
+ 'inverse_relationship' => $inverse_relationship,
+ 'type' => $type,
+ 'subtype' => $subtype,
+ 'owner_guid' => $owner_guid,
+ 'order_by' => $order_by,
+ 'limit' => $limit,
+ 'full_view' => $fullview,
+ 'list_type_toggle' => $listtypetoggle,
+ 'pagination' => $pagination,
+ ));
+}
+
+/**
+ * Gets the number of entities by a the number of entities related to them in a particular way.
+ * This is a good way to get out the users with the most friends, or the groups with the
+ * most members.
+ *
+ * @deprecated 1.8 Use elgg_get_entities_from_relationship_count()
+ *
+ * @param string $relationship The relationship eg "friends_of"
+ * @param bool $inverse_relationship Inverse relationship owners
+ * @param string $type The type of entity (default: all)
+ * @param string $subtype The entity subtype (default: all)
+ * @param int $owner_guid The owner of the entities (default: none)
+ * @param int $limit Limit
+ * @param int $offset Offset
+ * @param bool $count Return a count instead of entities
+ * @param int $site_guid Site GUID
+ *
+ * @return array|int|false An array of entities, or the number of entities, or false on failure
+ */
+function get_entities_by_relationship_count($relationship, $inverse_relationship = true, $type = "",
+$subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $count = false, $site_guid = 0) {
+ elgg_deprecated_notice('get_entities_by_relationship_count() is deprecated by elgg_get_entities_from_relationship_count()', 1.8);
+
+ $options = array();
+
+ $options['relationship'] = $relationship;
+
+ // this used to default to true, which is wrong.
+ // flip it for the new function
+ $options['inverse_relationship'] = !$inverse_relationship;
+
+ if ($type) {
+ $options['types'] = $type;
+ }
+
+ if ($subtype) {
+ $options['subtypes'] = $subtype;
+ }
+
+ if ($owner_guid) {
+ $options['owner_guid'] = $owner_guid;
+ }
+
+ $options['limit'] = $limit;
+
+ if ($offset) {
+ $options['offset'] = $offset;
+ }
+
+ if ($site_guid) {
+ $options['site_guid'];
+ }
+
+ if ($count) {
+ $options['count'] = $count;
+ }
+
+ return elgg_get_entities_from_relationship_count($options);
+}
+
+/**
+ * Displays a human-readable list of entities
+ *
+ * @deprecated 1.8 Use elgg_list_entities_from_relationship_count()
+ *
+ * @param string $relationship The relationship eg "friends_of"
+ * @param bool $inverse_relationship Inverse relationship owners
+ * @param string $type The type of entity (eg 'object')
+ * @param string $subtype The entity subtype
+ * @param int $owner_guid The owner (default: all)
+ * @param int $limit The number of entities to display on a page
+ * @param bool $fullview Whether or not to display the full view (default: true)
+ * @param bool $listtypetoggle Whether or not to allow gallery view
+ * @param bool $pagination Whether to display pagination (default: true)
+ *
+ * @return string The viewable list of entities
+ */
+function list_entities_by_relationship_count($relationship, $inverse_relationship = true,
+$type = "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true,
+$listtypetoggle = false, $pagination = true) {
+
+ elgg_deprecated_notice('list_entities_by_relationship_count() was deprecated by elgg_list_entities_from_relationship_count()', 1.8);
+
+ $options = array();
+
+ $options['relationship'] = $relationship;
+
+ // this used to default to true, which is wrong.
+ // flip it for the new function
+ $options['inverse_relationship'] = !$inverse_relationship;
+
+ if ($type) {
+ $options['types'] = $type;
+ }
+
+ if ($subtype) {
+ $options['subtypes'] = $subtype;
+ }
+
+ if ($owner_guid) {
+ $options['owner_guid'] = $owner_guid;
+ }
+
+ $options['limit'] = $limit;
+
+ $options['full_view'] = $fullview;
+
+ return elgg_list_entities_from_relationship_count($options);
+}
+
+/**
+ * Gets the number of entities by a the number of entities related to
+ * them in a particular way also constrained by metadata.
+ *
+ * @deprecated 1.8 Use elgg_get_entities_from_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 Inverse relationship owner
+ * @param String $meta_name The metadata name
+ * @param String $meta_value The metadata value
+ * @param string $type The type of entity (default: all)
+ * @param string $subtype The entity subtype (default: all)
+ * @param int $owner_guid The owner of the entities (default: none)
+ * @param int $limit Limit
+ * @param int $offset Offset
+ * @param bool $count Return a count instead of entities
+ * @param int $site_guid Site GUID
+ *
+ * @return array|int|false An array of entities, or the number of entities, or false on failure
+ */
+function get_entities_from_relationships_and_meta($relationship, $relationship_guid,
+$inverse_relationship = false, $meta_name = "", $meta_value = "", $type = "",
+$subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $count = false, $site_guid = 0) {
+
+ elgg_deprecated_notice('get_entities_from_relationship_and_meta() was deprecated by elgg_get_entities_from_relationship()!', 1.7);
+
+ $options = array();
+
+ $options['relationship'] = $relationship;
+ $options['relationship_guid'] = $relationship_guid;
+ $options['inverse_relationship'] = $inverse_relationship;
+
+ if ($meta_value) {
+ $options['values'] = $meta_value;
+ }
+
+ if ($entity_type) {
+ $options['types'] = $entity_type;
+ }
+
+ if ($type) {
+ $options['types'] = $type;
+ }
+
+ if ($subtype) {
+ $options['subtypes'] = $subtype;
+ }
+
+ if ($owner_guid) {
+ $options['owner_guid'] = $owner_guid;
+ }
+
+ if ($limit) {
+ $options['limit'] = $limit;
+ }
+
+ if ($offset) {
+ $options['offset'] = $offset;
+ }
+
+ if ($order_by) {
+ $options['order_by'];
+ }
+
+ if ($site_guid) {
+ $options['site_guid'];
+ }
+
+ if ($count) {
+ $options['count'] = $count;
+ }
+
+ return elgg_get_entities_from_relationship($options);
+}
+
+
+/**
+ * Retrieves items from the river. All parameters are optional.
+ *
+ * @param int|array $subject_guid Acting entity to restrict to. Default: all
+ * @param int|array $object_guid Entity being acted on to restrict to. Default: all
+ * @param string $subject_relationship If set to a relationship type, this will use
+ * $subject_guid as the starting point and set the
+ * subjects to be all users this
+ * entity has this relationship with (eg 'friend').
+ * Default: blank
+ * @param string $type The type of entity to restrict to. Default: all
+ * @param string $subtype The subtype of entity to restrict to. Default: all
+ * @param string $action_type The type of river action to restrict to. Default: all
+ * @param int $limit The number of items to retrieve. Default: 20
+ * @param int $offset The page offset. Default: 0
+ * @param int $posted_min The minimum time period to look at. Default: none
+ * @param int $posted_max The maximum time period to look at. Default: none
+ *
+ * @return array|false Depending on success
+ * @deprecated 1.8 Use elgg_get_river()
+ */
+function get_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '',
+$type = '', $subtype = '', $action_type = '', $limit = 20, $offset = 0, $posted_min = 0,
+$posted_max = 0) {
+ elgg_deprecated_notice("get_river_items deprecated by elgg_get_river", 1.8);
+
+ $options = array();
+
+ if ($subject_guid) {
+ $options['subject_guid'] = $subject_guid;
+ }
+
+ if ($object_guid) {
+ $options['object_guid'] = $object_guid;
+ }
+
+ if ($subject_relationship) {
+ $options['relationship'] = $subject_relationship;
+ unset($options['subject_guid']);
+ $options['relationship_guid'] = $subject_guid;
+ }
+
+ if ($type) {
+ $options['type'] = $type;
+ }
+
+ if ($subtype) {
+ $options['subtype'] = $subtype;
+ }
+
+ if ($action_type) {
+ $options['action_type'] = $action_type;
+ }
+
+ $options['limit'] = $limit;
+ $options['offset'] = $offset;
+
+ if ($posted_min) {
+ $options['posted_time_lower'] = $posted_min;
+ }
+
+ if ($posted_max) {
+ $options['posted_time_upper'] = $posted_max;
+ }
+
+ return elgg_get_river($options);
+}
+
+/**
+ * Returns a human-readable version of the river.
+ *
+ * @param int|array $subject_guid Acting entity to restrict to. Default: all
+ * @param int|array $object_guid Entity being acted on to restrict to. Default: all
+ * @param string $subject_relationship If set to a relationship type, this will use
+ * $subject_guid as the starting point and set
+ * the subjects to be all users this entity has this
+ * relationship with (eg 'friend'). Default: blank
+ * @param string $type The type of entity to restrict to. Default: all
+ * @param string $subtype The subtype of entity to restrict to. Default: all
+ * @param string $action_type The type of river action to restrict to. Default: all
+ * @param int $limit The number of items to retrieve. Default: 20
+ * @param int $posted_min The minimum time period to look at. Default: none
+ * @param int $posted_max The maximum time period to look at. Default: none
+ * @param bool $pagination Show pagination?
+ *
+ * @return string Human-readable river.
+ * @deprecated 1.8 Use elgg_list_river()
+ */
+function elgg_view_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '',
+$type = '', $subtype = '', $action_type = '', $limit = 20, $posted_min = 0,
+$posted_max = 0, $pagination = true) {
+ elgg_deprecated_notice("elgg_view_river_items deprecated for elgg_list_river", 1.8);
+
+ $river_items = get_river_items($subject_guid, $object_guid, $subject_relationship,
+ $type, $subtype, $action_type, $limit + 1, $posted_min, $posted_max);
+
+ // Get input from outside world and sanitise it
+ $offset = (int) get_input('offset', 0);
+
+ // view them
+ $params = array(
+ 'items' => $river_items,
+ 'count' => count($river_items),
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'pagination' => $pagination,
+ 'list-class' => 'elgg-list-river',
+ );
+
+ return elgg_view('page/components/list', $params);
+}
+
+/**
+ * Construct and execute the query required for the activity stream.
+ *
+ * @deprecated 1.8 This is outdated and uses the systemlog table instead of the river table.
+ * Don't use it.
+ */
+function get_activity_stream_data($limit = 10, $offset = 0, $type = "", $subtype = "",
+$owner_guid = "", $owner_relationship = "") {
+ elgg_deprecated_notice("get_activity_stream_data was deprecated", 1.8);
+
+ global $CONFIG;
+
+ $limit = (int)$limit;
+ $offset = (int)$offset;
+
+ if ($type) {
+ if (!is_array($type)) {
+ $type = array(sanitise_string($type));
+ } else {
+ foreach ($type as $k => $v) {
+ $type[$k] = sanitise_string($v);
+ }
+ }
+ }
+
+ if ($subtype) {
+ if (!is_array($subtype)) {
+ $subtype = array(sanitise_string($subtype));
+ } else {
+ foreach ($subtype as $k => $v) {
+ $subtype[$k] = sanitise_string($v);
+ }
+ }
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ foreach ($owner_guid as $k => $v) {
+ $owner_guid[$k] = (int)$v;
+ }
+ } else {
+ $owner_guid = array((int)$owner_guid);
+ }
+ }
+
+ $owner_relationship = sanitise_string($owner_relationship);
+
+ // Get a list of possible views
+ $activity_events = array();
+ $activity_views = array_merge(elgg_view_tree('activity', 'default'),
+ elgg_view_tree('river', 'default'));
+
+ $done = array();
+
+ foreach ($activity_views as $view) {
+ $fragments = explode('/', $view);
+ $tmp = explode('/', $view, 2);
+ $tmp = $tmp[1];
+
+ if ((isset($fragments[0])) && (($fragments[0] == 'river') || ($fragments[0] == 'activity'))
+ && (!in_array($tmp, $done))) {
+
+ if (isset($fragments[1])) {
+ $f = array();
+ for ($n = 1; $n < count($fragments); $n++) {
+ $val = sanitise_string($fragments[$n]);
+ switch($n) {
+ case 1: $key = 'type'; break;
+ case 2: $key = 'subtype'; break;
+ case 3: $key = 'event'; break;
+ }
+ $f[$key] = $val;
+ }
+
+ // Filter result based on parameters
+ $add = true;
+ if ($type) {
+ if (!in_array($f['type'], $type)) {
+ $add = false;
+ }
+ }
+ if (($add) && ($subtype)) {
+ if (!in_array($f['subtype'], $subtype)) {
+ $add = false;
+ }
+ }
+ if (($add) && ($event)) {
+ if (!in_array($f['event'], $event)) {
+ $add = false;
+ }
+ }
+
+ if ($add) {
+ $activity_events[] = $f;
+ }
+ }
+
+ $done[] = $tmp;
+ }
+ }
+
+ $n = 0;
+ foreach ($activity_events as $details) {
+ // Get what we're talking about
+ if ($details['subtype'] == 'default') {
+ $details['subtype'] = '';
+ }
+
+ if (($details['type']) && ($details['event'])) {
+ if ($n > 0) {
+ $obj_query .= " or ";
+ }
+
+ $access = "";
+ if ($details['type'] != 'relationship') {
+ $access = " and " . get_access_sql_suffix('sl');
+ }
+
+ $obj_query .= "( sl.object_type='{$details['type']}'
+ AND sl.object_subtype='{$details['subtype']}'
+ AND sl.event='{$details['event']}' $access )";
+
+ $n++;
+ }
+ }
+
+ // User
+ if ((count($owner_guid)) && ($owner_guid[0] != 0)) {
+ $user = " and sl.performed_by_guid in (" . implode(',', $owner_guid) . ")";
+
+ if ($owner_relationship) {
+ $friendsarray = "";
+ if ($friends = elgg_get_entities_from_relationship(array(
+ 'relationship' => $owner_relationship,
+ 'relationship_guid' => $owner_guid[0],
+ 'inverse_relationship' => FALSE,
+ 'type' => 'user',
+ 'subtype' => $subtype,
+ 'limit' => false))
+ ) {
+
+ $friendsarray = array();
+ foreach ($friends as $friend) {
+ $friendsarray[] = $friend->getGUID();
+ }
+
+ $user = " and sl.performed_by_guid in (" . implode(',', $friendsarray) . ")";
+ }
+ }
+ }
+
+ $query = "SELECT sl.* FROM {$CONFIG->dbprefix}system_log sl
+ WHERE 1 $user AND ($obj_query)
+ ORDER BY sl.time_created desc limit $offset, $limit";
+ return get_data($query);
+}
+
+/**
+ * Perform standard authentication with a given username and password.
+ * Returns an ElggUser object for use with login.
+ *
+ * @see login
+ *
+ * @param string $username The username, optionally (for standard logins)
+ * @param string $password The password, optionally (for standard logins)
+ *
+ * @return ElggUser|false The authenticated user object, or false on failure.
+ *
+ * @deprecated 1.8 Use elgg_authenticate
+ */
+function authenticate($username, $password) {
+ elgg_deprecated_notice('authenticate() has been deprecated for elgg_authenticate()', 1.8);
+ $pam = new ElggPAM('user');
+ $credentials = array('username' => $username, 'password' => $password);
+ $result = $pam->authenticate($credentials);
+ if ($result) {
+ return get_user_by_username($username);
+ }
+ return false;
+}
+
+
+/**
+ * Get the members of a site.
+ *
+ * @param int $site_guid Site GUID
+ * @param int $limit User GUID
+ * @param int $offset Offset
+ *
+ * @return mixed
+ * @deprecated 1.8 Use ElggSite::getMembers()
+ */
+function get_site_members($site_guid, $limit = 10, $offset = 0) {
+ elgg_deprecated_notice("get_site_members() deprecated.
+ Use ElggSite::getMembers()", 1.8);
+
+ $site = get_entity($site_guid);
+ if ($site) {
+ return $site->getMembers($limit, $offset);
+ }
+
+ return false;
+}
+
+/**
+ * Display a list of site members
+ *
+ * @param int $site_guid The GUID of the site
+ * @param int $limit The number of members to display on a page
+ * @param bool $fullview Whether or not to display the full view (default: true)
+ *
+ * @return string A displayable list of members
+ * @deprecated 1.8 Use ElggSite::listMembers()
+ */
+function list_site_members($site_guid, $limit = 10, $fullview = true) {
+ elgg_deprecated_notice("list_site_members() deprecated.
+ Use ElggSite::listMembers()", 1.8);
+
+ $options = array(
+ 'limit' => $limit,
+ 'full_view' => $full_view,
+ );
+
+ $site = get_entity($site_guid);
+ if ($site) {
+ return $site->listMembers($options);
+ }
+
+ return '';
+}
+
+
+/**
+ * Add a collection to a site.
+ *
+ * @param int $site_guid Site GUID
+ * @param int $collection_guid Collection GUID
+ *
+ * @return mixed
+ * @deprecated 1.8 Don't use this.
+ */
+function add_site_collection($site_guid, $collection_guid) {
+ elgg_deprecated_notice("add_site_collection has been deprecated", 1.8);
+ global $CONFIG;
+
+ $site_guid = (int)$site_guid;
+ $collection_guid = (int)$collection_guid;
+
+ return add_entity_relationship($collection_guid, "member_of_site", $site_guid);
+}
+
+/**
+ * Remove a collection from a site.
+ *
+ * @param int $site_guid Site GUID
+ * @param int $collection_guid Collection GUID
+ *
+ * @return mixed
+ * @deprecated 1.8 Don't use this.
+ */
+function remove_site_collection($site_guid, $collection_guid) {
+ elgg_deprecated_notice("remove_site_collection has been deprecated", 1.8);
+ $site_guid = (int)$site_guid;
+ $collection_guid = (int)$collection_guid;
+
+ return remove_entity_relationship($collection_guid, "member_of_site", $site_guid);
+}
+
+/**
+ * Get the collections belonging to a site.
+ *
+ * @param int $site_guid Site GUID
+ * @param string $subtype Subtype
+ * @param int $limit Limit
+ * @param int $offset Offset
+ *
+ * @return mixed
+ * @deprecated 1.8 Don't use this.
+ */
+function get_site_collections($site_guid, $subtype = "", $limit = 10, $offset = 0) {
+ elgg_deprecated_notice("get_site_collections has been deprecated", 1.8);
+ $site_guid = (int)$site_guid;
+ $subtype = sanitise_string($subtype);
+ $limit = (int)$limit;
+ $offset = (int)$offset;
+
+ // collection isn't a valid type. This won't work.
+ return elgg_get_entities_from_relationship(array(
+ 'relationship' => 'member_of_site',
+ 'relationship_guid' => $site_guid,
+ 'inverse_relationship' => TRUE,
+ 'type' => 'collection',
+ 'subtype' => $subtype,
+ 'limit' => $limit,
+ 'offset' => $offset
+ ));
+}
+
+/**
+ * Get an array of tags with weights for use with the output/tagcloud view.
+ *
+ * @deprecated 1.8 Use elgg_get_tags().
+ *
+ * @param int $threshold Get the threshold of minimum number of each tags to
+ * bother with (ie only show tags where there are more
+ * than $threshold occurances)
+ * @param int $limit Number of tags to return
+ * @param string $metadata_name Optionally, the name of the field you want to grab for
+ * @param string $entity_type Optionally, the entity type ('object' etc)
+ * @param string $entity_subtype The entity subtype, optionally
+ * @param int $owner_guid The GUID of the tags owner, optionally
+ * @param int $site_guid Optionally, the site to restrict to (default is the current site)
+ * @param int $start_ts Optionally specify a start timestamp for tags used to
+ * generate cloud.
+ * @param int $end_ts Optionally specify an end timestamp for tags used to generate cloud
+ *
+ * @return array|false Array of objects with ->tag and ->total values, or false on failure
+ */
+function get_tags($threshold = 1, $limit = 10, $metadata_name = "", $entity_type = "object",
+$entity_subtype = "", $owner_guid = "", $site_guid = -1, $start_ts = "", $end_ts = "") {
+
+ elgg_deprecated_notice('get_tags() has been replaced by elgg_get_tags()', 1.8);
+
+ if (is_array($metadata_name)) {
+ return false;
+ }
+
+ $options = array();
+ if ($metadata_name === '') {
+ $options['tag_names'] = array();
+ } else {
+ $options['tag_names'] = array($metadata_name);
+ }
+
+ $options['threshold'] = $threshold;
+ $options['limit'] = $limit;
+
+ // rewrite owner_guid to container_guid to emulate old functionality
+ $container_guid = $owner_guid;
+ if ($container_guid) {
+ $options['container_guids'] = $container_guid;
+ }
+
+ if ($entity_type) {
+ $options['type'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtype'] = $entity_subtype;
+ }
+
+ if ($site_guid != -1) {
+ $options['site_guids'] = $site_guid;
+ }
+
+ if ($end_ts) {
+ $options['created_time_upper'] = $end_ts;
+ }
+
+ if ($start_ts) {
+ $options['created_time_lower'] = $start_ts;
+ }
+
+ $r = elgg_get_tags($options);
+ return $r;
+}
+
+/**
+ * Loads and displays a tagcloud given particular criteria.
+ *
+ * @deprecated 1.8 use elgg_view_tagcloud()
+ *
+ * @param int $threshold Get the threshold of minimum number of each tags
+ * to bother with (ie only show tags where there are
+ * more than $threshold occurances)
+ * @param int $limit Number of tags to return
+ * @param string $metadata_name Optionally, the name of the field you want to grab for
+ * @param string $entity_type Optionally, the entity type ('object' etc)
+ * @param string $entity_subtype The entity subtype, optionally
+ * @param int $owner_guid The GUID of the tags owner, optionally
+ * @param int $site_guid Optionally, the site to restrict to (default is the current site)
+ * @param int $start_ts Optionally specify a start timestamp for tags used to
+ * generate cloud.
+ * @param int $end_ts Optionally specify an end timestamp for tags used to generate
+ * cloud.
+ *
+ * @return string The HTML (or other, depending on view type) of the tagcloud.
+ */
+function display_tagcloud($threshold = 1, $limit = 10, $metadata_name = "", $entity_type = "object",
+$entity_subtype = "", $owner_guid = "", $site_guid = -1, $start_ts = "", $end_ts = "") {
+
+ elgg_deprecated_notice('display_tagcloud() was deprecated by elgg_view_tagcloud()!', 1.8);
+
+ $tags = get_tags($threshold, $limit, $metadata_name, $entity_type,
+ $entity_subtype, $owner_guid, $site_guid, $start_ts, $end_ts);
+
+ return elgg_view('output/tagcloud', array(
+ 'value' => $tags,
+ 'type' => $entity_type,
+ 'subtype' => $entity_subtype,
+ ));
+}
+
+
+/**
+ * Obtains a list of objects owned by a user
+ *
+ * @param int $user_guid The GUID of the owning user
+ * @param string $subtype Optionally, the subtype of objects
+ * @param int $limit The number of results to return (default 10)
+ * @param int $offset Indexing offset, if any
+ * @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 false|array An array of ElggObjects or false, depending on success
+ * @deprecated 1.8 Use elgg_get_entities() instead
+ */
+function get_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10,
+$offset = 0, $timelower = 0, $timeupper = 0) {
+ elgg_deprecated_notice("get_user_objects() was deprecated in favor of elgg_get_entities()", 1.8);
+ $ntt = elgg_get_entities(array(
+ 'type' => 'object',
+ 'subtype' => $subtype,
+ 'owner_guid' => $user_guid,
+ 'limit' => $limit,
+ 'offset' => $offset,
+ 'container_guid' => $user_guid,
+ 'created_time_lower' => $timelower,
+ 'created_time_upper' => $timeupper
+ ));
+ return $ntt;
+}
+
+/**
+ * Counts the objects (optionally of a particular subtype) owned by a user
+ *
+ * @param int $user_guid The GUID of the owning user
+ * @param string $subtype Optionally, the subtype of objects
+ * @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 int The number of objects the user owns (of this subtype)
+ * @deprecated 1.8 Use elgg_get_entities() instead
+ */
+function count_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $timelower = 0,
+$timeupper = 0) {
+ elgg_deprecated_notice("count_user_objects() was deprecated in favor of elgg_get_entities()", 1.8);
+ $total = elgg_get_entities(array(
+ 'type' => 'object',
+ 'subtype' => $subtype,
+ 'owner_guid' => $user_guid,
+ 'count' => TRUE,
+ 'container_guid' => $user_guid,
+ 'created_time_lower' => $timelower,
+ 'created_time_upper' => $timeupper
+ ));
+ return $total;
+}
+
+/**
+ * Displays a list of user objects of a particular subtype, with navigation.
+ *
+ * @see elgg_view_entity_list
+ *
+ * @param int $user_guid The GUID of the user
+ * @param string $subtype The object subtype
+ * @param int $limit The number of entities to display on a page
+ * @param bool $fullview Whether or not to display the full view (default: true)
+ * @param bool $listtypetoggle Whether or not to allow gallery view (default: true)
+ * @param bool $pagination Whether to display pagination (default: true)
+ * @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
+ * @deprecated 1.8 Use elgg_list_entities() instead
+ */
+function list_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10,
+$fullview = true, $listtypetoggle = true, $pagination = true, $timelower = 0, $timeupper = 0) {
+ elgg_deprecated_notice("list_user_objects() was deprecated in favor of elgg_list_entities()", 1.8);
+
+ $offset = (int) get_input('offset');
+ $limit = (int) $limit;
+ $count = (int) count_user_objects($user_guid, $subtype, $timelower, $timeupper);
+ $entities = get_user_objects($user_guid, $subtype, $limit, $offset, $timelower, $timeupper);
+
+ return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $listtypetoggle,
+ $pagination);
+}
+
+
+/**
+ * Get user objects by an array of metadata
+ *
+ * @param int $user_guid The GUID of the owning user
+ * @param string $subtype Optionally, the subtype of objects
+ * @param array $metadata An array of metadata
+ * @param int $limit The number of results to return (default 10)
+ * @param int $offset Indexing offset, if any
+ *
+ * @return false|array An array of ElggObjects or false, depending on success
+ * @deprecated 1.8 Use elgg_get_entities_from_metadata() instead
+ */
+function get_user_objects_by_metadata($user_guid, $subtype = "", $metadata = array(),
+$limit = 0, $offset = 0) {
+ elgg_deprecated_notice("get_user_objects_by_metadata() was deprecated in favor of elgg_get_entities_from_metadata()", 1.8);
+ return get_entities_from_metadata_multi($metadata, "object", $subtype, $user_guid,
+ $limit, $offset);
+}
+
+/**
+ * Set the validation status for a user.
+ *
+ * @param bool $status Validated (true) or false
+ * @param string $method Optional method to say how a user was validated
+ * @return bool
+ * @deprecated 1.8 Use elgg_set_user_validation_status()
+ */
+function set_user_validation_status($user_guid, $status, $method = '') {
+ elgg_deprecated_notice("set_user_validation_status() is deprecated", 1.8);
+ return elgg_set_user_validation_status($user_guid, $status, $method);
+}
+
+/**
+ * Trigger an event requesting that a user guid be validated somehow - either by email address or some other way.
+ *
+ * This function invalidates any existing validation value.
+ *
+ * @param int $user_guid User's GUID
+ * @deprecated 1.8 Hook into the register, user plugin hook and request validation.
+ */
+function request_user_validation($user_guid) {
+ elgg_deprecated_notice("request_user_validation() is deprecated.
+ Plugins should register for the 'register, user' plugin hook", 1.8);
+ $user = get_entity($user_guid);
+
+ if (($user) && ($user instanceof ElggUser)) {
+ // invalidate any existing validations
+ set_user_validation_status($user_guid, false);
+
+ // request validation
+ trigger_elgg_event('validate', 'user', $user);
+ }
+}
+
+/**
+ * Register a user settings page with the admin panel.
+ * This function extends the view "usersettings/main" with the provided view.
+ * This view should provide a description and either a control or a link to.
+ *
+ * Usage:
+ * - To add a control to the main admin panel then extend usersettings/main
+ * - To add a control to a new page create a page which renders a view
+ * usersettings/subpage (where subpage is your new page -
+ * nb. some pages already exist that you can extend), extend the main view
+ * to point to it, and add controls to your new view.
+ *
+ * At the moment this is essentially a wrapper around elgg_extend_view().
+ *
+ * @param string $new_settings_view The view associated with the control you're adding
+ * @param string $view The view to extend, by default this is 'usersettings/main'.
+ * @param int $priority Optional priority to govern the appearance in the list.
+ *
+ * @return bool
+ * @deprecated 1.8 Extend one of the views in core/settings
+ */
+function extend_elgg_settings_page($new_settings_view, $view = 'usersettings/main',
+$priority = 500) {
+ // see views: /core/settings
+ elgg_deprecated_notice("extend_elgg_settings_page has been deprecated. Extend one of the settings views instead", 1.8);
+
+ return elgg_extend_view($view, $new_settings_view, $priority);
+}
+
+/**
+ * Returns a representation of a full 'page' (which might be an HTML page,
+ * RSS file, etc, depending on the current viewtype)
+ *
+ * @param string $title
+ * @param string $body
+ * @return string
+ *
+ * @deprecated 1.8 Use elgg_view_page()
+ */
+function page_draw($title, $body, $sidebar = "") {
+ elgg_deprecated_notice("page_draw() was deprecated in favor of elgg_view_page() in 1.8.", 1.8);
+
+ $vars = array(
+ 'sidebar' => $sidebar
+ );
+ echo elgg_view_page($title, $body, 'default', $vars);
+}
+
+/**
+ * Wrapper function to display search listings.
+ *
+ * @param string $icon The icon for the listing
+ * @param string $info Any information that needs to be displayed.
+ *
+ * @return string The HTML (etc) representing the listing
+ * @deprecated 1.8 use elgg_view_image_block()
+ */
+function elgg_view_listing($icon, $info) {
+ elgg_deprecated_notice('elgg_view_listing deprecated by elgg_view_image_block', 1.8);
+ return elgg_view('page/components/image_block', array('image' => $icon, 'body' => $info));
+}
+
+/**
+ * Return the icon URL for an entity.
+ *
+ * @tip Can be overridden by registering a plugin hook for entity:icon:url, $entity_type.
+ *
+ * @internal This is passed an entity rather than a guid to handle non-created entities.
+ *
+ * @param ElggEntity $entity The entity
+ * @param string $size Icon size
+ *
+ * @return string URL to the entity icon.
+ * @deprecated 1.8 Use $entity->getIconURL()
+ */
+function get_entity_icon_url(ElggEntity $entity, $size = 'medium') {
+ elgg_deprecated_notice("get_entity_icon_url() deprecated for getIconURL()", 1.8);
+ global $CONFIG;
+
+ $size = sanitise_string($size);
+ switch (strtolower($size)) {
+ case 'master':
+ $size = 'master';
+ break;
+
+ case 'large' :
+ $size = 'large';
+ break;
+
+ case 'topbar' :
+ $size = 'topbar';
+ break;
+
+ case 'tiny' :
+ $size = 'tiny';
+ break;
+
+ case 'small' :
+ $size = 'small';
+ break;
+
+ case 'medium' :
+ default:
+ $size = 'medium';
+ }
+
+ $url = false;
+
+ $viewtype = elgg_get_viewtype();
+
+ // Step one, see if anyone knows how to render this in the current view
+ $params = array('entity' => $entity, 'viewtype' => $viewtype, 'size' => $size);
+ $url = elgg_trigger_plugin_hook('entity:icon:url', $entity->getType(), $params, $url);
+
+ // Fail, so use default
+ if (!$url) {
+ $type = $entity->getType();
+ $subtype = $entity->getSubtype();
+
+ if (!empty($subtype)) {
+ $overrideurl = elgg_view("icon/{$type}/{$subtype}/{$size}", array('entity' => $entity));
+ if (!empty($overrideurl)) {
+ return $overrideurl;
+ }
+ }
+
+ $overrideurl = elgg_view("icon/{$type}/default/{$size}", array('entity' => $entity));
+ if (!empty($overrideurl)) {
+ return $overrideurl;
+ }
+
+ $url = "_graphics/icons/default/$size.png";
+ }
+
+ return elgg_normalize_url($url);
+}
+
+/**
+ * Return the current logged in user, or NULL if no user is logged in.
+ *
+ * If no user can be found in the current session, a plugin
+ * hook - 'session:get' 'user' to give plugin authors another
+ * way to provide user details to the ACL system without touching the session.
+ *
+ * @deprecated 1.8 Use elgg_get_logged_in_user_entity()
+ * @return ElggUser|NULL
+ */
+function get_loggedin_user() {
+ elgg_deprecated_notice('get_loggedin_user() is deprecated by elgg_get_logged_in_user_entity()', 1.8);
+ return elgg_get_logged_in_user_entity();
+}
+
+/**
+ * Return the current logged in user by id.
+ *
+ * @deprecated 1.8 Use elgg_get_logged_in_user_guid()
+ * @see elgg_get_logged_in_user_entity()
+ * @return int
+ */
+function get_loggedin_userid() {
+ elgg_deprecated_notice('get_loggedin_userid() is deprecated by elgg_get_logged_in_user_guid()', 1.8);
+ return elgg_get_logged_in_user_guid();
+}
+
+
+/**
+ * Returns whether or not the user is currently logged in
+ *
+ * @deprecated 1.8 Use elgg_is_logged_in();
+ * @return bool
+ */
+function isloggedin() {
+ elgg_deprecated_notice('isloggedin() is deprecated by elgg_is_logged_in()', 1.8);
+ return elgg_is_logged_in();
+}
+
+/**
+ * Returns whether or not the user is currently logged in and that they are an admin user.
+ *
+ * @deprecated 1.8 Use elgg_is_admin_logged_in()
+ * @return bool
+ */
+function isadminloggedin() {
+ elgg_deprecated_notice('isadminloggedin() is deprecated by elgg_is_admin_logged_in()', 1.8);
+ return elgg_is_admin_logged_in();
+}
+
+
+/**
+ * Loads plugins
+ *
+ * @deprecated 1.8 Use elgg_load_plugins()
+ *
+ * @return bool
+ */
+function load_plugins() {
+ elgg_deprecated_notice('load_plugins() is deprecated by elgg_load_plugins()', 1.8);
+ return elgg_load_plugins();
+}
+
+/**
+ * Find the plugin settings for a user.
+ *
+ * @param string $plugin_id Plugin name.
+ * @param int $user_guid The guid who's settings to retrieve.
+ *
+ * @deprecated 1.8 Use elgg_get_all_plugin_user_settings() or ElggPlugin->getAllUserSettings()
+ * @return StdClass Object with all user settings.
+ */
+function find_plugin_usersettings($plugin_id = null, $user_guid = 0) {
+ elgg_deprecated_notice('find_plugin_usersettings() is deprecated by elgg_get_all_plugin_user_settings()', 1.8);
+ return elgg_get_all_plugin_user_settings($user_guid, $plugin_id, true);
+}
+
+/**
+ * Set a user specific setting for a plugin.
+ *
+ * @param string $name The name - note, can't be "title".
+ * @param mixed $value The value.
+ * @param int $user_guid Optional user.
+ * @param string $plugin_id Optional plugin name, if not specified then it
+ * is detected from where you are calling from.
+ *
+ * @return bool
+ * @deprecated 1.8 Use elgg_set_plugin_user_setting() or ElggPlugin->setUserSetting()
+ */
+function set_plugin_usersetting($name, $value, $user_guid = 0, $plugin_id = "") {
+ elgg_deprecated_notice('find_plugin_usersettings() is deprecated by elgg_get_all_plugin_user_settings()', 1.8);
+ return elgg_set_plugin_user_setting($name, $value, $user_guid, $plugin_id);
+}
+
+/**
+ * Clears a user-specific plugin setting
+ *
+ * @param str $name Name of the plugin setting
+ * @param int $user_guid Defaults to logged in user
+ * @param str $plugin_id Defaults to contextual plugin name
+ *
+ * @deprecated 1.8 Use elgg_unset_plugin_user_setting or ElggPlugin->unsetUserSetting().
+ * @return bool Success
+ */
+function clear_plugin_usersetting($name, $user_guid = 0, $plugin_id = '') {
+ elgg_deprecated_notice('clear_plugin_usersetting() is deprecated by elgg_unset_plugin_usersetting()', 1.8);
+ return elgg_unset_plugin_user_setting($name, $user_guid, $plugin_id);
+}
+
+/**
+ * Get a user specific setting for a plugin.
+ *
+ * @param string $name The name.
+ * @param int $user_guid Guid of owning user
+ * @param string $plugin_id Optional plugin name, if not specified
+ * it is detected from where you are calling.
+ *
+ * @deprecated 1.8 Use elgg_get_plugin_user_setting() or ElggPlugin->getUserSetting()
+ * @return mixed
+ */
+function get_plugin_usersetting($name, $user_guid = 0, $plugin_id = "") {
+ elgg_deprecated_notice('get_plugin_usersetting() is deprecated by elgg_get_plugin_user_setting()', 1.8);
+ return elgg_get_plugin_user_setting($name, $user_guid, $plugin_id);
+}
+
+/**
+ * Set a setting for a plugin.
+ *
+ * @param string $name The name - note, can't be "title".
+ * @param mixed $value The value.
+ * @param string $plugin_id Optional plugin name, if not specified
+ * then it is detected from where you are calling from.
+ *
+ * @deprecated 1.8 Use elgg_set_plugin_setting() or ElggPlugin->setSetting()
+ * @return int|false
+ */
+function set_plugin_setting($name, $value, $plugin_id = null) {
+ elgg_deprecated_notice('set_plugin_setting() is deprecated by elgg_set_plugin_setting()', 1.8);
+ return elgg_set_plugin_setting($name, $value, $plugin_id);
+}
+
+/**
+ * Get setting for a plugin.
+ *
+ * @param string $name The name.
+ * @param string $plugin_id Optional plugin name, if not specified
+ * then it is detected from where you are calling from.
+ *
+ * @deprecated 1.8 Use elgg_get_plugin_setting() or ElggPlugin->getSetting()
+ * @return mixed
+ */
+function get_plugin_setting($name, $plugin_id = "") {
+ elgg_deprecated_notice('get_plugin_setting() is deprecated by elgg_get_plugin_setting()', 1.8);
+ return elgg_get_plugin_setting($name, $plugin_id);
+}
+
+/**
+ * Clear a plugin setting.
+ *
+ * @param string $name The name.
+ * @param string $plugin_id Optional plugin name, if not specified
+ * then it is detected from where you are calling from.
+ *
+ * @deprecated 1.8 Use elgg_unset_plugin_setting() or ElggPlugin->unsetSetting()
+ * @return bool
+ */
+function clear_plugin_setting($name, $plugin_id = "") {
+ elgg_deprecated_notice('clear_plugin_setting() is deprecated by elgg_unset_plugin_setting()', 1.8);
+ return elgg_unset_plugin_setting($name, $plugin_id);
+}
+
+/**
+ * Unsets all plugin settings for a plugin.
+ *
+ * @param string $plugin_id Optional plugin name, if not specified
+ * then it is detected from where you are calling from.
+ *
+ * @return bool
+ * @deprecated 1.8 Use elgg_unset_all_plugin_settings() or ElggPlugin->unsetAllSettings()
+ * @since 1.7.0
+ */
+function clear_all_plugin_settings($plugin_id = "") {
+ elgg_deprecated_notice('clear_all_plugin_settings() is deprecated by elgg_unset_all_plugin_setting()', 1.8);
+ return elgg_unset_all_plugin_settings($plugin_id);
+}
+
+
+/**
+ * Get a list of annotations for a given object/user/annotation type.
+ *
+ * @param int|array $entity_guid GUID to return annotations of (falsey for any)
+ * @param string $entity_type Type of entity
+ * @param string $entity_subtype Subtype of entity
+ * @param string $name Name of annotation
+ * @param mixed $value Value of annotation
+ * @param int|array $owner_guid Owner(s) of annotation
+ * @param int $limit Limit
+ * @param int $offset Offset
+ * @param string $order_by Order annotations by SQL
+ * @param int $timelower Lower time limit
+ * @param int $timeupper Upper time limit
+ * @param int $entity_owner_guid Owner guid for the entity
+ *
+ * @return array
+ * @deprecated 1.8 Use elgg_get_annotations()
+ */
+function get_annotations($entity_guid = 0, $entity_type = "", $entity_subtype = "", $name = "",
+$value = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "asc", $timelower = 0,
+$timeupper = 0, $entity_owner_guid = 0) {
+
+ elgg_deprecated_notice('get_annotations() is deprecated by elgg_get_annotations()', 1.8);
+ $options = array();
+
+ if ($entity_guid) {
+ $options['guid'] = $entity_guid;
+ }
+
+ if ($entity_type) {
+ $options['type'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtype'] = $entity_subtype;
+ }
+
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ if ($value) {
+ $options['annotation_value'] = $value;
+ }
+
+ if ($owner_guid) {
+ $options['annotation_owner_guid'] = $owner_guid;
+ }
+
+ $options['limit'] = $limit;
+ $options['offset'] = $offset;
+
+ if ($order_by == 'desc') {
+ $options['order_by'] = 'n_table.time_created desc';
+ }
+
+ if ($timelower) {
+ $options['annotation_time_lower'] = $timelower;
+ }
+
+ if ($timeupper) {
+ $options['annotation_time_upper'] = $timeupper;
+ }
+
+ if ($entity_owner_guid) {
+ $options['owner_guid'] = $entity_owner_guid;
+ }
+
+ return elgg_get_annotations($options);
+}
+
+
+/**
+ * Returns a human-readable list of annotations on a particular entity.
+ *
+ * @param int $entity_guid The entity GUID
+ * @param string $name The name of the kind of annotation
+ * @param int $limit The number of annotations to display at once
+ * @param true|false $asc Display annotations in ascending order. (Default: true)
+ *
+ * @return string HTML (etc) version of the annotation list
+ * @deprecated 1.8 Use elgg_list_annotations()
+ */
+function list_annotations($entity_guid, $name = "", $limit = 25, $asc = true) {
+ elgg_deprecated_notice('list_annotations() is deprecated by elgg_list_annotations()', 1.8);
+
+ if ($asc) {
+ $asc = "asc";
+ } else {
+ $asc = "desc";
+ }
+
+ $options = array(
+ 'guid' => $entity_guid,
+ 'limit' => $limit,
+ 'order_by' => "n_table.time_created $asc"
+ );
+
+ return elgg_list_annotations($options);
+}
+
+/**
+ * Helper function to deprecate annotation calculation functions. Don't use.
+ *
+ * @param unknown_type $entity_guid
+ * @param unknown_type $entity_type
+ * @param unknown_type $entity_subtype
+ * @param unknown_type $name
+ * @param unknown_type $value
+ * @param unknown_type $value_type
+ * @param unknown_type $owner_guid
+ * @param unknown_type $timelower
+ * @param unknown_type $timeupper
+ * @param unknown_type $calculation
+ * @internal Don't use this at all.
+ * @deprecated 1.8 Use elgg_get_annotations()
+ */
+function elgg_deprecated_annotation_calculation($entity_guid = 0, $entity_type = "", $entity_subtype = "",
+$name = "", $value = "", $value_type = "", $owner_guid = 0, $timelower = 0,
+$timeupper = 0, $calculation = '') {
+
+ $options = array('annotation_calculation' => $calculation);
+
+ if ($entity_guid) {
+ $options['guid'] = $entity_guid;
+ }
+
+ if ($entity_type) {
+ $options['type'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtype'] = $entity_subtype;
+ }
+
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ if ($value) {
+ $options['annotation_value'] = $value;
+ }
+
+ if ($owner_guid) {
+ $options['annotation_owner_guid'] = $owner_guid;
+ }
+
+ if ($order_by == 'desc') {
+ $options['order_by'] = 'n_table.time_created desc';
+ }
+
+ if ($timelower) {
+ $options['annotation_time_lower'] = $timelower;
+ }
+
+ if ($timeupper) {
+ $options['annotation_time_upper'] = $timeupper;
+ }
+
+ return elgg_get_annotations($options);
+}
+
+/**
+ * Count the number of annotations based on search parameters
+ *
+ * @param int $entity_guid Guid of Entity
+ * @param string $entity_type Type of Entity
+ * @param string $entity_subtype Subtype of Entity
+ * @param string $name Name of annotation
+ * @param string $value Value of annotation
+ * @param string $value_type Type of value
+ * @param int $owner_guid GUID of owner of annotation
+ * @param int $timelower Lower time limit
+ * @param int $timeupper Upper time limit
+ *
+ * @deprecated 1.8 Use elgg_get_annotations() and pass 'count' => true
+ * @return int
+ */
+function count_annotations($entity_guid = 0, $entity_type = "", $entity_subtype = "",
+$name = "", $value = "", $value_type = "", $owner_guid = 0, $timelower = 0,
+$timeupper = 0) {
+ elgg_deprecated_notice('count_annotations() is deprecated by elgg_get_annotations() and passing "count" => true', 1.8);
+ return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype,
+ $name, $value, $value_type, $owner_guid, $timelower, $timeupper, 'count');
+}
+
+/**
+ * Return the sum of a given integer annotation.
+ *
+ * @param int $entity_guid Guid of Entity
+ * @param string $entity_type Type of Entity
+ * @param string $entity_subtype Subtype of Entity
+ * @param string $name Name of annotation
+ * @param string $value Value of annotation
+ * @param string $value_type Type of value
+ * @param int $owner_guid GUID of owner of annotation
+ *
+ * @deprecated 1.8 Use elgg_get_annotations() and pass 'annotation_calculation' => 'sum'
+ * @return int
+ */
+function get_annotations_sum($entity_guid, $entity_type = "", $entity_subtype = "", $name = "",
+$value = "", $value_type = "", $owner_guid = 0) {
+ elgg_deprecated_notice('get_annotations_sum() is deprecated by elgg_get_annotations() and passing "annotation_calculation" => "sum"', 1.8);
+
+ return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype,
+ $name, $value, $value_type, $owner_guid, $timelower, $timeupper, 'sum');
+}
+
+/**
+ * Return the max of a given integer annotation.
+ *
+ * @param int $entity_guid Guid of Entity
+ * @param string $entity_type Type of Entity
+ * @param string $entity_subtype Subtype of Entity
+ * @param string $name Name of annotation
+ * @param string $value Value of annotation
+ * @param string $value_type Type of value
+ * @param int $owner_guid GUID of owner of annotation
+ *
+ * @deprecated 1.8 Use elgg_get_annotations() and pass 'annotation_calculation' => 'max'
+ * @return int
+ */
+function get_annotations_max($entity_guid, $entity_type = "", $entity_subtype = "", $name = "",
+$value = "", $value_type = "", $owner_guid = 0) {
+ elgg_deprecated_notice('get_annotations_max() is deprecated by elgg_get_annotations() and passing "annotation_calculation" => "max"', 1.8);
+
+ return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype,
+ $name, $value, $value_type, $owner_guid, $timelower, $timeupper, 'max');
+}
+
+
+/**
+ * Return the minumum of a given integer annotation.
+ *
+ * @param int $entity_guid Guid of Entity
+ * @param string $entity_type Type of Entity
+ * @param string $entity_subtype Subtype of Entity
+ * @param string $name Name of annotation
+ * @param string $value Value of annotation
+ * @param string $value_type Type of value
+ * @param int $owner_guid GUID of owner of annotation
+ *
+ * @deprecated 1.8 Use elgg_get_annotations() and pass 'annotation_calculation' => 'min'
+ * @return int
+ */
+function get_annotations_min($entity_guid, $entity_type = "", $entity_subtype = "", $name = "",
+$value = "", $value_type = "", $owner_guid = 0) {
+ elgg_deprecated_notice('get_annotations_min() is deprecated by elgg_get_annotations() and passing "annotation_calculation" => "min"', 1.8);
+
+ return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype,
+ $name, $value, $value_type, $owner_guid, $timelower, $timeupper, 'min');
+}
+
+
+/**
+ * Return the average of a given integer annotation.
+ *
+ * @param int $entity_guid Guid of Entity
+ * @param string $entity_type Type of Entity
+ * @param string $entity_subtype Subtype of Entity
+ * @param string $name Name of annotation
+ * @param string $value Value of annotation
+ * @param string $value_type Type of value
+ * @param int $owner_guid GUID of owner of annotation
+ *
+ * @deprecated 1.8 Use elgg_get_annotations() and pass 'annotation_calculation' => 'min'
+ *
+ * @return int
+ */
+function get_annotations_avg($entity_guid, $entity_type = "", $entity_subtype = "", $name = "",
+$value = "", $value_type = "", $owner_guid = 0) {
+ elgg_deprecated_notice('get_annotations_avg() is deprecated by elgg_get_annotations() and passing "annotation_calculation" => "avg"', 1.8);
+
+ return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype,
+ $name, $value, $value_type, $owner_guid, $timelower, $timeupper, 'avg');
+}
+
+
+/**
+ * Perform a mathmatical calculation on integer annotations.
+ *
+ * @param string $sum What sort of calculation to perform
+ * @param int $entity_guid Guid of Entity
+ * @param string $entity_type Type of Entity
+ * @param string $entity_subtype Subtype of Entity
+ * @param string $name Name of annotation
+ * @param string $value Value of annotation
+ * @param string $value_type Type of value
+ * @param int $owner_guid GUID of owner of annotation
+ * @param int $timelower Lower time limit
+ * @param int $timeupper Upper time limit
+ *
+ * @return int
+ * @deprecated 1.8 Use elgg_get_annotations() and pass anntoation_calculation => <calculation>
+ */
+function get_annotations_calculate_x($sum = "avg", $entity_guid, $entity_type = "",
+$entity_subtype = "", $name = "", $value = "", $value_type = "", $owner_guid = 0,
+$timelower = 0, $timeupper = 0) {
+ elgg_deprecated_notice('get_annotations_calculate_x() is deprecated by elgg_get_annotations() and passing "annotation_calculation" => "calculation"', 1.8);
+
+ return elgg_deprecated_annotation_calculation($entity_guid, $entity_type, $entity_subtype,
+ $name, $value, $value_type, $owner_guid, $timelower, $timeupper, $sum);
+}
+
+
+/**
+ * Lists entities by the totals of a particular kind of annotation AND
+ * the value of a piece of metadata
+ *
+ * @param string $entity_type Type of entity.
+ * @param string $entity_subtype Subtype of entity.
+ * @param string $name Name of annotation.
+ * @param string $mdname Metadata name
+ * @param string $mdvalue Metadata value
+ * @param int $limit Maximum number of results to return.
+ * @param int $owner_guid Owner.
+ * @param int $group_guid Group container. Currently only supported if entity_type is object
+ * @param boolean $asc Whether to list in ascending or descending order (default: desc)
+ * @param boolean $fullview Whether to display the entities in full
+ * @param boolean $listtypetoggle Can the 'gallery' view can be displayed (default: no)
+ * @param boolean $pagination Display pagination
+ * @param string $orderdir 'desc' or 'asc'
+ *
+ * @deprecated 1.8 Use elgg_list_entities_from_annotation_calculation().
+ *
+ * @return string Formatted entity list
+ */
+function list_entities_from_annotation_count_by_metadata($entity_type = "", $entity_subtype = "",
+$name = "", $mdname = '', $mdvalue = '', $limit = 10, $owner_guid = 0, $group_guid = 0,
+$asc = false, $fullview = true, $listtypetoggle = false, $pagination = true, $orderdir = 'desc') {
+
+ $msg = 'list_entities_from_annotation_count_by_metadata() is deprecated by elgg_list_entities_from_annotation_calculation().';
+
+ elgg_deprecated_notice($msg, 1.8);
+
+ $options = array();
+
+ $options['calculation'] = 'sum';
+
+ if ($entity_type) {
+ $options['types'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtypes'] = $entity_subtype;
+ }
+
+ $options['annotation_names'] = $name;
+
+ if ($mdname) {
+ $options['metadata_name'] = $mdname;
+ }
+
+ if ($mdvalue) {
+ $options['metadata_value'] = $mdvalue;
+ }
+
+ if ($owner_guid) {
+ if (is_array($owner_guid)) {
+ $options['owner_guids'] = $owner_guid;
+ } else {
+ $options['owner_guid'] = $owner_guid;
+ }
+ }
+
+ $options['full_view'] = $fullview;
+
+ $options['list_type_toggle'] = $listtypetoggle;
+
+ $options['pagination'] = $pagination;
+
+ $options['limit'] = $limit;
+
+ $options['order_by'] = "annotation_calculation $orderdir";
+
+ return elgg_get_entities_from_annotation_calculation($options);
+}
+
+/**
+ * Set an alternative base location for a view (as opposed to the default of $CONFIG->viewpath)
+ *
+ * @param string $view The name of the view
+ * @param string $location The base location path
+ *
+ * @deprecated 1.8 Use elgg_set_view_location()
+ */
+function set_view_location($view, $location, $viewtype = '') {
+ elgg_deprecated_notice("set_view_location() was deprecated by elgg_set_view_location()", 1.8);
+ return elgg_set_view_location($view, $location, $viewtype);
+}
+
+/**
+ * Sets the URL handler for a particular entity type and subtype
+ *
+ * @param string $function_name The function to register
+ * @param string $entity_type The entity type
+ * @param string $entity_subtype The entity subtype
+ * @return true|false Depending on success
+ *
+ * @deprecated 1.8 Use elgg_register_entity_url_handler()
+ */
+function register_entity_url_handler($function_name, $entity_type = "all", $entity_subtype = "all") {
+ elgg_deprecated_notice("register_entity_url_handler() was deprecated by elgg_register_entity_url_handler()", 1.8);
+ return elgg_register_entity_url_handler($entity_type, $entity_subtype, $function_name);
+}
+
+
+/**
+ * Get the metadata where the entities they are referring to match a given criteria.
+ *
+ * @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 $limit Limit
+ * @param int $offset Offset
+ * @param string $order_by Optional ordering.
+ * @param int $site_guid Site GUID. 0 for current, -1 for any
+ *
+ * @return mixed
+ * @deprecated 1.8 Use elgg_get_metadata()
+ */
+function find_metadata($meta_name = "", $meta_value = "", $entity_type = "", $entity_subtype = "",
+ $limit = 10, $offset = 0, $order_by = "", $site_guid = 0) {
+
+ elgg_deprecated_notice('get_metadata() is deprecated by elgg_get_metadata()', 1.8);
+
+ $options = array();
+
+ if ($meta_name) {
+ $options['annotation_name'] = $meta_name;
+ }
+
+ if ($meta_value) {
+ $options['annotation_value'] = $meta_value;
+ }
+
+ if ($entity_type) {
+ $options['type'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtype'] = $entity_subtype;
+ }
+
+ $options['limit'] = $limit;
+ $options['offset'] = $offset;
+
+ if ($order_by == 'desc') {
+ $options['order_by'] = 'n_table.time_created desc';
+ }
+
+ if ($site_guid) {
+ $options['site_guid'] = $site_guid;
+ }
+
+ return elgg_get_metadata($options);
+}
+
+/**
+ * Get metadata objects by name.
+ *
+ * @param int $entity_guid Entity GUID
+ * @param string $meta_name Metadata name
+ *
+ * @return mixed ElggMetadata object, an array of ElggMetadata or false.
+ * @deprecated 1.8 Use elgg_get_metadata()
+ */
+function get_metadata_byname($entity_guid, $meta_name) {
+ elgg_deprecated_notice('get_metadata_byname() is deprecated by elgg_get_metadata()', 1.8);
+
+ if (!$entity_guid || !$meta_name) {
+ return false;
+ }
+
+ $options = array(
+ 'guid' => $entity_guid,
+ 'metadata_name' => $meta_name,
+ 'limit' => 0
+ );
+
+ $md = elgg_get_metadata($options);
+
+ if ($md && count($md) == 1) {
+ return $md[0];
+ }
+
+ return $md;
+}
+
+/**
+ * Return all the metadata for a given GUID.
+ *
+ * @param int $entity_guid Entity GUID
+ *
+ * @return mixed
+ * @deprecated 1.8 Use elgg_get_metadata()
+ */
+function get_metadata_for_entity($entity_guid) {
+ elgg_deprecated_notice('get_metadata_for_entity() is deprecated by elgg_get_metadata()', 1.8);
+
+ if (!$entity_guid) {
+ return false;
+ }
+
+ $options = array(
+ 'guid' => $entity_guid,
+ 'limit' => 0
+ );
+
+ return elgg_get_metadata($options);
+}
+
+/**
+ * Get a specific metadata object.
+ *
+ * @param int $id The id of the metadata being retrieved.
+ *
+ * @return mixed False on failure or ElggMetadata
+ * @deprecated 1.8 Use elgg_get_metadata_from_id()
+ */
+function get_metadata($id) {
+ elgg_deprecated_notice('get_metadata() is deprecated by elgg_get_metadata_from_id()', 1.8);
+ return elgg_get_metadata_from_id($id);
+}
+
+/**
+ * Clear all the metadata for a given entity, assuming you have access to that entity.
+ *
+ * @param int $guid Entity GUID
+ *
+ * @return bool
+ * @deprecated 1.8 Use elgg_delete_metadata()
+ */
+function clear_metadata($guid) {
+ elgg_deprecated_notice('clear_metadata() is deprecated by elgg_delete_metadata()', 1.8);
+ if (!$guid) {
+ return false;
+ }
+ return elgg_delete_metadata(array('guid' => $guid, 'limit' => 0));
+}
+
+/**
+ * Clear all metadata belonging to a given owner_guid
+ *
+ * @param int $owner_guid The owner
+ *
+ * @return bool
+ * @deprecated 1.8 Use elgg_delete_metadata()
+ */
+function clear_metadata_by_owner($owner_guid) {
+ elgg_deprecated_notice('clear_metadata() is deprecated by elgg_delete_metadata()', 1.8);
+ if (!$owner_guid) {
+ return false;
+ }
+ return elgg_delete_metadata(array('metadata_owner_guid' => $owner_guid, 'limit' => 0));
+}
+
+/**
+ * Delete a piece of metadata, where the current user has access.
+ *
+ * @param int $id The id of metadata to delete.
+ *
+ * @return bool
+ * @deprecated 1.8 Use elgg_delete_metadata()
+ */
+function delete_metadata($id) {
+ elgg_deprecated_notice('delete_metadata() is deprecated by elgg_delete_metadata()', 1.8);
+ if (!$id) {
+ return false;
+ }
+ return elgg_delete_metadata(array('metadata_id' => $id));
+}
+
+/**
+ * Removes metadata on an entity with a particular name, optionally with a given value.
+ *
+ * @param int $guid The entity GUID
+ * @param string $name The name of the metadata
+ * @param string $value The value of the metadata (useful to remove a single item of a set)
+ *
+ * @return bool Depending on success
+ * @deprecated 1.8 Use elgg_delete_metadata()
+ */
+function remove_metadata($guid, $name, $value = "") {
+ elgg_deprecated_notice('delete_metadata() is deprecated by elgg_delete_metadata()', 1.8);
+
+ // prevent them from deleting everything
+ if (!$guid) {
+ return false;
+ }
+
+ $options = array(
+ 'guid' => $guid,
+ 'metadata_name' => $name,
+ 'limit' => 0
+ );
+
+ if ($value) {
+ $options['metadata_value'] = $value;
+ }
+
+ return elgg_delete_metadata($options);
+}
+
+/**
+ * Get a specific annotation.
+ *
+ * @param int $annotation_id Annotation ID
+ *
+ * @return ElggAnnotation
+ * @deprecated 1.8 Use elgg_get_annotation_from_id()
+ */
+function get_annotation($annotation_id) {
+ elgg_deprecated_notice('get_annotation() is deprecated by elgg_get_annotation_from_id()', 1.8);
+ return elgg_get_annotation_from_id($annotation_id);
+}
+
+/**
+ * Delete a given annotation.
+ *
+ * @param int $id The annotation id
+ *
+ * @return bool
+ * @deprecated 1.8 Use elgg_delete_annotations()
+ */
+function delete_annotation($id) {
+ elgg_deprecated_notice('delete_annotation() is deprecated by elgg_delete_annotations()', 1.8);
+ if (!$id) {
+ return false;
+ }
+ return elgg_delete_annotations(array('annotation_id' => $annotation_id));
+}
+
+/**
+ * Clear all the annotations for a given entity, assuming you have access to that metadata.
+ *
+ * @param int $guid The entity guid
+ * @param string $name The name of the annotation to delete.
+ *
+ * @return int Number of annotations deleted or false if an error
+ * @deprecated 1.8 Use elgg_delete_annotations()
+ */
+function clear_annotations($guid, $name = "") {
+ elgg_deprecated_notice('clear_annotations() is deprecated by elgg_delete_annotations()', 1.8);
+
+ if (!$guid) {
+ return false;
+ }
+
+ $options = array(
+ 'guid' => $guid,
+ 'limit' => 0
+ );
+
+ if ($name) {
+ $options['annotation_name'] = $name;
+ }
+
+ return elgg_delete_annotations($options);
+}
+
+/**
+ * Clear all annotations belonging to a given owner_guid
+ *
+ * @param int $owner_guid The owner
+ *
+ * @return int Number of annotations deleted
+ * @deprecated 1.8 Use elgg_delete_annotations()
+ */
+function clear_annotations_by_owner($owner_guid) {
+ elgg_deprecated_notice('clear_annotations_by_owner() is deprecated by elgg_delete_annotations()', 1.8);
+
+ if (!$owner_guid) {
+ return false;
+ }
+
+ $options = array(
+ 'annotation_owner_guid' => $guid,
+ 'limit' => 0
+ );
+
+ return elgg_delete_annotations($options);
+}
+
+/**
+ * Registers a page handler for a particular identifier
+ *
+ * For example, you can register a function called 'blog_page_handler' for handler type 'blog'
+ * Now for all URLs of type http://yoururl/pg/blog/*, the blog_page_handler() function will be called.
+ * The part of the URL marked with * above will be exploded on '/' characters and passed as an
+ * array to that function.
+ * For example, the URL http://yoururl/blog/username/friends/ would result in the call:
+ * blog_page_handler(array('username','friends'), blog);
+ *
+ * Page handler functions should return true or the default page handler will be called.
+ *
+ * A request to register a page handler with the same identifier as previously registered
+ * handler will replace the previous one.
+ *
+ * The context is set to the page handler identifier before the registered
+ * page handler function is called. For the above example, the context is set to 'blog'.
+ *
+ * @param string $handler The page type to handle
+ * @param string $function Your function name
+ * @return true|false Depending on success
+ *
+ * @deprecated 1.8 Use {@link elgg_register_page_handler()}
+ */
+function register_page_handler($handler, $function){
+ elgg_deprecated_notice("register_page_handler() was deprecated by elgg_register_page_handler()", 1.8);
+ return elgg_register_page_handler($handler, $function);
+}
+
+/**
+ * Unregister a page handler for an identifier
+ *
+ * Note: to replace a page handler, call register_page_handler()
+ *
+ * @param string $handler The page type identifier
+ * @since 1.7.2
+ *
+ * @deprecated 1.8 Use {@link elgg_unregister_page_handler()}
+ */
+function unregister_page_handler($handler) {
+ elgg_deprecated_notice("unregister_page_handler() was deprecated by elgg_unregister_page_handler()", 1.8);
+ return elgg_unregister_page_handler($handler);
+}
+
+/**
+ * Register an annotation url handler.
+ *
+ * @param string $function_name The function.
+ * @param string $extender_name The name, default 'all'.
+ *
+ * @deprecated 1.8 Use {@link elgg_register_annotation_url_handler()}
+ */
+function register_annotation_url_handler($function, $extender_name) {
+ elgg_deprecated_notice("register_annotation_url_handler() was deprecated by elgg_register_annotation_url_handler()", 1.8);
+ return elgg_register_annotation_url_handler($extender_name, $function);
+}
+
+/**
+ * Sets the URL handler for a particular extender type and name.
+ * It is recommended that you do not call this directly, instead use one of the wrapper functions in the
+ * subtype files.
+ *
+ * @param string $function_name The function to register
+ * @param string $extender_type Extender type
+ * @param string $extender_name The name of the extender
+ * @return true|false Depending on success
+ *
+ * @deprecated 1.8 Use {@link elgg_register_extender_url_handler()}
+ */
+function register_extender_url_handler($function, $type = "all", $name = "all") {
+ elgg_deprecated_notice("register_extender_url_handler() was deprecated by elgg_register_extender_url_handler()", 1.8);
+ return elgg_register_extender_url_handler($type, $name, $function);
+}
+
+/**
+ * Registers and entity type and subtype to return in search and other places.
+ * A description in the elgg_echo languages file of the form item:type:subtype
+ * is also expected.
+ *
+ * @param string $type The type of entity (object, site, user, group)
+ * @param string $subtype The subtype to register (may be blank)
+ * @return true|false Depending on success
+ *
+ * @deprecated 1.8 Use {@link elgg_register_entity_type()}
+ */
+function register_entity_type($type, $subtype = null) {
+ elgg_deprecated_notice("register_entity_type() was deprecated by elgg_register_entity_type()", 1.8);
+ return elgg_register_entity_type($type, $subtype);
+}
+
+/**
+ * Register a metadata url handler.
+ *
+ * @param string $function_name The function.
+ * @param string $extender_name The name, default 'all'.
+ *
+ * @deprecated 1.8 Use {@link elgg_register_metadata_url_handler()}
+ */
+function register_metadata_url_handler($function, $extender_name = "all") {
+ return elgg_register_metadata_url_handler($extender_name, $function);
+}
+
+/**
+ * Sets the URL handler for a particular relationship type
+ *
+ * @param string $function_name The function to register
+ * @param string $relationship_type The relationship type.
+ * @return true|false Depending on success
+ *
+ * @deprecated 1.8 Use {@link elgg_register_relationship_url_handler()}
+ */
+function register_relationship_url_handler($function_name, $relationship_type = "all") {
+ elgg_deprecated_notice("register_relationship_url_handler() was deprecated by elgg_register_relationship_url_handler()", 1.8);
+ return elgg_register_relationship_url_handler($relationship_type, $function_name);
+}
+
+/**
+ * Registers a view to be simply cached
+ *
+ * Views cached in this manner must take no parameters and be login agnostic -
+ * that is to say, they look the same no matter who is logged in (or logged out).
+ *
+ * CSS and the basic jS views are automatically cached like this.
+ *
+ * @param string $viewname View name
+ *
+ * @deprecated 1.8 Use {@link elgg_register_simplecache_view()}
+ */
+function elgg_view_register_simplecache($viewname) {
+ elgg_deprecated_notice("elgg_view_register_simplecache() was deprecated by elgg_register_simplecache_view()", 1.8);
+ return elgg_register_simplecache_view($viewname);
+}
+
+/**
+ * Regenerates the simple cache.
+ *
+ * @param string $viewtype Optional viewtype to regenerate
+ * @see elgg_view_register_simplecache()
+ *
+ * @deprecated 1.8 Use {@link elgg_regenerate_simplecache()}
+ */
+function elgg_view_regenerate_simplecache($viewtype = NULL) {
+ elgg_deprecated_notice("elgg_view_regenerate_simplecache() was deprecated by elgg_regenerate_simplecache()", 1.8);
+ return elgg_regenerate_simplecache($viewtype);
+}
+
+/**
+ * Enables the simple cache.
+ *
+ * @see elgg_view_register_simplecache()
+ *
+ * @deprecated 1.8 Use {@link elgg_enable_simplecache()}
+ */
+function elgg_view_enable_simplecache() {
+ elgg_deprecated_notice("elgg_view_enable_simplecache() was deprecated by elgg_enable_simplecache()", 1.8);
+ return elgg_enable_simplecache();
+}
+
+/**
+ * Disables the simple cache.
+ *
+ * @see elgg_view_register_simplecache()
+ *
+ * @deprecated 1.8 Use {@link elgg_disable_simplecache()}
+ */
+function elgg_view_disable_simplecache() {
+ elgg_deprecated_notice("elgg_view_disable_simplecache() was deprecated by elgg_disable_simplecache()", 1.8);
+ return elgg_disable_simplecache();
+}
+
+// these were internal functions that perhaps can be removed rather than deprecated
+/**
+ * @deprecated 1.8
+ */
+function is_db_installed() {
+ elgg_deprecated_notice('is_db_installed() has been deprecated', 1.8);
+ return true;
+}
+
+/**
+ * @deprecated 1.8
+ */
+function is_installed() {
+ elgg_deprecated_notice('is_installed() has been deprecated', 1.8);
+ return true;
+}
+
+/**
+ * Attempt to authenticate.
+ * This function will process all registered PAM handlers or stop when the first
+ * handler fails. A handler fails by either returning false or throwing an
+ * exception. The advantage of throwing an exception is that it returns a message
+ * through the global $_PAM_HANDLERS_MSG which can be used in communication with
+ * a user. The order that handlers are processed is determined by the order that
+ * they were registered.
+ *
+ * If $credentials are provided the PAM handler should authenticate using the
+ * provided credentials, if not then credentials should be prompted for or
+ * otherwise retrieved (eg from the HTTP header or $_SESSION).
+ *
+ * @param mixed $credentials Mixed PAM handler specific credentials (e.g. username, password)
+ * @param string $policy - the policy type, default is "user"
+ * @return bool true if authenticated, false if not.
+ *
+ * @deprecated 1.8 See {@link ElggPAM}
+ */
+function pam_authenticate($credentials = NULL, $policy = "user") {
+ elgg_deprecated_notice('pam_authenticate has been deprecated for ElggPAM', 1.8);
+ global $_PAM_HANDLERS, $_PAM_HANDLERS_MSG;
+
+ $_PAM_HANDLERS_MSG = array();
+
+ $authenticated = false;
+
+ foreach ($_PAM_HANDLERS[$policy] as $k => $v) {
+ $handler = $v->handler;
+ $importance = $v->importance;
+
+ try {
+ // Execute the handler
+ if ($handler($credentials)) {
+ // Explicitly returned true
+ $_PAM_HANDLERS_MSG[$k] = "Authenticated!";
+
+ $authenticated = true;
+ } else {
+ $_PAM_HANDLERS_MSG[$k] = "Not Authenticated.";
+
+ // If this is required then abort.
+ if ($importance == 'required') {
+ return false;
+ }
+ }
+ } catch (Exception $e) {
+ $_PAM_HANDLERS_MSG[$k] = "$e";
+
+ // If this is required then abort.
+ if ($importance == 'required') {
+ return false;
+ }
+ }
+ }
+
+ return $authenticated;
+}
+
+
+/**
+ * When given a widget entity and a new requested location, saves the new location
+ * and also provides a sensible ordering for all widgets in that column
+ *
+ * @param ElggObject $widget The widget entity
+ * @param int $order The order within the column
+ * @param int $column The column (1, 2 or 3)
+ *
+ * @return bool Depending on success
+ * @deprecated 1.8 use ElggWidget::move()
+ */
+function save_widget_location(ElggObject $widget, $order, $column) {
+ elgg_deprecated_notice('save_widget_location() is deprecated', 1.8);
+ if ($widget instanceof ElggObject) {
+ if ($widget->subtype == "widget") {
+ // If you can't move the widget, don't save a new location
+ if (!$widget->draggable) {
+ return false;
+ }
+
+ // Sanitise the column value
+ if ($column != 1 || $column != 2 || $column != 3) {
+ $column = 1;
+ }
+
+ $widget->column = (int) $column;
+
+ $ordertmp = array();
+ $params = array(
+ 'context' => $widget->context,
+ 'column' => $column,
+ );
+
+ if ($entities = get_entities_from_metadata_multi($params, 'object', 'widget')) {
+ foreach ($entities as $entity) {
+ $entityorder = $entity->order;
+ if ($entityorder < $order) {
+ $ordertmp[$entityorder] = $entity;
+ }
+ if ($entityorder >= $order) {
+ $ordertmp[$entityorder + 10000] = $entity;
+ }
+ }
+ }
+
+ $ordertmp[$order] = $widget;
+ ksort($ordertmp);
+
+ $orderticker = 10;
+ foreach ($ordertmp as $orderval => $entity) {
+ $entity->order = $orderticker;
+ $orderticker += 10;
+ }
+
+ return true;
+ } else {
+ register_error($widget->subtype);
+ }
+
+ }
+
+ return false;
+}
+
+/**
+ * Get widgets for a particular context and column, in order of display
+ *
+ * @param int $user_guid The owner user GUID
+ * @param string $context The context (profile, dashboard etc)
+ * @param int $column The column (1 or 2)
+ *
+ * @return array|false An array of widget ElggObjects, or false
+ * @deprecated 1.8 Use elgg_get_widgets()
+ */
+function get_widgets($user_guid, $context, $column) {
+ elgg_deprecated_notice('get_widgets is depecated for elgg_get_widgets', 1.8);
+ $params = array(
+ 'column' => $column,
+ 'context' => $context
+ );
+ $widgets = get_entities_from_private_setting_multi($params, "object",
+ "widget", $user_guid, "", 10000);
+
+ if ($widgets) {
+ $widgetorder = array();
+ foreach ($widgets as $widget) {
+ $order = $widget->order;
+ while (isset($widgetorder[$order])) {
+ $order++;
+ }
+ $widgetorder[$order] = $widget;
+ }
+
+ ksort($widgetorder);
+
+ return $widgetorder;
+ }
+
+ return false;
+}
+
+/**
+ * Add a new widget instance
+ *
+ * @param int $entity_guid GUID of entity that owns this widget
+ * @param string $handler The handler for this widget
+ * @param string $context The page context for this widget
+ * @param int $order The order to display this widget in
+ * @param int $column The column to display this widget in (1, 2 or 3)
+ * @param int $access_id If not specified, it is set to the default access level
+ *
+ * @return int|false Widget GUID or false on failure
+ * @deprecated 1.8 use elgg_create_widget()
+ */
+function add_widget($entity_guid, $handler, $context, $order = 0, $column = 1, $access_id = null) {
+ elgg_deprecated_notice('add_widget has been deprecated for elgg_create_widget', 1.8);
+ if (empty($entity_guid) || empty($context) || empty($handler) || !widget_type_exists($handler)) {
+ return false;
+ }
+
+ if ($entity = get_entity($entity_guid)) {
+ $widget = new ElggWidget;
+ $widget->owner_guid = $entity_guid;
+ $widget->container_guid = $entity_guid;
+ if (isset($access_id)) {
+ $widget->access_id = $access_id;
+ } else {
+ $widget->access_id = get_default_access();
+ }
+
+ $guid = $widget->save();
+
+ // private settings cannot be set until ElggWidget saved
+ $widget->handler = $handler;
+ $widget->context = $context;
+ $widget->column = $column;
+ $widget->order = $order;
+
+ return $guid;
+ }
+
+ return false;
+}
+
+/**
+ * Define a new widget type
+ *
+ * @param string $handler The identifier for the widget handler
+ * @param string $name The name of the widget type
+ * @param string $description A description for the widget type
+ * @param string $context A comma-separated list of contexts where this
+ * widget is allowed (default: 'all')
+ * @param bool $multiple Whether or not multiple instances of this widget
+ * are allowed on a single dashboard (default: false)
+ * @param string $positions A comma-separated list of positions on the page
+ * (side or main) where this widget is allowed (default: "side,main")
+ *
+ * @return bool Depending on success
+ * @deprecated 1.8 Use elgg_register_widget_type
+ */
+function add_widget_type($handler, $name, $description, $context = "all",
+$multiple = false, $positions = "side,main") {
+ elgg_deprecated_notice("add_widget_type deprecated for elgg_register_widget_type", 1.8);
+
+ return elgg_register_widget_type($handler, $name, $description, $context, $multiple);
+}
+
+/**
+ * Remove a widget type
+ *
+ * @param string $handler The identifier for the widget handler
+ *
+ * @return void
+ * @since 1.7.1
+ * @deprecated 1.8 Use elgg_unregister_widget_type
+ */
+function remove_widget_type($handler) {
+ elgg_deprecated_notice("remove_widget_type deprecated for elgg_unregister_widget_type", 1.8);
+ return elgg_unregister_widget_type($handler);
+}
+
+/**
+ * Determines whether or not widgets with the specified handler have been defined
+ *
+ * @param string $handler The widget handler identifying string
+ *
+ * @return bool Whether or not those widgets exist
+ * @deprecated 1.8 Use elgg_is_widget_type
+ */
+function widget_type_exists($handler) {
+ elgg_deprecated_notice("widget_type_exists deprecated for elgg_is_widget_type", 1.8);
+ return elgg_is_widget_type($handler);
+}
+
+/**
+ * Returns an array of stdClass objects representing the defined widget types
+ *
+ * @return array A list of types defined (if any)
+ * @deprecated 1.8 Use elgg_get_widget_types
+ */
+function get_widget_types() {
+ elgg_deprecated_notice("get_widget_types deprecrated for elgg_get_widget_types", 1.8);
+ return elgg_get_widget_types();
+}
+
+/**
+ * Saves a widget's settings (by passing an array of
+ * (name => value) pairs to save_{$handler}_widget)
+ *
+ * @param int $widget_guid The GUID of the widget we're saving to
+ * @param array $params An array of name => value parameters
+ *
+ * @return bool
+ * @deprecated 1.8 Use elgg_save_widget_settings
+ */
+function save_widget_info($widget_guid, $params) {
+ elgg_deprecated_notice("save_widget_info() is deprecated for elgg_save_widget_settings", 1.8);
+ if ($widget = get_entity($widget_guid)) {
+
+ $subtype = $widget->getSubtype();
+
+ if ($subtype != "widget") {
+ return false;
+ }
+ $handler = $widget->handler;
+ if (empty($handler) || !widget_type_exists($handler)) {
+ return false;
+ }
+
+ if (!$widget->canEdit()) {
+ return false;
+ }
+
+ // Save the params to the widget
+ if (is_array($params) && sizeof($params) > 0) {
+ foreach ($params as $name => $value) {
+
+ if (!empty($name) && !in_array($name, array(
+ 'guid', 'owner_guid', 'site_guid'
+ ))) {
+ if (is_array($value)) {
+ // @todo Handle arrays securely
+ $widget->setMetaData($name, $value, "", true);
+ } else {
+ $widget->$name = $value;
+ }
+ }
+ }
+ $widget->save();
+ }
+
+ $function = "save_{$handler}_widget";
+ if (is_callable($function)) {
+ return $function($params);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Reorders the widgets from a widget panel
+ *
+ * @param string $panelstring1 String of guids of ElggWidget objects separated by ::
+ * @param string $panelstring2 String of guids of ElggWidget objects separated by ::
+ * @param string $panelstring3 String of guids of ElggWidget objects separated by ::
+ * @param string $context Profile or dashboard
+ * @param int $owner Owner guid
+ *
+ * @return void
+ * @deprecated 1.8 Don't use.
+ */
+function reorder_widgets_from_panel($panelstring1, $panelstring2, $panelstring3, $context, $owner) {
+ elgg_deprecated_notice("reorder_widgets_from_panel() is deprecated", 1.8);
+ $return = true;
+
+ $mainwidgets = explode('::', $panelstring1);
+ $sidewidgets = explode('::', $panelstring2);
+ $rightwidgets = explode('::', $panelstring3);
+
+ $handlers = array();
+ $guids = array();
+
+ if (is_array($mainwidgets) && sizeof($mainwidgets) > 0) {
+ foreach ($mainwidgets as $widget) {
+
+ $guid = (int) $widget;
+
+ if ("{$guid}" == "{$widget}") {
+ $guids[1][] = $widget;
+ } else {
+ $handlers[1][] = $widget;
+ }
+ }
+ }
+ if (is_array($sidewidgets) && sizeof($sidewidgets) > 0) {
+ foreach ($sidewidgets as $widget) {
+
+ $guid = (int) $widget;
+
+ if ("{$guid}" == "{$widget}") {
+ $guids[2][] = $widget;
+ } else {
+ $handlers[2][] = $widget;
+ }
+
+ }
+ }
+ if (is_array($rightwidgets) && sizeof($rightwidgets) > 0) {
+ foreach ($rightwidgets as $widget) {
+
+ $guid = (int) $widget;
+
+ if ("{$guid}" == "{$widget}") {
+ $guids[3][] = $widget;
+ } else {
+ $handlers[3][] = $widget;
+ }
+
+ }
+ }
+
+ // Reorder existing widgets or delete ones that have vanished
+ foreach (array(1, 2, 3) as $column) {
+ if ($dbwidgets = get_widgets($owner, $context, $column)) {
+
+ foreach ($dbwidgets as $dbwidget) {
+ if (in_array($dbwidget->getGUID(), $guids[1])
+ || in_array($dbwidget->getGUID(), $guids[2]) || in_array($dbwidget->getGUID(), $guids[3])) {
+
+ if (in_array($dbwidget->getGUID(), $guids[1])) {
+ $pos = array_search($dbwidget->getGUID(), $guids[1]);
+ $col = 1;
+ } else if (in_array($dbwidget->getGUID(), $guids[2])) {
+ $pos = array_search($dbwidget->getGUID(), $guids[2]);
+ $col = 2;
+ } else {
+ $pos = array_search($dbwidget->getGUID(), $guids[3]);
+ $col = 3;
+ }
+ $pos = ($pos + 1) * 10;
+ $dbwidget->column = $col;
+ $dbwidget->order = $pos;
+ } else {
+ $dbguid = $dbwidget->getGUID();
+ if (!$dbwidget->delete()) {
+ $return = false;
+ } else {
+ // Remove state cookie
+ setcookie('widget' + $dbguid, null);
+ }
+ }
+ }
+
+ }
+ // Add new ones
+ if (sizeof($guids[$column]) > 0) {
+ foreach ($guids[$column] as $key => $guid) {
+ if ($guid == 0) {
+ $pos = ($key + 1) * 10;
+ $handler = $handlers[$column][$key];
+ if (!add_widget($owner, $handler, $context, $pos, $column)) {
+ $return = false;
+ }
+ }
+ }
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Register a particular context for use with widgets.
+ *
+ * @param string $context The context we wish to enable context for
+ *
+ * @return void
+ * @deprecated 1.8 Don't use.
+ */
+function use_widgets($context) {
+ elgg_deprecated_notice("use_widgets is deprecated", 1.8);
+ global $CONFIG;
+
+ if (!isset($CONFIG->widgets)) {
+ $CONFIG->widgets = new stdClass;
+ }
+
+ if (!isset($CONFIG->widgets->contexts)) {
+ $CONFIG->widgets->contexts = array();
+ }
+
+ if (!empty($context)) {
+ $CONFIG->widgets->contexts[] = $context;
+ }
+}
+
+/**
+ * Determines whether or not the current context is using widgets
+ *
+ * @return bool Depending on widget status
+ * @deprecated 1.8 Don't use.
+ */
+function using_widgets() {
+ elgg_deprecated_notice("using_widgets is deprecated", 1.8);
+ global $CONFIG;
+
+ $context = elgg_get_context();
+ if (isset($CONFIG->widgets->contexts) && is_array($CONFIG->widgets->contexts)) {
+ if (in_array($context, $CONFIG->widgets->contexts)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Displays a particular widget
+ *
+ * @param ElggObject $widget The widget to display
+ * @return string The HTML for the widget, including JavaScript wrapper
+ *
+ * @deprecated 1.8 Use elgg_view_entity()
+ */
+function display_widget(ElggObject $widget) {
+ elgg_deprecated_notice("display_widget() was been deprecated. Use elgg_view_entity().", 1.8);
+ return elgg_view_entity($widget);
+}
+
+/**
+ * Count the number of comments attached to an entity
+ *
+ * @param ElggEntity $entity
+ * @return int Number of comments
+ * @deprecated 1.8 Use ElggEntity->countComments()
+ */
+function elgg_count_comments($entity) {
+ elgg_deprecated_notice('elgg_count_comments() is deprecated by ElggEntity->countComments()', 1.8);
+
+ if ($entity instanceof ElggEntity) {
+ return $entity->countComments();
+ }
+
+ return 0;
+}
+
+/**
+ * Removes all items relating to a particular acting entity from the river
+ *
+ * @param int $subject_guid The GUID of the entity
+ *
+ * @return bool Depending on success
+ * @deprecated 1.8 Use elgg_delete_river()
+ */
+function remove_from_river_by_subject($subject_guid) {
+ elgg_deprecated_notice("remove_from_river_by_subject() deprecated by elgg_delete_river()", 1.8);
+
+ return elgg_delete_river(array('subject_guid' => $subject_guid));
+}
+
+/**
+ * Removes all items relating to a particular entity being acted upon from the river
+ *
+ * @param int $object_guid The GUID of the entity
+ *
+ * @return bool Depending on success
+ * @deprecated 1.8 Use elgg_delete_river()
+ */
+function remove_from_river_by_object($object_guid) {
+ elgg_deprecated_notice("remove_from_river_by_object() deprecated by elgg_delete_river()", 1.8);
+
+ return elgg_delete_river(array('object_guid' => $object_guid));
+}
+
+/**
+ * Removes all items relating to a particular annotation being acted upon from the river
+ *
+ * @param int $annotation_id The ID of the annotation
+ *
+ * @return bool Depending on success
+ * @since 1.7.0
+ * @deprecated 1.8 Use elgg_delete_river()
+ */
+function remove_from_river_by_annotation($annotation_id) {
+ elgg_deprecated_notice("remove_from_river_by_annotation() deprecated by elgg_delete_river()", 1.8);
+
+ return elgg_delete_river(array('annotation_id' => $annotation_id));
+}
+
+/**
+ * Removes a single river entry
+ *
+ * @param int $id The ID of the river entry
+ *
+ * @return bool Depending on success
+ * @since 1.7.2
+ * @deprecated 1.8 Use elgg_delete_river()
+ */
+function remove_from_river_by_id($id) {
+ elgg_deprecated_notice("remove_from_river_by_id() deprecated by elgg_delete_river()", 1.8);
+
+ return elgg_delete_river(array('id' => $id));
+}
+
+/**
+ * A default page handler
+ * Tries to locate a suitable file to include. Only works for core pages, not plugins.
+ *
+ * @param array $page The page URL elements
+ * @param string $handler The base handler
+ *
+ * @return true|false Depending on success
+ * @deprecated 1.8
+ */
+function default_page_handler($page, $handler) {
+ global $CONFIG;
+
+ elgg_deprecated_notice("default_page_handler is deprecated", "1.8");
+
+ $page = implode('/', $page);
+
+ // protect against including arbitary files
+ $page = str_replace("..", "", $page);
+
+ $callpath = $CONFIG->path . $handler . "/" . $page;
+ if (is_dir($callpath)) {
+ $callpath = sanitise_filepath($callpath);
+ $callpath .= "index.php";
+ if (file_exists($callpath)) {
+ if (include($callpath)) {
+ return TRUE;
+ }
+ }
+ } else if (file_exists($callpath)) {
+ include($callpath);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Invalidate this class's entry in the cache.
+ *
+ * @param int $guid The entity guid
+ *
+ * @return void
+ * @access private
+ * @deprecated 1.8
+ */
+function invalidate_cache_for_entity($guid) {
+ elgg_deprecated_notice('invalidate_cache_for_entity() is a private function and should not be used.', 1.8);
+ _elgg_invalidate_cache_for_entity($guid);
+}
+
+/**
+ * Cache an entity.
+ *
+ * Stores an entity in $ENTITY_CACHE;
+ *
+ * @param ElggEntity $entity Entity to cache
+ *
+ * @return void
+ * @access private
+ * @deprecated 1.8
+ */
+function cache_entity(ElggEntity $entity) {
+ elgg_deprecated_notice('cache_entity() is a private function and should not be used.', 1.8);
+ _elgg_cache_entity($entity);
+}
+
+/**
+ * Retrieve a entity from the cache.
+ *
+ * @param int $guid The guid
+ *
+ * @return ElggEntity|bool false if entity not cached, or not fully loaded
+ * @access private
+ * @deprecated 1.8
+ */
+function retrieve_cached_entity($guid) {
+ elgg_deprecated_notice('retrieve_cached_entity() is a private function and should not be used.', 1.8);
+ return _elgg_retrieve_cached_entity($guid);
+}
diff --git a/engine/lib/deprecated-1.9.php b/engine/lib/deprecated-1.9.php
new file mode 100644
index 000000000..31d03428f
--- /dev/null
+++ b/engine/lib/deprecated-1.9.php
@@ -0,0 +1,582 @@
+<?php
+/**
+ * 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
+ * @deprecated 1.9
+ */
+function get_day_start($day = null, $month = null, $year = null) {
+ elgg_deprecated_notice('get_day_start() has been deprecated', 1.9);
+ 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
+ * @deprecated 1.9
+ */
+function get_day_end($day = null, $month = null, $year = null) {
+ elgg_deprecated_notice('get_day_end() has been deprecated', 1.9);
+ 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
+ * @deprecated 1.9
+ */
+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) {
+ elgg_deprecated_notice('get_notable_entities() has been deprecated', 1.9);
+ 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
+ * @deprecated 1.9
+ */
+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) {
+ elgg_deprecated_notice('get_notable_entities_from_metadata() has been deprecated', 1.9);
+
+ 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
+ * @deprecated 1.9
+ */
+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) {
+ elgg_deprecated_notice('get_noteable_entities_from_relationship() has been deprecated', 1.9);
+
+ 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
+ * @deprecated 1.9
+ */
+function get_todays_entities($type = "", $subtype = "", $owner_guid = 0, $order_by = "",
+$limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = null) {
+ elgg_deprecated_notice('get_todays_entities() has been deprecated', 1.9);
+
+ $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
+ * @deprecated 1.9
+ */
+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) {
+ elgg_deprecated_notice('get_todays_entities_from_metadata() has been deprecated', 1.9);
+
+ $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
+ * @deprecated 1.9
+ */
+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) {
+ elgg_deprecated_notice('get_todays_entities_from_relationship() has been deprecated', 1.9);
+
+ $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
+ * @deprecated 1.9
+ */
+function list_notable_entities($start_time, $end_time, $type= "", $subtype = "", $owner_guid = 0,
+$limit = 10, $fullview = true, $listtypetoggle = false, $navigation = true) {
+ elgg_deprecated_notice('list_notable_entities() has been deprecated', 1.9);
+
+ $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
+ * @deprecated 1.9
+ */
+function list_todays_entities($type= "", $subtype = "", $owner_guid = 0, $limit = 10,
+$fullview = true, $listtypetoggle = false, $navigation = true) {
+ elgg_deprecated_notice('list_todays_entities() has been deprecated', 1.9);
+
+ $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/elgglib.php b/engine/lib/elgglib.php
index a4e83e40b..34111c69d 100644
--- a/engine/lib/elgglib.php
+++ b/engine/lib/elgglib.php
@@ -1,785 +1,546 @@
<?php
/**
- * Elgg library
- * Contains important functionality core to Elgg
+ * Bootstrapping and helper procedural code available for use in Elgg core and plugins.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @todo These functions can't be subpackaged because they cover a wide mix of
+ * purposes and subsystems. Many of them should be moved to more relevant files.
*/
-/**
- * Getting directories and moving the browser
- */
+// prep core classes to be autoloadable
+spl_autoload_register('_elgg_autoload');
+elgg_register_classes(dirname(dirname(__FILE__)) . '/classes');
/**
- * Forwards the browser.
- * Returns false if headers have already been sent and the browser cannot be moved.
+ * Autoload classes
*
- * @param string $location URL to forward to browser to. Can be relative path.
- * @return nothing|false
- */function forward($location = "") {
- global $CONFIG;
-
- if (!headers_sent()) {
- if ($location === REFERER) {
- $location = $_SERVER['HTTP_REFERER'];
- }
-
- $current_page = current_page_url();
- if ((substr_count($location, 'http://') == 0) && (substr_count($location, 'https://') == 0)) {
- $location = $CONFIG->url . $location;
- }
-
- // return new forward location or false to stop the forward or empty string to exit
- $params = array('current_url' => $current_page, 'forward_url' => $location);
- $location = trigger_plugin_hook('forward', 'system', $params, $location);
- if ($location) {
- header("Location: {$location}");
- exit;
- } else if ($location === '') {
- exit;
- }
- }
-
- return false;
-}
-
-/**
- * Return the current page URL.
- */
-function current_page_url() {
- global $CONFIG;
-
- $url = parse_url($CONFIG->wwwroot);
-
- $page = $url['scheme'] . "://";
-
- // user/pass
- if ((isset($url['user'])) && ($url['user'])) {
- $page .= $url['user'];
- }
- if ((isset($url['pass'])) && ($url['pass'])) {
- $page .= ":".$url['pass'];
- }
- if ((isset($url['user']) && $url['user']) ||
- (isset($url['pass']) && $url['pass'])) {
- $page .="@";
- }
-
- $page .= $url['host'];
-
- if ((isset($url['port'])) && ($url['port'])) {
- $page .= ":" . $url['port'];
- }
-
- //$page.="/";
- $page = trim($page, "/");
-
- $page .= $_SERVER['REQUEST_URI'];
-
- return $page;
-}
-
-/**
- * This is a factory function which produces an ElggCache object suitable for caching file load paths.
+ * @param string $class The name of the class
*
- * TODO: Can this be done in a cleaner way?
- * TODO: Swap to memcache etc?
+ * @return void
+ * @throws Exception
+ * @access private
*/
-function elgg_get_filepath_cache() {
+function _elgg_autoload($class) {
global $CONFIG;
- static $FILE_PATH_CACHE;
- if (!$FILE_PATH_CACHE) $FILE_PATH_CACHE = new ElggFileCache($CONFIG->dataroot);
- return $FILE_PATH_CACHE;
+ if (!isset($CONFIG->classes[$class]) || !include($CONFIG->classes[$class])) {
+ return false;
+ }
}
/**
- * Function which resets the file path cache.
+ * Register all files found in $dir as classes
+ * Need to be named MyClass.php
*
- */
-function elgg_filepath_cache_reset() {
- $cache = elgg_get_filepath_cache();
- return $cache->delete('view_paths');
-}
-
-/**
- * Saves a filepath cache.
+ * @param string $dir The dir to look in
*
- * @param mixed $data
+ * @return void
+ * @since 1.8.0
*/
-function elgg_filepath_cache_save($data) {
- global $CONFIG;
+function elgg_register_classes($dir) {
+ $classes = elgg_get_file_list($dir, array(), array(), array('.php'));
- if ($CONFIG->viewpath_cache_enabled) {
- $cache = elgg_get_filepath_cache();
- return $cache->save('view_paths', $data);
+ foreach ($classes as $class) {
+ elgg_register_class(basename($class, '.php'), $class);
}
-
- return false;
}
/**
- * Retrieve the contents of the filepath cache.
+ * Register a classname to a file.
*
+ * @param string $class The name of the class
+ * @param string $location The location of the file
+ *
+ * @return true
+ * @since 1.8.0
*/
-function elgg_filepath_cache_load() {
+function elgg_register_class($class, $location) {
global $CONFIG;
- if ($CONFIG->viewpath_cache_enabled) {
- $cache = elgg_get_filepath_cache();
- $cached_view_paths = $cache->load('view_paths');
-
- if ($cached_view_paths) {
- return $cached_view_paths;
- }
+ if (!isset($CONFIG->classes)) {
+ $CONFIG->classes = array();
}
- return NULL;
-}
+ $CONFIG->classes[$class] = $location;
-/**
- * Enable the filepath cache.
- *
- */
-function elgg_enable_filepath_cache() {
- global $CONFIG;
-
- datalist_set('viewpath_cache_enabled',1);
- $CONFIG->viewpath_cache_enabled = 1;
- elgg_filepath_cache_reset();
+ return true;
}
/**
- * Disable filepath cache.
+ * Register a php library.
*
- */
-function elgg_disable_filepath_cache() {
- global $CONFIG;
-
- datalist_set('viewpath_cache_enabled',0);
- $CONFIG->viewpath_cache_enabled = 0;
- elgg_filepath_cache_reset();
-}
-
-/**
- * Deprecated by elgg_add_submenu_item()
+ * @param string $name The name of the library
+ * @param string $location The location of the file
*
- * @see elgg_add_submenu_item()
- * @deprecated 1.8
+ * @return void
+ * @since 1.8.0
*/
-function add_submenu_item($label, $link, $group = 'default', $onclick = false, $selected = NULL) {
- elgg_deprecated_notice('add_submenu_item was deprecated by elgg_add_submenu_item', 1.8);
-
- $item = array(
- 'text' => $label,
- 'href' => $link,
- 'selected' => $selected
- );
-
- if (!$group) {
- $group = 'default';
- }
+function elgg_register_library($name, $location) {
+ global $CONFIG;
- if ($onclick) {
- $js = "onclick=\"javascript:return confirm('". elgg_echo('deleteconfirm') . "')\"";
- $item['vars'] = array('js' => $js);
+ if (!isset($CONFIG->libraries)) {
+ $CONFIG->libraries = array();
}
- // submenu items were added in the page setup hook usually by checking
- // the context. We'll pass in the current context here, which will
- // emulate that effect.
- // if context == 'main' (default) it probably means they always wanted
- // the menu item to show up everywhere.
- $context = get_context();
- if ($context == 'main') {
- $context = 'all';
- }
- return elgg_add_submenu_item($item, $context, $group);
+ $CONFIG->libraries[$name] = $location;
}
/**
- * Add an entry to the submenu.
+ * Load a php library.
*
- * @param array $item The item as array(
- * 'title' => 'Text to display',
- * 'url' => 'URL of the link',
- * 'id' => 'entry_unique_id' //used by children items to identify parents
- * 'parent_id' => 'id_of_parent',
- * 'selected' => BOOL // Is this item selected? (If NULL or unset will attempt to guess)
- * 'vars' => array() // Array of vars to pass to the navigation/submenu_item view
- * )
+ * @param string $name The name of the library
*
- * @param string $context Context in which to display this menu item. 'all' will make it show up all the time. Use sparingly.
- * @param string $group Group for the item. Each submenu group has its own <ul>
- * @return BOOL
- * @since 1.8
+ * @return void
+ * @throws InvalidParameterException
+ * @since 1.8.0
+ * @todo return boolean in 1.9 to indicate whether the library has been loaded
*/
-function elgg_add_submenu_item(array $item, $context = 'all', $group = 'default') {
+function elgg_load_library($name) {
global $CONFIG;
- if (!isset($CONFIG->submenu_items)) {
- $CONFIG->submenu_items = array();
- }
+ static $loaded_libraries = array();
- if (!isset($CONFIG->submenu_items[$context])) {
- $CONFIG->submenu_items[$context] = array();
+ if (in_array($name, $loaded_libraries)) {
+ return;
}
- if (!isset($CONFIG->submenu_items[$context][$group])) {
- $CONFIG->submenu_items[$context][$group] = array();
+ if (!isset($CONFIG->libraries)) {
+ $CONFIG->libraries = array();
}
- if (!isset($item['text'])) {
- return FALSE;
+ if (!isset($CONFIG->libraries[$name])) {
+ $error = elgg_echo('InvalidParameterException:LibraryNotRegistered', array($name));
+ throw new InvalidParameterException($error);
}
- // we use persistent object properties in the submenu
- // setup function, so normalize the array to an object.
- // we pass it in as an array because this would be the only
- // place in elgg that we ask for an object like this.
- // consistency ftw.
- $item_obj = new StdClass();
-
- foreach ($item as $k => $v) {
- switch ($k) {
- case 'parent_id':
- case 'id':
- // make sure '' and false make sense
- $v = (empty($v)) ? NULL : $v;
-
- default:
- $item_obj->$k = $v;
- break;
- }
+ if (!include_once($CONFIG->libraries[$name])) {
+ $error = elgg_echo('InvalidParameterException:LibraryNotFound', array(
+ $name,
+ $CONFIG->libraries[$name])
+ );
+ throw new InvalidParameterException($error);
}
- $CONFIG->submenu_items[$context][$group][] = $item_obj;
-
- return TRUE;
+ $loaded_libraries[] = $name;
}
/**
- * Properly nest all submenu entries for contexts $context and 'all'
+ * Forward to $location.
+ *
+ * Sends a 'Location: $location' header and exists. If headers have
+ * already been sent, returns FALSE.
+ *
+ * @param string $location URL to forward to browser to. Can be path relative to the network's URL.
+ * @param string $reason Short explanation for why we're forwarding
*
- * @param string $context
- * @param bool $sort Sort the menu items alphabetically
- * @since 1.8
+ * @return false False if headers have been sent. Terminates execution if forwarding.
+ * @throws SecurityException
*/
-function elgg_prepare_submenu($context = 'main', $sort = FALSE) {
- global $CONFIG;
-
- if (!isset($CONFIG->submenu_items) || !($CONFIG->submenu_items)) {
- return FALSE;
- }
-
- $groups = array();
-
- if (isset($CONFIG->submenu_items['all'])) {
- $groups = $CONFIG->submenu_items['all'];
- }
-
- if (isset($CONFIG->submenu_items[$context])) {
- $groups = array_merge_recursive($groups, $CONFIG->submenu_items[$context]);
- }
-
- if (!$groups) {
- return FALSE;
- }
-
- foreach ($groups as $group => $items) {
- if ($sort) {
- usort($items, 'elgg_submenu_item_cmp');
+function forward($location = "", $reason = 'system') {
+ if (!headers_sent($file, $line)) {
+ if ($location === REFERER) {
+ $location = $_SERVER['HTTP_REFERER'];
}
- $parsed_menu = array();
- // determin which children need to go in this item.
- foreach ($items as $i => $item) {
- // can only support children if there's an id
- if (isset($item->id)) {
- foreach ($items as $child_i => $child_item) {
- // don't check ourselves or used children.
- if ($child_i == $i || $child_item->used == TRUE) {
- continue;
- }
-
- if (isset($child_item->parent_id) && $child_item->parent_id == $item->id) {
- if (!isset($item->children)) {
- $item->children = array();
- }
- $item->children[] = $child_item;
- $child_item->parent = $item;
- // don't unset because we still need to check this item for children
- $child_item->used = TRUE;
- }
- }
+ $location = elgg_normalize_url($location);
- // if the parent doesn't have a url, make it the first child item.
- if (isset($item->children) && $item->children && !$item->href) {
- $child = $item->children[0];
- while ($child && !isset($child->href)) {
- if (isset($child->children) && isset($child->children[0])) {
- $child = $child->children[0];
- } else {
- $child = NULL;
- }
- }
-
- if ($child && isset($child->href)) {
- $item->href = $child->href;
- } else {
- // @todo There are no URLs anywhere in this tree.
- $item->href = $CONFIG->url;
- }
- }
- }
+ // return new forward location or false to stop the forward or empty string to exit
+ $current_page = current_page_url();
+ $params = array('current_url' => $current_page, 'forward_url' => $location);
+ $location = elgg_trigger_plugin_hook('forward', $reason, $params, $location);
- // only add top-level elements to the menu.
- // the rest are children.
- if (!isset($item->parent_id)) {
- $parsed_menu[] = $item;
- }
+ if ($location) {
+ header("Location: {$location}");
+ exit;
+ } else if ($location === '') {
+ exit;
}
-
- $CONFIG->submenu[$context][$group] = $parsed_menu;
+ } else {
+ throw new SecurityException(elgg_echo('SecurityException:ForwardFailedToRedirect', array($file, $line)));
}
-
- return TRUE;
}
/**
- * Helper function used to sort submenu items by their display text.
+ * Register a JavaScript file for inclusion
+ *
+ * This function handles adding JavaScript to a web page. If multiple
+ * calls are made to register the same JavaScript file based on the $id
+ * variable, only the last file is included. This allows a plugin to add
+ * JavaScript from a view that may be called more than once. It also handles
+ * more than one plugin adding the same JavaScript.
+ *
+ * jQuery plugins often have filenames such as jquery.rating.js. A best practice
+ * is to base $name on the filename: "jquery.rating". It is recommended to not
+ * use version numbers in the name.
*
- * @param object $a
- * @param object $b
- * @since 1.8
+ * The JavaScript files can be local to the server or remote (such as
+ * Google's CDN).
+ *
+ * @param string $name An identifier for the JavaScript library
+ * @param string $url URL of the JavaScript file
+ * @param string $location Page location: head or footer. (default: head)
+ * @param int $priority Priority of the JS file (lower numbers load earlier)
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function elgg_submenu_item_cmp($a, $b) {
- $a = $a->text;
- $b = $b->text;
-
- return strnatcmp($a, $b);
+function elgg_register_js($name, $url, $location = 'head', $priority = null) {
+ return elgg_register_external_file('js', $name, $url, $location, $priority);
}
/**
- * Use elgg_get_submenu().
+ * Unregister a JavaScript file
*
- * @see elgg_get_submenu()
- * @deprecated 1.8
+ * @param string $name The identifier for the JavaScript library
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function get_submenu() {
- elgg_deprecated_notice("get_submenu() has been deprecated by elgg_get_submenu()", 1.8);
- return elgg_get_submenu();
+function elgg_unregister_js($name) {
+ return elgg_unregister_external_file('js', $name);
}
/**
- * Return the HTML for a sidemenu.
+ * Load a JavaScript resource on this page
+ *
+ * This must be called before elgg_view_page(). It can be called before the
+ * script is registered. If you do not want a script loaded, unregister it.
*
- * @param string $context The context of the submenu (defaults to main)
- * @param BOOL $sort Sort by display name?
- * @return string Formatted HTML.
- * @since 1.8
+ * @param string $name Identifier of the JavaScript resource
+ *
+ * @return void
+ * @since 1.8.0
*/
-function elgg_get_submenu($context = NULL, $sort = FALSE) {
- global $CONFIG;
-
- if (!$context) {
- $context = get_context();
- }
-
- if (!elgg_prepare_submenu($context, $sort)) {
- return '';
- }
-
- $groups = $CONFIG->submenu[$context];
- $submenu_html = '';
-
- foreach ($groups as $group => $items) {
- // how far down we are in children arrays
- $depth = 0;
- // push and pop parent items
- $temp_items = array();
-
- while ($item = current($items)) {
- $t = '';
- // ignore parents created by a child but parent never defined properly
- if (!isset($item->text) || !($item->text)) {
- next($items);
- continue;
- }
-
- // try to guess if this should be selected if they don't specify
- if ((!isset($item->selected) || $item->selected === NULL) && isset($item->href)) {
- $item->selected = elgg_http_url_is_identical(full_url(), $item->href);
- }
-
- // traverse up the parent tree if matached to mark all parents as selected/expanded.
- if ($item->selected && isset($item->parent)) {
- $parent = $item->parent;
- while ($parent) {
- $parent->selected = TRUE;
- if (isset($parent->parent)) {
- $parent = $parent->parent;
- } else {
- $parent = NULL;
- }
- }
- }
-
- // get the next item
- if (isset($item->children) && $item->children) {
- $depth++;
- array_push($temp_items, $items);
- $items = $item->children;
- } elseif ($depth > 0) {
- // check if there are more children elements in the current items
- // pop back up to the parent(s) if not
- if ($item = next($items)) {
- continue;
- } else {
- while($depth > 0) {
- $depth--;
- $items = array_pop($temp_items);
- if ($item = next($items)) {
- break;
- }
- }
- }
- } else {
- next($items);
- }
- }
-
- $submenu_html .= elgg_view('navigation/submenu_group', array('group' => $group, 'items' => $items));
- }
-
- // include the JS for the expand menus too
- return elgg_view('navigation/submenu_js') . $submenu_html;
+function elgg_load_js($name) {
+ elgg_load_external_file('js', $name);
}
/**
- * Automatically views likes and a like input relating to the given entity
+ * Get the JavaScript URLs that are loaded
*
- * @param ElggEntity $entity The entity to like
- * @return string|false The HTML (etc) for the likes, or false on failure
- * @since 1.8
+ * @param string $location 'head' or 'footer'
+ *
+ * @return array
+ * @since 1.8.0
*/
-function elgg_view_likes($entity){
- if (!($entity instanceof ElggEntity)) {
- return false;
- }
- if ($likes = trigger_plugin_hook('likes', $entity->getType(), array('entity' => $entity), false)) {
- return $likes;
- } else {
- //display the form
- $likes = elgg_view('likes/forms/edit', array('entity' => $entity));
- return $likes;
- }
+function elgg_get_loaded_js($location = 'head') {
+ return elgg_get_loaded_external_files('js', $location);
}
/**
- * Count the number of likes attached to an entity
+ * Register a CSS file for inclusion in the HTML head
*
- * @param ElggEntity $entity
- * @return int Number of likes
- * @since 1.8
+ * @param string $name An identifier for the CSS file
+ * @param string $url URL of the CSS file
+ * @param int $priority Priority of the CSS file (lower numbers load earlier)
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function elgg_count_likes($entity) {
- if ($likeno = trigger_plugin_hook('likes:count', $entity->getType(),
- array('entity' => $entity), false)) {
- return $likeno;
- } else {
- return count_annotations($entity->getGUID(), "", "", "likes");
- }
+function elgg_register_css($name, $url, $priority = null) {
+ return elgg_register_external_file('css', $name, $url, 'head', $priority);
}
/**
- * Count the number of comments attached to an entity
+ * Unregister a CSS file
*
- * @param ElggEntity $entity
- * @return int Number of comments
+ * @param string $name The identifier for the CSS file
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function elgg_count_comments($entity) {
- if ($commentno = trigger_plugin_hook('comments:count', $entity->getType(),
- array('entity' => $entity), false)) {
- return $commentno;
- } else {
- return count_annotations($entity->getGUID(), "", "", "generic_comment");
- }
+function elgg_unregister_css($name) {
+ return elgg_unregister_external_file('css', $name);
}
-
-
/**
- * Library loading and handling
+ * Load a CSS file for this page
+ *
+ * This must be called before elgg_view_page(). It can be called before the
+ * CSS file is registered. If you do not want a CSS file loaded, unregister it.
+ *
+ * @param string $name Identifier of the CSS file
+ *
+ * @return void
+ * @since 1.8.0
*/
+function elgg_load_css($name) {
+ elgg_load_external_file('css', $name);
+}
/**
- * @deprecated 1.7
+ * Get the loaded CSS URLs
+ *
+ * @return array
+ * @since 1.8.0
*/
-function get_library_files($directory, $exceptions = array(), $list = array()) {
- elgg_deprecated_notice('get_library_files() deprecated by elgg_get_file_list()', 1.7);
- return elgg_get_file_list($directory, $exceptions, $list, array('.php'));
+function elgg_get_loaded_css() {
+ return elgg_get_loaded_external_files('css', 'head');
}
/**
- * Returns a list of files in $directory
+ * Core registration function for external files
*
- * @param str $directory
- * @param array $exceptions Array of filenames to ignore
- * @param array $list Array of files to append to
- * @param mixed $extensions Array of extensions to allow, NULL for all. (With a dot: array('.php'))
- * @return array of filenames including $directory
+ * @param string $type Type of external resource (js or css)
+ * @param string $name Identifier used as key
+ * @param string $url URL
+ * @param string $location Location in the page to include the file
+ * @param int $priority Loading priority of the file
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function elgg_get_file_list($directory, $exceptions = array(), $list = array(), $extensions = NULL) {
- $directory = sanitise_filepath($directory);
- if ($handle = opendir($directory)) {
- while (($file = readdir($handle)) !== FALSE) {
- if (!is_file($directory . $file) || in_array($file, $exceptions)) {
- continue;
- }
+function elgg_register_external_file($type, $name, $url, $location, $priority = 500) {
+ global $CONFIG;
- if (is_array($extensions)) {
- if (in_array(strrchr($file, '.'), $extensions)) {
- $list[] = $directory . $file;
- }
- } else {
- $list[] = $directory . $file;
- }
- }
- closedir($handle);
+ if (empty($name) || empty($url)) {
+ return false;
}
- return $list;
-}
+ $url = elgg_format_url($url);
+ $url = elgg_normalize_url($url);
+
+ elgg_bootstrap_externals_data_structure($type);
-/**
- * Ensures that the installation has all the correct files, that PHP is configured correctly, and so on.
- * Leaves appropriate messages in the error register if not.
- *
- * @return true|false True if everything is ok (or Elgg is fit enough to run); false if not.
- */
-function sanitised() {
- $sanitised = true;
+ $name = trim(strtolower($name));
- if (!file_exists(dirname(dirname(__FILE__)) . "/settings.php")) {
- // See if we are being asked to save the file
- $save_vars = get_input('db_install_vars');
- $result = "";
- if ($save_vars) {
- $rtn = db_check_settings($save_vars['CONFIG_DBUSER'],
- $save_vars['CONFIG_DBPASS'],
- $save_vars['CONFIG_DBNAME'],
- $save_vars['CONFIG_DBHOST'] );
- if ($rtn == FALSE) {
- register_error(elgg_view("messages/sanitisation/dbsettings_error"));
- register_error(elgg_view("messages/sanitisation/settings",
- array( 'settings.php' => $result,
- 'sticky' => $save_vars)));
- return FALSE;
- }
+ // normalize bogus priorities, but allow empty, null, and false to be defaults.
+ if (!is_numeric($priority)) {
+ $priority = 500;
+ }
- $result = create_settings($save_vars, dirname(dirname(__FILE__)) . "/settings.example.php");
+ // no negative priorities right now.
+ $priority = max((int)$priority, 0);
+ $item = elgg_extract($name, $CONFIG->externals_map[$type]);
- if (file_put_contents(dirname(dirname(__FILE__)) . "/settings.php", $result)) {
- // blank result to stop it being displayed in textarea
- $result = "";
- }
- }
+ if ($item) {
+ // updating a registered item
+ // don't update loaded because it could already be set
+ $item->url = $url;
+ $item->location = $location;
- // Recheck to see if the file is still missing
- if (!file_exists(dirname(dirname(__FILE__)) . "/settings.php")) {
- register_error(elgg_view("messages/sanitisation/settings", array('settings.php' => $result)));
- $sanitised = false;
+ // if loaded before registered, that means it hasn't been added to the list yet
+ if ($CONFIG->externals[$type]->contains($item)) {
+ $priority = $CONFIG->externals[$type]->move($item, $priority);
+ } else {
+ $priority = $CONFIG->externals[$type]->add($item, $priority);
}
- }
+ } else {
+ $item = new stdClass();
+ $item->loaded = false;
+ $item->url = $url;
+ $item->location = $location;
- if (!file_exists(dirname(dirname(dirname(__FILE__))) . "/.htaccess")) {
- if (!@copy(dirname(dirname(dirname(__FILE__))) . "/htaccess_dist", dirname(dirname(dirname(__FILE__))) . "/.htaccess")) {
- register_error(elgg_view("messages/sanitisation/htaccess", array('.htaccess' => file_get_contents(dirname(dirname(dirname(__FILE__))) . "/htaccess_dist"))));
- $sanitised = false;
- }
+ $priority = $CONFIG->externals[$type]->add($item, $priority);
}
- return $sanitised;
-}
+ $CONFIG->externals_map[$type][$name] = $item;
-/**
- * Registers
- */
+ return $priority !== false;
+}
/**
- * Adds an array with a name to a given generic array register.
- * For example, these are used for menus.
+ * Unregister an external file
+ *
+ * @param string $type Type of file: js or css
+ * @param string $name The identifier of the file
*
- * @param string $register_name The name of the top-level register
- * @param string $subregister_name The name of the subregister
- * @param mixed $subregister_value The value of the subregister
- * @param array $children_array Optionally, an array of children
- * @return true|false Depending on success
+ * @return bool
+ * @since 1.8.0
*/
-function add_to_register($register_name, $subregister_name, $subregister_value, $children_array = array()) {
+function elgg_unregister_external_file($type, $name) {
global $CONFIG;
- if (empty($register_name) || empty($subregister_name)) {
- return false;
- }
-
- if (!isset($CONFIG->registers)) {
- $CONFIG->registers = array();
- }
-
- if (!isset($CONFIG->registers[$register_name])) {
- $CONFIG->registers[$register_name] = array();
- }
+ elgg_bootstrap_externals_data_structure($type);
- $subregister = new stdClass;
- $subregister->name = $subregister_name;
- $subregister->value = $subregister_value;
+ $name = trim(strtolower($name));
+ $item = elgg_extract($name, $CONFIG->externals_map[$type]);
- if (is_array($children_array)) {
- $subregister->children = $children_array;
+ if ($item) {
+ unset($CONFIG->externals_map[$type][$name]);
+ return $CONFIG->externals[$type]->remove($item);
}
- $CONFIG->registers[$register_name][$subregister_name] = $subregister;
- return true;
+ return false;
}
/**
- * Removes what has been registered at [register_name][subregister_name]
+ * Load an external resource for use on this page
*
- * @param string $register_name The name of the top-level register
- * @param string $subregister_name The name of the subregister
- * @return true|false Depending on success
+ * @param string $type Type of file: js or css
+ * @param string $name The identifier for the file
+ *
+ * @return void
+ * @since 1.8.0
*/
-function remove_from_register($register_name, $subregister_name) {
+function elgg_load_external_file($type, $name) {
global $CONFIG;
- if (empty($register_name) || empty($subregister_name)) {
- return false;
- }
-
- if (!isset($CONFIG->registers)) {
- return false;
- }
+ elgg_bootstrap_externals_data_structure($type);
- if (!isset($CONFIG->registers[$register_name])) {
- return false;
- }
+ $name = trim(strtolower($name));
- if (isset($CONFIG->registers[$register_name][$subregister_name])) {
- unset($CONFIG->registers[$register_name][$subregister_name]);
- return true;
- }
+ $item = elgg_extract($name, $CONFIG->externals_map[$type]);
- return false;
-}
+ if ($item) {
+ // update a registered item
+ $item->loaded = true;
+ } else {
+ $item = new stdClass();
+ $item->loaded = true;
+ $item->url = '';
+ $item->location = '';
-/**
- * Returns a register object
- *
- * @param string $register_name The name of the register
- * @param mixed $register_value The value of the register
- * @param array $children_array Optionally, an array of children
- * @return false|stdClass Depending on success
- */
-function make_register_object($register_name, $register_value, $children_array = array()) {
- elgg_deprecated_notice('make_register_object() is deprecated by add_submenu_item()', 1.7);
- if (empty($register_name) || empty($register_value)) {
- return false;
+ $CONFIG->externals[$type]->add($item);
+ $CONFIG->externals_map[$type][$name] = $item;
}
-
- $register = new stdClass;
- $register->name = $register_name;
- $register->value = $register_value;
- $register->children = $children_array;
-
- return $register;
}
/**
- * If it exists, returns a particular register as an array
+ * Get external resource descriptors
+ *
+ * @param string $type Type of file: js or css
+ * @param string $location Page location
*
- * @param string $register_name The name of the register
- * @return array|false Depending on success
+ * @return array
+ * @since 1.8.0
*/
-function get_register($register_name) {
+function elgg_get_loaded_external_files($type, $location) {
global $CONFIG;
- if (isset($CONFIG->registers[$register_name])) {
- return $CONFIG->registers[$register_name];
- }
+ if (isset($CONFIG->externals) && $CONFIG->externals[$type] instanceof ElggPriorityList) {
+ $items = $CONFIG->externals[$type]->getElements();
- return false;
+ $callback = "return \$v->loaded == true && \$v->location == '$location';";
+ $items = array_filter($items, create_function('$v', $callback));
+ if ($items) {
+ array_walk($items, create_function('&$v,$k', '$v = $v->url;'));
+ }
+ return $items;
+ }
+ return array();
}
/**
- * Adds an item to the menu register
- * This is used in the core to create the tools dropdown menu
- * You can obtain the menu array by calling get_register('menu')
+ * Bootstraps the externals data structure in $CONFIG.
*
- * @param string $menu_name The name of the menu item
- * @param string $menu_url The URL of the page
- * @param array $menu_children Optionally, an array of submenu items (not currently used)
- * @param string $context
- * @return true|false Depending on success
+ * @param string $type The type of external, js or css.
+ * @access private
*/
-function add_menu($menu_name, $menu_url, $menu_children = array(), $context = "") {
+function elgg_bootstrap_externals_data_structure($type) {
global $CONFIG;
- if (!isset($CONFIG->menucontexts)) {
- $CONFIG->menucontexts = array();
+ if (!isset($CONFIG->externals)) {
+ $CONFIG->externals = array();
}
- if (empty($context)) {
- $context = get_plugin_name();
+ if (!isset($CONFIG->externals[$type]) || !$CONFIG->externals[$type] instanceof ElggPriorityList) {
+ $CONFIG->externals[$type] = new ElggPriorityList();
}
- $value = new stdClass();
- $value->url = $menu_url;
- $value->context = $context;
+ if (!isset($CONFIG->externals_map)) {
+ $CONFIG->externals_map = array();
+ }
- $CONFIG->menucontexts[] = $context;
- return add_to_register('menu', $menu_name, $value, $menu_children);
+ if (!isset($CONFIG->externals_map[$type])) {
+ $CONFIG->externals_map[$type] = array();
+ }
}
/**
- * Removes an item from the menu register
+ * Returns a list of files in $directory.
+ *
+ * Only returns files. Does not recurse into subdirs.
+ *
+ * @param string $directory Directory to look in
+ * @param array $exceptions Array of filenames to ignore
+ * @param array $list Array of files to append to
+ * @param mixed $extensions Array of extensions to allow, NULL for all. Use a dot: array('.php').
*
- * @param string $menu_name The name of the menu item
- * @return true|false Depending on success
+ * @return array Filenames in $directory, in the form $directory/filename.
*/
-function remove_menu($menu_name) {
- return remove_from_register('menu', $menu_name);
+function elgg_get_file_list($directory, $exceptions = array(), $list = array(),
+$extensions = NULL) {
+
+ $directory = sanitise_filepath($directory);
+ if ($handle = opendir($directory)) {
+ while (($file = readdir($handle)) !== FALSE) {
+ if (!is_file($directory . $file) || in_array($file, $exceptions)) {
+ continue;
+ }
+
+ if (is_array($extensions)) {
+ if (in_array(strrchr($file, '.'), $extensions)) {
+ $list[] = $directory . $file;
+ }
+ } else {
+ $list[] = $directory . $file;
+ }
+ }
+ closedir($handle);
+ }
+
+ return $list;
}
/**
- * Returns a menu item for use in the children section of add_menu()
- * This is not currently used in the Elgg core
+ * Sanitise file paths ensuring that they begin and end with slashes etc.
*
- * @param string $menu_name The name of the menu item
- * @param string $menu_url Its URL
- * @return stdClass|false Depending on success
+ * @param string $path The path
+ * @param bool $append_slash Add tailing slash
+ *
+ * @return string
*/
-function menu_item($menu_name, $menu_url) {
- elgg_deprecated_notice('menu_item() is deprecated by add_submenu_item', 1.7);
- return make_register_object($menu_name, $menu_url);
+function sanitise_filepath($path, $append_slash = TRUE) {
+ // Convert to correct UNIX paths
+ $path = str_replace('\\', '/', $path);
+ $path = str_replace('../', '/', $path);
+ // replace // with / except when preceeded by :
+ $path = preg_replace("/([^:])\/\//", "$1/", $path);
+
+ // Sort trailing slash
+ $path = trim($path);
+ // rtrim defaults plus /
+ $path = rtrim($path, " \n\t\0\x0B/");
+
+ if ($append_slash) {
+ $path = $path . '/';
+ }
+
+ return $path;
}
/**
- * Message register handling
- * If a null $message parameter is given, the function returns the array of messages so far and empties it
- * based on the $register parameters. Otherwise, any message or array of messages is added.
+ * Queues a message to be displayed.
+ *
+ * Messages will not be displayed immediately, but are stored in
+ * for later display, usually upon next page load.
+ *
+ * The method of displaying these messages differs depending upon plugins and
+ * viewtypes. The core default viewtype retrieves messages in
+ * {@link views/default/page/shells/default.php} and displays messages as
+ * javascript popups.
+ *
+ * @internal Messages are stored as strings in the $_SESSION['msg'][$register] array.
+ *
+ * @warning This function is used to both add to and clear the message
+ * stack. If $messages is null, $register will be returned and cleared.
+ * If $messages is null and $register is empty, all messages will be
+ * returned and removed.
+ *
+ * @important This function handles the standard {@link system_message()} ($register =
+ * 'messages') as well as {@link register_error()} messages ($register = 'errors').
+ *
+ * @param mixed $message Optionally, a single message or array of messages to add, (default: null)
+ * @param string $register Types of message: "error", "success" (default: success)
+ * @param bool $count Count the number of messages (default: false)
*
- * @param string|array $message Optionally, a single message or array of messages to add, (default: null)
- * @param string $register This allows for different types of messages: "errors", "messages" (default: messages)
- * @param bool $count Count the number of messages (default: false)
- * @return true|false|array Either the array of messages, or a response regarding whether the message addition was successful
+ * @return bool|array Either the array of messages, or a response regarding
+ * whether the message addition was successful.
+ * @todo Clean up. Separate registering messages and retrieving them.
*/
-function system_messages($message = null, $register = "messages", $count = false) {
+function system_messages($message = null, $register = "success", $count = false) {
if (!isset($_SESSION['msg'])) {
$_SESSION['msg'] = array();
}
@@ -809,7 +570,7 @@ function system_messages($message = null, $register = "messages", $count = false
return sizeof($_SESSION['msg'][$register]);
} else {
$count = 0;
- foreach($_SESSION['msg'] as $register => $submessages) {
+ foreach ($_SESSION['msg'] as $submessages) {
$count += sizeof($submessages);
}
return $count;
@@ -822,287 +583,414 @@ function system_messages($message = null, $register = "messages", $count = false
* Counts the number of messages, either globally or in a particular register
*
* @param string $register Optionally, the register
+ *
* @return integer The number of messages
*/
function count_messages($register = "") {
- return system_messages(null,$register,true);
+ return system_messages(null, $register, true);
}
/**
- * An alias for system_messages($message) to handle standard user information messages
+ * Display a system message on next page load.
+ *
+ * @see system_messages()
*
* @param string|array $message Message or messages to add
- * @return true|false Success response
+ *
+ * @return bool
*/
function system_message($message) {
- return system_messages($message, "messages");
+ return system_messages($message, "success");
}
/**
- * An alias for system_messages($message) to handle error messages
+ * Display an error on next page load.
+ *
+ * @see system_messages()
*
- * @param string|array $message Error or errors to add
- * @return true|false Success response
+ * @param string|array $error Error or errors to add
+ *
+ * @return bool
*/
function register_error($error) {
- return system_messages($error, "errors");
+ return system_messages($error, "error");
}
/**
- * Event register
- * Adds functions to the register for a particular event, but also calls all functions registered to an event when required
+ * Register a callback as an Elgg event handler.
*
- * Event handler functions must be of the form:
+ * Events are emitted by Elgg when certain actions occur. Plugins
+ * can respond to these events or halt them completely by registering a handler
+ * as a callback to an event. Multiple handlers can be registered for
+ * the same event and will be executed in order of $priority. Any handler
+ * returning false will halt the execution chain.
*
- * event_handler_function($event, $object_type, $object);
+ * This function is called with the event name, event type, and handler callback name.
+ * Setting the optional $priority allows plugin authors to specify when the
+ * callback should be run. Priorities for plugins should be 1-1000.
*
- * And must return true or false depending on success. A false will halt the event in its tracks and no more functions will be called.
+ * The callback is passed 3 arguments when called: $event, $type, and optional $params.
*
- * You can then simply register them using the following function. Optionally, this can be called with a priority nominally from 0 to 1000, where functions with lower priority values are called first (note that priorities CANNOT be negative):
+ * $event is the name of event being emitted.
+ * $type is the type of event or object concerned.
+ * $params is an optional parameter passed that can include a related object. See
+ * specific event documentation for details on which events pass what parameteres.
*
- * register_elgg_event_handler($event, $object_type, $function_name [, $priority = 500]);
+ * @tip If a priority isn't specified it is determined by the order the handler was
+ * registered relative to the event and type. For plugins, this generally means
+ * the earlier the plugin is in the load order, the earlier the priorities are for
+ * any event handlers.
*
- * Note that you can also use 'all' in place of both the event and object type.
+ * @tip $event and $object_type can use the special keyword 'all'. Handler callbacks registered
+ * with $event = all will be called for all events of type $object_type. Similarly,
+ * callbacks registered with $object_type = all will be called for all events of type
+ * $event, regardless of $object_type. If $event and $object_type both are 'all', the
+ * handler callback will be called for all events.
*
- * To trigger an event properly, you should always use:
+ * @tip Event handler callbacks are considered in the follow order:
+ * - Specific registration where 'all' isn't used.
+ * - Registration where 'all' is used for $event only.
+ * - Registration where 'all' is used for $type only.
+ * - Registration where 'all' is used for both.
*
- * trigger_elgg_event($event, $object_type [, $object]);
+ * @warning If you use the 'all' keyword, you must have logic in the handler callback to
+ * test the passed parameters before taking an action.
*
- * Where $object is optional, and represents the $object_type the event concerns. This will return true if successful, or false if it fails.
+ * @tip When referring to events, the preferred syntax is "event, type".
*
- * @param string $event The type of event (eg 'init', 'update', 'delete')
- * @param string $object_type The type of object (eg 'system', 'blog', 'user')
- * @param string $function The name of the function that will handle the event
- * @param int $priority A priority to add new event handlers at. Lower numbers will be called first (default 500)
- * @param boolean $call Set to true to call the event rather than add to it (default false)
- * @param mixed $object Optionally, the object the event is being performed on (eg a user)
- * @return true|false Depending on success
+ * @internal Events are stored in $CONFIG->events as:
+ * <code>
+ * $CONFIG->events[$event][$type][$priority] = $callback;
+ * </code>
+ *
+ * @param string $event The event type
+ * @param string $object_type The object type
+ * @param string $callback The handler callback
+ * @param int $priority The priority - 0 is default, negative before, positive after
+ *
+ * @return bool
+ * @link http://docs.elgg.org/Tutorials/Plugins/Events
+ * @example events/basic.php Basic example of registering an event handler callback.
+ * @example events/advanced.php Advanced example of registering an event handler
+ * callback and halting execution.
+ * @example events/all.php Example of how to use the 'all' keyword.
*/
-function events($event = "", $object_type = "", $function = "", $priority = 500, $call = false, $object = null) {
+function elgg_register_event_handler($event, $object_type, $callback, $priority = 500) {
global $CONFIG;
+ if (empty($event) || empty($object_type)) {
+ return false;
+ }
+
if (!isset($CONFIG->events)) {
$CONFIG->events = array();
- } else if (!isset($CONFIG->events[$event]) && !empty($event)) {
+ }
+ if (!isset($CONFIG->events[$event])) {
$CONFIG->events[$event] = array();
- } else if (!isset($CONFIG->events[$event][$object_type]) && !empty($event) && !empty($object_type)) {
+ }
+ if (!isset($CONFIG->events[$event][$object_type])) {
$CONFIG->events[$event][$object_type] = array();
}
- if (!$call) {
- if (!empty($event) && !empty($object_type) && is_callable($function)) {
- $priority = (int) $priority;
- if ($priority < 0) {
- $priority = 0;
- }
- while (isset($CONFIG->events[$event][$object_type][$priority])) {
- $priority++;
- }
- $CONFIG->events[$event][$object_type][$priority] = $function;
- ksort($CONFIG->events[$event][$object_type]);
- return true;
- } else {
- return false;
- }
- } else {
- $return = true;
- if (!empty($CONFIG->events[$event][$object_type]) && is_array($CONFIG->events[$event][$object_type])) {
- foreach($CONFIG->events[$event][$object_type] as $eventfunction) {
- if ($eventfunction($event, $object_type, $object) === false) {
- return false;
- }
- }
- }
-
- if (!empty($CONFIG->events['all'][$object_type]) && is_array($CONFIG->events['all'][$object_type])) {
- foreach($CONFIG->events['all'][$object_type] as $eventfunction) {
- if ($eventfunction($event, $object_type, $object) === false) {
- return false;
- }
- }
- }
-
- if (!empty($CONFIG->events[$event]['all']) && is_array($CONFIG->events[$event]['all'])) {
- foreach($CONFIG->events[$event]['all'] as $eventfunction) {
- if ($eventfunction($event, $object_type, $object) === false) {
- return false;
- }
- }
- }
-
- if (!empty($CONFIG->events['all']['all']) && is_array($CONFIG->events['all']['all'])) {
- foreach($CONFIG->events['all']['all'] as $eventfunction) {
- if ($eventfunction($event, $object_type, $object) === false) {
- return false;
- }
- }
- }
+ if (!is_callable($callback, true)) {
+ return false;
+ }
- return $return;
+ $priority = max((int) $priority, 0);
+ while (isset($CONFIG->events[$event][$object_type][$priority])) {
+ $priority++;
}
-
- return false;
+ $CONFIG->events[$event][$object_type][$priority] = $callback;
+ ksort($CONFIG->events[$event][$object_type]);
+ return true;
}
/**
- * Alias function for events, that registers a function to a particular kind of event
+ * Unregisters a callback for an event.
*
- * @param string $event The event type
+ * @param string $event The event type
* @param string $object_type The object type
- * @param string $function The function name
- * @return true|false Depending on success
- */
-function register_elgg_event_handler($event, $object_type, $function, $priority = 500) {
- return events($event, $object_type, $function, $priority);
-}
-
-/**
- * Unregisters a function to a particular kind of event
+ * @param string $callback The callback
*
- * @param string $event The event type
- * @param string $object_type The object type
- * @param string $function The function name
+ * @return void
+ * @since 1.7
*/
-function unregister_elgg_event_handler($event, $object_type, $function) {
+function elgg_unregister_event_handler($event, $object_type, $callback) {
global $CONFIG;
- foreach($CONFIG->events[$event][$object_type] as $key => $event_function) {
- if ($event_function == $function) {
- unset($CONFIG->events[$event][$object_type][$key]);
+
+ if (isset($CONFIG->events[$event]) && isset($CONFIG->events[$event][$object_type])) {
+ foreach ($CONFIG->events[$event][$object_type] as $key => $event_callback) {
+ if ($event_callback == $callback) {
+ unset($CONFIG->events[$event][$object_type][$key]);
+ }
}
}
}
/**
- * Alias function for events, that triggers a particular kind of event
+ * Trigger an Elgg Event and run all handler callbacks registered to that event, type.
+ *
+ * This function runs all handlers registered to $event, $object_type or
+ * the special keyword 'all' for either or both.
+ *
+ * $event is usually a verb: create, update, delete, annotation.
+ *
+ * $object_type is usually a noun: object, group, user, annotation, relationship, metadata.
+ *
+ * $object is usually an Elgg* object assciated with the event.
*
- * @param string $event The event type
+ * @warning Elgg events should only be triggered by core. Plugin authors should use
+ * {@link trigger_elgg_plugin_hook()} instead.
+ *
+ * @tip When referring to events, the preferred syntax is "event, type".
+ *
+ * @internal Only rarely should events be changed, added, or removed in core.
+ * When making changes to events, be sure to first create a ticket on Github.
+ *
+ * @internal @tip Think of $object_type as the primary namespace element, and
+ * $event as the secondary namespace.
+ *
+ * @param string $event The event type
* @param string $object_type The object type
- * @param string $function The function name
- * @return true|false Depending on success
+ * @param string $object The object involved in the event
+ *
+ * @return bool The result of running all handler callbacks.
+ * @link http://docs.elgg.org/Tutorials/Core/Events
+ * @internal @example events/emit.php Basic emitting of an Elgg event.
*/
-function trigger_elgg_event($event, $object_type, $object = null) {
- $return = true;
- $return1 = events($event, $object_type, "", null, true, $object);
- if (!is_null($return1)) {
- $return = $return1;
+function elgg_trigger_event($event, $object_type, $object = null) {
+ global $CONFIG;
+
+ $events = array();
+ if (isset($CONFIG->events[$event][$object_type])) {
+ $events[] = $CONFIG->events[$event][$object_type];
}
- return $return;
+ if (isset($CONFIG->events['all'][$object_type])) {
+ $events[] = $CONFIG->events['all'][$object_type];
+ }
+ if (isset($CONFIG->events[$event]['all'])) {
+ $events[] = $CONFIG->events[$event]['all'];
+ }
+ if (isset($CONFIG->events['all']['all'])) {
+ $events[] = $CONFIG->events['all']['all'];
+ }
+
+ $args = array($event, $object_type, $object);
+
+ foreach ($events as $callback_list) {
+ if (is_array($callback_list)) {
+ foreach ($callback_list as $callback) {
+ if (is_callable($callback) && (call_user_func_array($callback, $args) === false)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
}
/**
- * Register a function to a plugin hook for a particular hook name and type, with a given priority.
+ * Register a callback as a plugin hook handler.
+ *
+ * Plugin hooks allow developers to losely couple plugins and features by
+ * repsonding to and emitting {@link elgg_trigger_plugin_hook()} customizable hooks.
+ * Handler callbacks can respond to the hook, change the details of the hook, or
+ * ignore it.
+ *
+ * Multiple handlers can be registered for a plugin hook, and each callback
+ * is called in order of priority. If the return value of a handler is not
+ * null, that value is passed to the next callback in the call stack. When all
+ * callbacks have been run, the final value is passed back to the caller
+ * via {@link elgg_trigger_plugin_hook()}.
+ *
+ * Similar to Elgg Events, plugin hook handler callbacks are registered by passing
+ * a hook, a type, and a priority.
+ *
+ * The callback is passed 4 arguments when called: $hook, $type, $value, and $params.
+ *
+ * - str $hook The name of the hook.
+ * - str $type The type of hook.
+ * - mixed $value The return value of the last handler or the default
+ * value if no other handlers have been called.
+ * - mixed $params An optional array of parameters. Used to provide additional
+ * information to plugins.
*
- * eg if you want the function "export_user" to be called when the hook "export" for "user" entities
- * is run, use:
+ * @internal Plugin hooks are stored in $CONFIG->hooks as:
+ * <code>
+ * $CONFIG->hooks[$hook][$type][$priority] = $callback;
+ * </code>
*
- * register_plugin_hook("export", "user", "export_user");
+ * @tip Plugin hooks are similar to Elgg Events in that Elgg emits
+ * a plugin hook when certain actions occur, but a plugin hook allows you to alter the
+ * parameters, as well as halt execution.
*
- * "all" is a valid value for both $hook and $entity_type. "none" is a valid value for $entity_type.
+ * @tip If a priority isn't specified it is determined by the order the handler was
+ * registered relative to the event and type. For plugins, this generally means
+ * the earlier the plugin is in the load order, the earlier the priorities are for
+ * any event handlers.
*
- * The export_user function would then be defined as:
+ * @tip Like Elgg Events, $hook and $type can use the special keyword 'all'.
+ * Handler callbacks registered with $hook = all will be called for all hooks
+ * of type $type. Similarly, handlers registered with $type = all will be
+ * called for all hooks of type $event, regardless of $object_type. If $hook
+ * and $type both are 'all', the handler will be called for all hooks.
*
- * function export_user($hook, $entity_type, $returnvalue, $params);
+ * @tip Plugin hooks are sometimes used to gather lists from plugins. This is
+ * usually done by pushing elements into an array passed in $params. Be sure
+ * to append to and then return $value so you don't overwrite other plugin's
+ * values.
*
- * Where $returnvalue is the return value returned by the last function returned by the hook, and
- * $params is an array containing a set of parameters (or nothing).
+ * @warning Unlike Elgg Events, a handler that returns false will NOT halt the
+ * execution chain.
*
- * @param string $hook The name of the hook
- * @param string $type The type of the hook (NB Can be an ElggEntity type [user, object, group, site] or custom-defined 'get_sections')
- * @param string $function The name of a valid function to be run
- * @param string $priority The priority - 0 is first, 1000 last, default is 500
- * @return true|false Depending on success
+ * @param string $hook The name of the hook
+ * @param string $type The type of the hook
+ * @param callable $callback The name of a valid function or an array with object and method
+ * @param int $priority The priority - 500 is default, lower numbers called first
+ *
+ * @return bool
+ *
+ * @example hooks/register/basic.php Registering for a plugin hook and examining the variables.
+ * @example hooks/register/advanced.php Registering for a plugin hook and changing the params.
+ * @link http://docs.elgg.org/Tutorials/Plugins/Hooks
+ * @since 1.8.0
*/
-function register_plugin_hook($hook, $type, $function, $priority = 500) {
+function elgg_register_plugin_hook_handler($hook, $type, $callback, $priority = 500) {
global $CONFIG;
+ if (empty($hook) || empty($type)) {
+ return false;
+ }
+
if (!isset($CONFIG->hooks)) {
$CONFIG->hooks = array();
- } else if (!isset($CONFIG->hooks[$hook]) && !empty($hook)) {
+ }
+ if (!isset($CONFIG->hooks[$hook])) {
$CONFIG->hooks[$hook] = array();
- } else if (!isset($CONFIG->hooks[$hook][$type]) && !empty($type)) {
+ }
+ if (!isset($CONFIG->hooks[$hook][$type])) {
$CONFIG->hooks[$hook][$type] = array();
}
- if (!empty($hook) && !empty($type) && is_callable($function)) {
- $priority = (int) $priority;
- if ($priority < 0) {
- $priority = 0;
- }
- while (isset($CONFIG->hooks[$hook][$type][$priority])) {
- $priority++;
- }
- $CONFIG->hooks[$hook][$type][$priority] = $function;
- ksort($CONFIG->hooks[$hook][$type]);
- return true;
- } else {
+ if (!is_callable($callback, true)) {
return false;
}
+
+ $priority = max((int) $priority, 0);
+
+ while (isset($CONFIG->hooks[$hook][$type][$priority])) {
+ $priority++;
+ }
+ $CONFIG->hooks[$hook][$type][$priority] = $callback;
+ ksort($CONFIG->hooks[$hook][$type]);
+ return true;
}
/**
- * Unregister a function to a plugin hook for a particular entity type
+ * Unregister a callback as a plugin hook.
+ *
+ * @param string $hook The name of the hook
+ * @param string $entity_type The name of the type of entity (eg "user", "object" etc)
+ * @param callable $callback The PHP callback to be removed
*
- * @param string $hook The name of the hook
- * @param string $entity_type The name of the type of entity (eg "user", "object" etc)
- * @param string $function The name of a valid function to be run
+ * @return void
+ * @since 1.8.0
*/
-function unregister_plugin_hook($hook, $entity_type, $function) {
+function elgg_unregister_plugin_hook_handler($hook, $entity_type, $callback) {
global $CONFIG;
- foreach($CONFIG->hooks[$hook][$entity_type] as $key => $hook_function) {
- if ($hook_function == $function) {
- unset($CONFIG->hooks[$hook][$entity_type][$key]);
+
+ if (isset($CONFIG->hooks[$hook]) && isset($CONFIG->hooks[$hook][$entity_type])) {
+ foreach ($CONFIG->hooks[$hook][$entity_type] as $key => $hook_callback) {
+ if ($hook_callback == $callback) {
+ unset($CONFIG->hooks[$hook][$entity_type][$key]);
+ }
}
}
}
/**
- * Triggers a plugin hook, with various parameters as an array. For example, to provide
- * a 'foo' hook that concerns the type 'bar', with a parameter called 'param1'
- * with value 'value1', that by default returns true, you'd call:
+ * Trigger a Plugin Hook and run all handler callbacks registered to that hook:type.
+ *
+ * This function runs all handlers regsitered to $hook, $type or
+ * the special keyword 'all' for either or both.
+ *
+ * Use $params to send additional information to the handler callbacks.
+ *
+ * $returnvalue Is the initial value to pass to the handlers, which can
+ * then change it. It is useful to use $returnvalue to set defaults.
+ * If no handlers are registered, $returnvalue is immediately returned.
+ *
+ * $hook is usually a verb: import, get_views, output.
+ *
+ * $type is usually a noun: user, ecml, page.
+ *
+ * @tip Like Elgg Events, $hook and $type can use the special keyword 'all'.
+ * Handler callbacks registered with $hook = all will be called for all hooks
+ * of type $type. Similarly, handlers registered with $type = all will be
+ * called for all hooks of type $event, regardless of $object_type. If $hook
+ * and $type both are 'all', the handler will be called for all hooks.
*
- * trigger_plugin_hook('foo', 'bar', array('param1' => 'value1'), true);
+ * @internal The checks for $hook and/or $type not being equal to 'all' is to
+ * prevent a plugin hook being registered with an 'all' being called more than
+ * once if the trigger occurs with an 'all'. An example in core of this is in
+ * actions.php:
+ * elgg_trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', ...)
*
- * @see register_plugin_hook
- * @param string $hook The name of the hook to trigger (NB: "all" will trigger for all $types regardless of $hook value)
- * @param string $type The type of the hook to trigger (NB: "all" will trigger for all $hooks regardless of $type value)
- * @param array $params Any parameters. It's good practice to name the keys, i.e. by using array('name' => 'value', 'name2' => 'value2')
- * @param mixed $returnvalue An initial return value
- * @return mixed|null The cumulative return value for the plugin hook functions
+ * @see elgg_register_plugin_hook_handler()
+ *
+ * @param string $hook The name of the hook to trigger ("all" will
+ * trigger for all $types regardless of $hook value)
+ * @param string $type The type of the hook to trigger ("all" will
+ * trigger for all $hooks regardless of $type value)
+ * @param mixed $params Additional parameters to pass to the handlers
+ * @param mixed $returnvalue An initial return value
+ *
+ * @return mixed|null The return value of the last handler callback called
+ *
+ * @example hooks/trigger/basic.php Trigger a hook that determins if execution
+ * should continue.
+ * @example hooks/trigger/advanced.php Trigger a hook with a default value and use
+ * the results to populate a menu.
+ * @example hooks/basic.php Trigger and respond to a basic plugin hook.
+ * @link http://docs.elgg.org/Tutorials/Plugins/Hooks
+ *
+ * @since 1.8.0
*/
-function trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null) {
+function elgg_trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null) {
global $CONFIG;
- if (!empty($CONFIG->hooks[$hook][$type]) && is_array($CONFIG->hooks[$hook][$type])) {
- foreach($CONFIG->hooks[$hook][$type] as $hookfunction) {
- $temp_return_value = $hookfunction($hook, $type, $returnvalue, $params);
- if (!is_null($temp_return_value)) {
- $returnvalue = $temp_return_value;
- }
+ $hooks = array();
+ if (isset($CONFIG->hooks[$hook][$type])) {
+ if ($hook != 'all' && $type != 'all') {
+ $hooks[] = $CONFIG->hooks[$hook][$type];
}
}
-
- if (!empty($CONFIG->hooks['all'][$type]) && is_array($CONFIG->hooks['all'][$type])) {
- foreach($CONFIG->hooks['all'][$type] as $hookfunction) {
- $temp_return_value = $hookfunction($hook, $type, $returnvalue, $params);
- if (!is_null($temp_return_value)) $returnvalue = $temp_return_value;
+ if (isset($CONFIG->hooks['all'][$type])) {
+ if ($type != 'all') {
+ $hooks[] = $CONFIG->hooks['all'][$type];
}
}
-
- if (!empty($CONFIG->hooks[$hook]['all']) && is_array($CONFIG->hooks[$hook]['all'])) {
- foreach($CONFIG->hooks[$hook]['all'] as $hookfunction) {
- $temp_return_value = $hookfunction($hook, $type, $returnvalue, $params);
- if (!is_null($temp_return_value)) {
- $returnvalue = $temp_return_value;
- }
+ if (isset($CONFIG->hooks[$hook]['all'])) {
+ if ($hook != 'all') {
+ $hooks[] = $CONFIG->hooks[$hook]['all'];
}
}
+ if (isset($CONFIG->hooks['all']['all'])) {
+ $hooks[] = $CONFIG->hooks['all']['all'];
+ }
- if (!empty($CONFIG->hooks['all']['all']) && is_array($CONFIG->hooks['all']['all'])) {
- foreach($CONFIG->hooks['all']['all'] as $hookfunction) {
- $temp_return_value = $hookfunction($hook, $type, $returnvalue, $params);
- if (!is_null($temp_return_value)) {
- $returnvalue = $temp_return_value;
+ foreach ($hooks as $callback_list) {
+ if (is_array($callback_list)) {
+ foreach ($callback_list as $hookcallback) {
+ if (is_callable($hookcallback)) {
+ $args = array($hook, $type, $returnvalue, $params);
+ $temp_return_value = call_user_func_array($hookcallback, $args);
+ if (!is_null($temp_return_value)) {
+ $returnvalue = $temp_return_value;
+ }
+ }
}
}
}
@@ -1111,26 +999,85 @@ function trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null)
}
/**
- * Error handling
+ * Intercepts, logs, and displays uncaught exceptions.
+ *
+ * @warning This function should never be called directly.
+ *
+ * @see http://www.php.net/set-exception-handler
+ *
+ * @param Exception $exception The exception being handled
+ *
+ * @return void
+ * @access private
*/
+function _elgg_php_exception_handler($exception) {
+ $timestamp = time();
+ error_log("Exception #$timestamp: $exception");
+
+ // Wipe any existing output buffer
+ ob_end_clean();
+
+ // make sure the error isn't cached
+ header("Cache-Control: no-cache, must-revalidate", true);
+ header('Expires: Fri, 05 Feb 1982 00:00:00 -0500', true);
+ // @note Do not send a 500 header because it is not a server error
+
+ try {
+ // we don't want the 'pagesetup', 'system' event to fire
+ global $CONFIG;
+ $CONFIG->pagesetupdone = true;
+
+ elgg_set_viewtype('failsafe');
+ 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();
+ $message = $e->getMessage();
+ echo "Fatal error in exception handler. Check log for Exception #$timestamp";
+ error_log("Exception #$timestamp : fatal error in exception handler : $message");
+ }
+}
/**
- * PHP Error handler function.
- * This function acts as a wrapper to catch and report PHP error messages.
+ * Intercepts catchable PHP errors.
+ *
+ * @warning This function should never be called directly.
+ *
+ * @internal
+ * For catchable fatal errors, throws an Exception with the error.
+ *
+ * For non-fatal errors, depending upon the debug settings, either
+ * log the error or ignore it.
*
* @see http://www.php.net/set-error-handler
- * @param int $errno The level of the error raised
- * @param string $errmsg The error message
+ *
+ * @param int $errno The level of the error raised
+ * @param string $errmsg The error message
* @param string $filename The filename the error was raised in
- * @param int $linenum The line number the error was raised at
- * @param array $vars An array that points to the active symbol table at the point that the error occurred
+ * @param int $linenum The line number the error was raised at
+ * @param array $vars An array that points to the active symbol table where error occurred
+ *
+ * @return true
+ * @throws Exception
+ * @access private
+ * @todo Replace error_log calls with elgg_log calls.
*/
-function __elgg_php_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
+function _elgg_php_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");
+ error_log("PHP ERROR: $error");
register_error("ERROR: $error");
// Since this is a fatal error, we want to stop any further execution but do so gracefully.
@@ -1139,13 +1086,18 @@ function __elgg_php_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
case E_WARNING :
case E_USER_WARNING :
- error_log("WARNING: $error");
+ case E_RECOVERABLE_ERROR: // (e.g. type hint violation)
+
+ // check if the error wasn't suppressed by the error control operator (@)
+ if (error_reporting()) {
+ error_log("PHP WARNING: $error");
+ }
break;
default:
global $CONFIG;
if (isset($CONFIG->debug) && $CONFIG->debug === 'NOTICE') {
- error_log("NOTICE: $error");
+ error_log("PHP NOTICE: $error");
}
}
@@ -1153,19 +1105,25 @@ function __elgg_php_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
}
/**
- * Throws a message to the Elgg logger
+ * Display or log a message.
+ *
+ * If $level is >= to the debug setting in {@link $CONFIG->debug}, the
+ * message will be sent to {@link elgg_dump()}. Messages with lower
+ * priority than {@link $CONFIG->debug} are ignored.
*
- * The Elgg log is currently implemented such that any messages sent at a level
- * greater than or equal to the debug setting will be sent to elgg_dump.
- * The default location for elgg_dump is the screen except for notices.
+ * {@link elgg_dump()} outputs all levels but NOTICE to screen by default.
*
- * Note: No messages will be displayed unless debugging has been enabled.
+ * @note No messages will be displayed unless debugging has been enabled.
+ *
+ * @param string $message User message
+ * @param string $level NOTICE | WARNING | ERROR | DEBUG
*
- * @param str $message User message
- * @param str $level NOTICE | WARNING | ERROR | DEBUG
* @return bool
+ * @since 1.7.0
+ * @todo This is complicated and confusing. Using int constants for debug levels will
+ * make things easier.
*/
-function elgg_log($message, $level='NOTICE') {
+function elgg_log($message, $level = 'NOTICE') {
global $CONFIG;
// only log when debugging is enabled
@@ -1201,25 +1159,31 @@ function elgg_log($message, $level='NOTICE') {
}
/**
- * Extremely generic var_dump-esque wrapper
+ * Logs or displays $value.
+ *
+ * If $to_screen is true, $value is displayed to screen. Else,
+ * it is handled by PHP's {@link error_log()} function.
*
- * Immediately dumps the given $value as a human-readable string.
- * The $value can instead be written to the screen or server log depending on
- * the value of the $to_screen flag.
+ * A {@elgg_plugin_hook debug log} is called. If a handler returns
+ * false, it will stop the default logging method.
+ *
+ * @param mixed $value The value
+ * @param bool $to_screen Display to screen?
+ * @param string $level The debug level
*
- * @param mixed $value
- * @param bool $to_screen
- * @param string $level
* @return void
+ * @since 1.7.0
*/
function elgg_dump($value, $to_screen = TRUE, $level = 'NOTICE') {
global $CONFIG;
// plugin can return false to stop the default logging method
- $params = array('level' => $level,
- 'msg' => $value,
- 'to_screen' => $to_screen);
- if (!trigger_plugin_hook('debug', 'log', $params, true)) {
+ $params = array(
+ 'level' => $level,
+ 'msg' => $value,
+ 'to_screen' => $to_screen,
+ );
+ if (!elgg_trigger_plugin_hook('debug', 'log', $params, true)) {
return;
}
@@ -1230,6 +1194,11 @@ function elgg_dump($value, $to_screen = TRUE, $level = 'NOTICE') {
$to_screen = FALSE;
}
+ // Do not want to write to JS or CSS pages
+ if (elgg_in_context('js') || elgg_in_context('css')) {
+ $to_screen = FALSE;
+ }
+
if ($to_screen == TRUE) {
echo '<pre>';
print_r($value);
@@ -1240,606 +1209,165 @@ function elgg_dump($value, $to_screen = TRUE, $level = 'NOTICE') {
}
/**
- * Custom exception handler.
- * This function catches any thrown exceptions and handles them appropriately.
+ * Sends a notice about deprecated use of a function, view, etc.
*
- * @see http://www.php.net/set-exception-handler
- * @param Exception $exception The exception being handled
- */
-function __elgg_php_exception_handler($exception) {
- error_log("*** FATAL EXCEPTION *** : " . $exception);
-
- ob_end_clean(); // Wipe any existing output buffer
-
- // make sure the error isn't cached
- header("Cache-Control: no-cache, must-revalidate", true);
- header('Expires: Fri, 05 Feb 1982 00:00:00 -0500', true);
- //header("Internal Server Error", true, 500);
-
- $body = elgg_view("messages/exceptions/exception",array('object' => $exception));
- page_draw(elgg_echo('exception:title'), $body);
-}
-
-/**
- * Data lists
- */
-
-$DATALIST_CACHE = array();
-
-/**
- * Get the value of a particular piece of data in the datalist
+ * This function either displays or logs the deprecation message,
+ * depending upon the deprecation policies in {@link CODING.txt}.
+ * Logged messages are sent with the level of 'WARNING'. Only admins
+ * get visual deprecation notices. When non-admins are logged in, the
+ * notices are sent to PHP's log through elgg_dump().
*
- * @param string $name The name of the datalist
- * @return string|false Depending on success
- */
-function datalist_get($name) {
- global $CONFIG, $DATALIST_CACHE;
-
- // We need this, because sometimes datalists are received before the database is created
- if (!is_db_installed()) {
- return false;
- }
-
- $name = sanitise_string($name);
- if (isset($DATALIST_CACHE[$name])) {
- return $DATALIST_CACHE[$name];
- }
-
- // If memcache enabled then cache value in memcache
- $value = null;
- static $datalist_memcache;
- if ((!$datalist_memcache) && (is_memcache_available())) {
- $datalist_memcache = new ElggMemcache('datalist_memcache');
- }
- if ($datalist_memcache) {
- $value = $datalist_memcache->load($name);
- }
- if ($value) {
- return $value;
- }
-
- // [Marcus Povey 20090217 : Now retrieving all datalist values on first load as this saves about 9 queries per page]
- $result = get_data("SELECT * from {$CONFIG->dbprefix}datalists");
- if ($result) {
- foreach ($result as $row) {
- $DATALIST_CACHE[$row->name] = $row->value;
-
- // Cache it if memcache is available
- if ($datalist_memcache) {
- $datalist_memcache->save($row->name, $row->value);
- }
- }
-
- if (isset($DATALIST_CACHE[$name])) {
- return $DATALIST_CACHE[$name];
- }
- }
-
-
- /*if ($row = get_data_row("SELECT value from {$CONFIG->dbprefix}datalists where name = '{$name}' limit 1")) {
- $DATALIST_CACHE[$name] = $row->value;
-
- // Cache it if memcache is available
- if ($datalist_memcache) $datalist_memcache->save($name, $row->value);
-
- return $row->value;
- }*/
-
- return false;
-}
-
-/**
- * Sets the value for a system-wide piece of data (overwriting a previous value if it exists)
- *
- * @param string $name The name of the datalist
- * @param string $value The new value
- * @return true
- */
-function datalist_set($name, $value) {
-
- global $CONFIG, $DATALIST_CACHE;
-
- $name = sanitise_string($name);
- $value = sanitise_string($value);
-
- // If memcache is available then invalidate the cached copy
- static $datalist_memcache;
- if ((!$datalist_memcache) && (is_memcache_available())) {
- $datalist_memcache = new ElggMemcache('datalist_memcache');
- }
-
- if ($datalist_memcache) {
- $datalist_memcache->delete($name);
- }
-
- //delete_data("delete from {$CONFIG->dbprefix}datalists where name = '{$name}'");
- insert_data("INSERT into {$CONFIG->dbprefix}datalists set name = '{$name}', value = '{$value}' ON DUPLICATE KEY UPDATE value='{$value}'");
-
- $DATALIST_CACHE[$name] = $value;
-
- return true;
-}
-
-/**
- * Runs a function once - not per page load, but per installation.
- * If you like, you can also set the threshold for the function execution - i.e.,
- * if the function was executed before or on $timelastupdatedcheck, this
- * function will run it again.
+ * A user-visual message will be displayed if $dep_version is greater
+ * than 1 minor releases lower than the current Elgg version, or at all
+ * lower than the current Elgg major version.
*
- * @param string $functionname The name of the function you want to run.
- * @param int $timelastupdatedcheck Optionally, the UNIX epoch timestamp of the execution threshold
- * @return true|false Depending on success.
- */
-function run_function_once($functionname, $timelastupdatedcheck = 0) {
- if ($lastupdated = datalist_get($functionname)) {
- $lastupdated = (int) $lastupdated;
- } else {
- $lastupdated = 0;
- }
- if (is_callable($functionname) && $lastupdated <= $timelastupdatedcheck) {
- $functionname();
- datalist_set($functionname,time());
- return true;
- } else {
- return false;
- }
-}
-
-/**
- * Sends a notice about deprecated use of a function, view, etc.
- * Note: This will ALWAYS at least log a warning. Don't use to pre-deprecate things.
+ * @note This will always at least log a warning. Don't use to pre-deprecate things.
* This assumes we are releasing in order and deprecating according to policy.
*
- * @param str $msg Message to log / display.
- * @param str $version human-readable *release* version the function was deprecated. No bloody A, B, (R)C, or D.
+ * @see CODING.txt
+ *
+ * @param string $msg Message to log / display.
+ * @param string $dep_version Human-readable *release* version: 1.7, 1.8, ...
+ * @param int $backtrace_level How many levels back to display the backtrace.
+ * Useful if calling from functions that are called
+ * from other places (like elgg_view()). Set to -1
+ * for a full backtrace.
*
* @return bool
+ * @since 1.7.0
*/
-function elgg_deprecated_notice($msg, $dep_version) {
+function elgg_deprecated_notice($msg, $dep_version, $backtrace_level = 1) {
// if it's a major release behind, visual and logged
- // if it's a 2 minor releases behind, visual and logged
- // if it's 1 minor release behind, logged.
- // bugfixes don't matter because you're not deprecating between them, RIGHT?
+ // if it's a 1 minor release behind, visual and logged
+ // if it's for current minor release, logged.
+ // bugfixes don't matter because we are not deprecating between them
if (!$dep_version) {
- return FALSE;
+ return false;
}
- $elgg_version = get_version(TRUE);
+ $elgg_version = get_version(true);
$elgg_version_arr = explode('.', $elgg_version);
- $elgg_major_version = $elgg_version_arr[0];
- $elgg_minor_version = $elgg_version_arr[1];
+ $elgg_major_version = (int)$elgg_version_arr[0];
+ $elgg_minor_version = (int)$elgg_version_arr[1];
- $dep_version_arr = explode('.', $dep_version);
- $dep_major_version = $dep_version_arr[0];
- $dep_minor_version = $dep_version_arr[1];
+ $dep_major_version = (int)$dep_version;
+ $dep_minor_version = 10 * ($dep_version - $dep_major_version);
- $last_working_version = $dep_minor_version - 1;
+ $visual = false;
- $visual = FALSE;
-
- // use version_compare to account for 1.7a < 1.7
- if (($dep_major_version < $elgg_major_version)
- || (($elgg_minor_version - $last_working_version) > 1)) {
- $visual = TRUE;
+ if (($dep_major_version < $elgg_major_version) ||
+ ($dep_minor_version < $elgg_minor_version)) {
+ $visual = true;
}
- $msg = "Deprecated in $dep_version: $msg";
+ $msg = "Deprecated in $dep_major_version.$dep_minor_version: $msg";
- if ($visual) {
+ if ($visual && elgg_is_admin_logged_in()) {
register_error($msg);
}
// Get a file and line number for the log. Never show this in the UI.
// Skip over the function that sent this notice and see who called the deprecated
// function itself.
+ $msg .= " Called from ";
+ $stack = array();
$backtrace = debug_backtrace();
- $caller = $backtrace[1];
- $msg .= " (Called from {$caller['file']}:{$caller['line']})";
+ // never show this call.
+ array_shift($backtrace);
+ $i = count($backtrace);
- elgg_log($msg, 'WARNING');
+ foreach ($backtrace as $trace) {
+ $stack[] = "[#$i] {$trace['file']}:{$trace['line']}";
+ $i--;
- return TRUE;
-}
-
-
-/**
- * Privilege elevation and gatekeeper code
- */
-
-
-/**
- * Gatekeeper function which ensures that a we are being executed from
- * a specified location.
- *
- * To use, call this function with the function name (and optional file location) that it has to be called
- * from, it will either return true or false.
- *
- * e.g.
- *
- * function my_secure_function()
- * {
- * if (!call_gatekeeper("my_call_function"))
- * return false;
- *
- * ... do secure stuff ...
- * }
- *
- * function my_call_function()
- * {
- * // will work
- * my_secure_function();
- * }
- *
- * function bad_function()
- * {
- * // Will not work
- * my_secure_function();
- * }
- *
- * @param mixed $function The function that this function must have in its call stack,
- * to test against a method pass an array containing a class and method name.
- * @param string $file Optional file that the function must reside in.
- */
-function call_gatekeeper($function, $file = "") {
- // Sanity check
- if (!$function) {
- return false;
- }
-
- // Check against call stack to see if this is being called from the correct location
- $callstack = debug_backtrace();
- $stack_element = false;
-
- foreach ($callstack as $call) {
- if (is_array($function)) {
- if (
- (strcmp($call['class'], $function[0]) == 0) &&
- (strcmp($call['function'], $function[1]) == 0)
- ) {
- $stack_element = $call;
- }
- } else {
- if (strcmp($call['function'], $function) == 0) {
- $stack_element = $call;
+ if ($backtrace_level > 0) {
+ if ($backtrace_level <= 1) {
+ break;
}
+ $backtrace_level--;
}
}
- if (!$stack_element) {
- return false;
- }
-
-
- // If file then check that this it is being called from this function
- if ($file) {
- $mirror = null;
+ $msg .= implode("<br /> -> ", $stack);
- if (is_array($function)) {
- $mirror = new ReflectionMethod($function[0], $function[1]);
- } else {
- $mirror = new ReflectionFunction($function);
- }
-
- if ((!$mirror) || (strcmp($file,$mirror->getFileName())!=0)) {
- return false;
- }
- }
+ elgg_log($msg, 'WARNING');
return true;
}
/**
- * This function checks to see if it is being called at somepoint by a function defined somewhere
- * on a given path (optionally including subdirectories).
+ * Returns the current page's complete URL.
*
- * This function is similar to call_gatekeeper() but returns true if it is being called by a method or function which has been defined on a given path or by a specified file.
+ * The current URL is assembled using the network's wwwroot and the request URI
+ * in $_SERVER as populated by the web server. This function will include
+ * any schemes, usernames and passwords, and ports.
*
- * @param string $path The full path and filename that this function must have in its call stack If a partial path is given and $include_subdirs is true, then the function will return true if called by any function in or below the specified path.
- * @param bool $include_subdirs Are subdirectories of the path ok, or must you specify an absolute path and filename.
- * @param bool $strict_mode If true then the calling method or function must be directly called by something on $path, if false the whole call stack is searched.
+ * @return string The current page URL.
*/
-function callpath_gatekeeper($path, $include_subdirs = true, $strict_mode = false) {
- global $CONFIG;
-
- $path = sanitise_string($path);
-
- if ($path) {
- $callstack = debug_backtrace();
-
- foreach ($callstack as $call) {
- $call['file'] = str_replace("\\","/",$call['file']);
-
- if ($include_subdirs) {
- if (strpos($call['file'], $path) === 0) {
-
- if ($strict_mode) {
- $callstack[1]['file'] = str_replace("\\","/",$callstack[1]['file']);
- if ($callstack[1] === $call) { return true; }
- } else {
- return true;
- }
- }
- } else {
- if (strcmp($path, $call['file'])==0) {
- if ($strict_mode) {
- if ($callstack[1] === $call) {
- return true;
- }
- } else {
- return true;
- }
- }
- }
+function current_page_url() {
+ $url = parse_url(elgg_get_site_url());
- }
- return false;
- }
+ $page = $url['scheme'] . "://";
- if (isset($CONFIG->debug)) {
- system_message("Gatekeeper'd function called from {$callstack[1]['file']}:{$callstack[1]['line']}\n\nStack trace:\n\n" . print_r($callstack, true));
+ // user/pass
+ if ((isset($url['user'])) && ($url['user'])) {
+ $page .= $url['user'];
}
-
- return false;
-}
-
-/**
- * Returns true or false depending on whether a PHP .ini setting is on or off
- *
- * @param string $ini_get_arg The INI setting
- * @return true|false Depending on whether it's on or off
- */
-function ini_get_bool($ini_get_arg) {
- $temp = ini_get($ini_get_arg);
-
- if ($temp == '1' or strtolower($temp) == 'on') {
- return true;
+ if ((isset($url['pass'])) && ($url['pass'])) {
+ $page .= ":" . $url['pass'];
}
- return false;
-}
-
-/**
- * Function to be used in array_filter which returns true if $string is not null.
- *
- * @param string $string
- * @return bool
- */
-function is_not_null($string) {
- if (($string==='') || ($string===false) || ($string===null)) {
- return false;
+ if ((isset($url['user']) && $url['user']) ||
+ (isset($url['pass']) && $url['pass'])) {
+ $page .= "@";
}
- return true;
-}
+ $page .= $url['host'];
+ if ((isset($url['port'])) && ($url['port'])) {
+ $page .= ":" . $url['port'];
+ }
-/**
- * Normalise the singular keys in an options array
- * to the plural keys.
- *
- * @param $options
- * @param $singulars
- * @return array
- */
-function elgg_normalise_plural_options_array($options, $singulars) {
- foreach ($singulars as $singular) {
- $plural = $singular . 's';
+ $page = trim($page, "/");
- // normalize the singular to plural
- // isset() returns FALSE for array values of NULL, so they are ignored.
- // everything else falsy is included.
- //if (isset($options[$singular]) && $options[$singular] !== NULL && $options[$singular] !== FALSE) {
- if (isset($options[$singular])) {
- if (isset($options[$plural])) {
- if (is_array($options[$plural])) {
- $options[$plural][] = $options[$singlar];
- } else {
- $options[$plural] = array($options[$plural], $options[$singular]);
- }
- } else {
- $options[$plural] = array($options[$singular]);
- }
- }
- unset($options[$singular]);
- }
+ $page .= $_SERVER['REQUEST_URI'];
- return $options;
+ return $page;
}
/**
- * Get the full URL of the current page.
+ * Return the full URL of the current page.
*
* @return string The URL
+ * @todo Combine / replace with current_page_url()
*/
function full_url() {
$s = empty($_SERVER["HTTPS"]) ? '' : ($_SERVER["HTTPS"] == "on") ? "s" : "";
- $protocol = substr(strtolower($_SERVER["SERVER_PROTOCOL"]), 0, strpos(strtolower($_SERVER["SERVER_PROTOCOL"]), "/")) . $s;
- $port = ($_SERVER["SERVER_PORT"] == "80" || $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]);
+ $protocol = substr(strtolower($_SERVER["SERVER_PROTOCOL"]), 0,
+ strpos(strtolower($_SERVER["SERVER_PROTOCOL"]), "/")) . $s;
+
+ $port = ($_SERVER["SERVER_PORT"] == "80" || $_SERVER["SERVER_PORT"] == "443") ?
+ "" : (":" . $_SERVER["SERVER_PORT"]);
+ // This is here to prevent XSS in poorly written browsers used by 80% of the population.
+ // https://github.com/Elgg/Elgg/commit/0c947e80f512cb0a482b1864fd0a6965c8a0cd4a
$quotes = array('\'', '"');
$encoded = array('%27', '%22');
- return $protocol . "://" . $_SERVER['SERVER_NAME'] . $port . str_replace($quotes, $encoded, $_SERVER['REQUEST_URI']);
+ return $protocol . "://" . $_SERVER['SERVER_NAME'] . $port .
+ str_replace($quotes, $encoded, $_SERVER['REQUEST_URI']);
}
/**
- * Useful function found in the comments on the PHP man page for ip2long.
- * Returns 1 if an IP matches a given range.
+ * Builds a URL from the a parts array like one returned by {@link parse_url()}.
*
- * TODO: Check licence... assuming this is PD since it was found several places on the interwebs..
- * please check or rewrite.
+ * @note If only partial information is passed, a partial URL will be returned.
*
- * Matches:
- * xxx.xxx.xxx.xxx (exact)
- * xxx.xxx.xxx.[yyy-zzz] (range)
- * xxx.xxx.xxx.xxx/nn (nn = # bits, cisco style -- i.e. /24 = class C)
- * Does not match:
- * xxx.xxx.xxx.xx[yyy-zzz] (range, partial octets not supported)
- */
-function test_ip($range, $ip) {
- $result = 1;
-
- # IP Pattern Matcher
- # J.Adams <jna@retina.net>
- #
- # Matches:
- #
- # xxx.xxx.xxx.xxx (exact)
- # xxx.xxx.xxx.[yyy-zzz] (range)
- # xxx.xxx.xxx.xxx/nn (nn = # bits, cisco style -- i.e. /24 = class C)
- #
- # Does not match:
- # xxx.xxx.xxx.xx[yyy-zzz] (range, partial octets not supported)
-
- if (ereg("([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/([0-9]+)",$range,$regs)) {
- # perform a mask match
- $ipl = ip2long($ip);
- $rangel = ip2long($regs[1] . "." . $regs[2] . "." . $regs[3] . "." . $regs[4]);
-
- $maskl = 0;
-
- for ($i = 0; $i< 31; $i++) {
- if ($i < $regs[5]-1) {
- $maskl = $maskl + pow(2,(30-$i));
- }
- }
-
- if (($maskl & $rangel) == ($maskl & $ipl)) {
- return 1;
- } else {
- return 0;
- }
- } else {
- # range based
- $maskocts = split("\.",$range);
- $ipocts = split("\.",$ip);
-
- # perform a range match
- for ($i=0; $i<4; $i++) {
- if (ereg("\[([0-9]+)\-([0-9]+)\]",$maskocts[$i],$regs)) {
- if ( ($ipocts[$i] > $regs[2]) || ($ipocts[$i] < $regs[1])) {
- $result = 0;
- }
- } else {
- if ($maskocts[$i] <> $ipocts[$i]) {
- $result = 0;
- }
- }
- }
- }
-
- return $result;
-}
-
-/**
- * Match an IP address against a number of ip addresses or ranges, returning true if found.
+ * @param array $parts Associative array of URL components like parse_url() returns
+ * @param bool $html_encode HTML Encode the url?
*
- * @param array $networks
- * @param string $ip
- * @return bool
- */
-function is_ip_in_array(array $networks, $ip) {
- global $SYSTEM_LOG;
-
- foreach ($networks as $network) {
- if (test_ip(trim($network), $ip)) {
- return true;
- }
- }
-
- return false;
-}
-
-/**
- * An interface for objects that behave as elements within a social network that have a profile.
- *
- */
-interface Friendable {
- /**
- * Adds a user as a friend
- *
- * @param int $friend_guid The GUID of the user to add
- */
- public function addFriend($friend_guid);
-
- /**
- * Removes a user as a friend
- *
- * @param int $friend_guid The GUID of the user to remove
- */
- public function removeFriend($friend_guid);
-
- /**
- * Determines whether or not the current user is a friend of this entity
- *
- */
- public function isFriend();
-
- /**
- * Determines whether or not this entity is friends with a particular entity
- *
- * @param int $user_guid The GUID of the entity this entity may or may not be friends with
- */
- public function isFriendsWith($user_guid);
-
- /**
- * Determines whether or not a foreign entity has made this one a friend
- *
- * @param int $user_guid The GUID of the foreign entity
- */
- public function isFriendOf($user_guid);
-
- /**
- * Returns this entity's friends
- *
- * @param string $subtype The subtype of entity to return
- * @param int $limit The number of entities to return
- * @param int $offset Indexing offset
- */
- public function getFriends($subtype = "", $limit = 10, $offset = 0);
-
- /**
- * Returns entities that have made this entity a friend
- *
- * @param string $subtype The subtype of entity to return
- * @param int $limit The number of entities to return
- * @param int $offset Indexing offset
- */
- public function getFriendsOf($subtype = "", $limit = 10, $offset = 0);
-
- /**
- * Returns objects in this entity's container
- *
- * @param string $subtype The subtype of entity to return
- * @param int $limit The number of entities to return
- * @param int $offset Indexing offset
- */
- public function getObjects($subtype="", $limit = 10, $offset = 0);
-
- /**
- * Returns objects in the containers of this entity's friends
- *
- * @param string $subtype The subtype of entity to return
- * @param int $limit The number of entities to return
- * @param int $offset Indexing offset
- */
- public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0);
-
- /**
- * Returns the number of object entities in this entity's container
- *
- * @param string $subtype The subtype of entity to count
- */
- public function countObjects($subtype = "");
-}
-
-/**
- * Rebuilds a parsed (partial) URL
- *
- * @param array $parts Associative array of URL components like parse_url() returns
- * @param bool $htmlencode HTML Encode the url?
- * @return str Full URL
- * @since 1.7
+ * @return string Full URL
+ * @since 1.7.0
*/
function elgg_http_build_url(array $parts, $html_encode = TRUE) {
// build only what's given to us.
@@ -1858,17 +1386,26 @@ function elgg_http_build_url(array $parts, $html_encode = TRUE) {
}
}
-
/**
* Adds action tokens to URL
*
- * @param str $link Full action URL
- * @param bool $htmlencode html encode the url?
- * @return str URL with action tokens
- * @since 1.7
+ * As of 1.7.0 action tokens are required on all actions.
+ * Use this function to append action tokens to a URL's GET parameters.
+ * This will preserve any existing GET parameters.
+ *
+ * @note If you are using {@elgg_view input/form} you don't need to
+ * add tokens to the action. The form view automatically handles
+ * tokens.
+ *
+ * @param string $url Full action URL
+ * @param bool $html_encode HTML encode the url? (default: false)
+ *
+ * @return string URL with action tokens
+ * @since 1.7.0
+ * @link http://docs.elgg.org/Tutorials/Actions
*/
-function elgg_add_action_tokens_to_url($url, $html_encode = TRUE) {
- $components = parse_url($url);
+function elgg_add_action_tokens_to_url($url, $html_encode = FALSE) {
+ $components = parse_url(elgg_normalize_url($url));
if (isset($components['query'])) {
$query = elgg_parse_str($components['query']);
@@ -1890,20 +1427,15 @@ function elgg_add_action_tokens_to_url($url, $html_encode = TRUE) {
}
/**
- * @deprecated 1.7 final
- */
-function elgg_validate_action_url($url) {
- elgg_deprecated_notice('elgg_validate_action_url had a short life. Use elgg_add_action_tokens_to_url() instead.', '1.7b');
-
- return elgg_add_action_tokens_to_url($url);
-}
-
-/**
- * Removes a single elementry from a (partial) url query.
+ * Removes an element from a URL's query string.
*
- * @param string $url
- * @param string $element
- * @return string
+ * @note You can send a partial URL string.
+ *
+ * @param string $url Full URL
+ * @param string $element The element to remove
+ *
+ * @return string The new URL with the query element removed.
+ * @since 1.7.0
*/
function elgg_http_remove_url_query_element($url, $element) {
$url_array = parse_url($url);
@@ -1920,17 +1452,18 @@ function elgg_http_remove_url_query_element($url, $element) {
}
$url_array['query'] = http_build_query($query);
- $string = elgg_http_build_url($url_array);
+ $string = elgg_http_build_url($url_array, false);
return $string;
}
-
/**
- * Adds get params to $url
+ * Adds an element or elements to a URL's query string.
*
- * @param str $url
- * @param array $elements k/v pairs.
- * @return str
+ * @param string $url The URL
+ * @param array $elements Key/value pairs to add to the URL
+ *
+ * @return string The new URL with the query strings added
+ * @since 1.7.0
*/
function elgg_http_add_url_query_elements($url, array $elements) {
$url_array = parse_url($url);
@@ -1946,168 +1479,203 @@ function elgg_http_add_url_query_elements($url, array $elements) {
}
$url_array['query'] = http_build_query($query);
- $string = elgg_http_build_url($url_array);
+ $string = elgg_http_build_url($url_array, false);
return $string;
}
-
-/**
- * Breadcrumb support.
- */
-
/**
- * Adds a breadcrumb to the stack
+ * Test if two URLs are functionally identical.
+ *
+ * @tip If $ignore_params is used, neither the name nor its value will be considered when comparing.
+ *
+ * @tip The order of GET params doesn't matter.
*
- * @param string $title The title to display
- * @param string $link Optional. The link for the title.
+ * @param string $url1 First URL
+ * @param string $url2 Second URL
+ * @param array $ignore_params GET params to ignore in the comparison
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function elgg_push_breadcrumb($title, $link = NULL) {
- global $CONFIG;
- if (!is_array($CONFIG->breadcrumbs)) {
- $CONFIG->breadcrumbs = array();
+function elgg_http_url_is_identical($url1, $url2, $ignore_params = array('offset', 'limit')) {
+ // if the server portion is missing but it starts with / then add the url in.
+ // @todo use elgg_normalize_url()
+ if (elgg_substr($url1, 0, 1) == '/') {
+ $url1 = elgg_get_site_url() . ltrim($url1, '/');
}
- // avoid key collisions.
- $CONFIG->breadcrumbs[] = array('title' => $title, 'link' => $link);
-}
+ if (elgg_substr($url1, 0, 1) == '/') {
+ $url2 = elgg_get_site_url() . ltrim($url2, '/');
+ }
-/**
- * Removes last breadcrumb entry.
- *
- * @return array popped item.
- */
-function elgg_pop_breadcrumb() {
- global $CONFIG;
+ // @todo - should probably do something with relative URLs
- if (is_array($CONFIG->breadcrumbs)) {
- array_pop($CONFIG->breadcrumbs);
+ if ($url1 == $url2) {
+ return TRUE;
}
- return FALSE;
-}
+ $url1_info = parse_url($url1);
+ $url2_info = parse_url($url2);
-/**
- * Returns all breadcrumbs
- *
- * @return array Breadcrumbs
- */
-function elgg_get_breadcrumbs() {
- global $CONFIG;
+ if (isset($url1_info['path'])) {
+ $url1_info['path'] = trim($url1_info['path'], '/');
+ }
+ if (isset($url2_info['path'])) {
+ $url2_info['path'] = trim($url2_info['path'], '/');
+ }
- return (is_array($CONFIG->breadcrumbs)) ? $CONFIG->breadcrumbs : array();
-}
+ // compare basic bits
+ $parts = array('scheme', 'host', 'path');
+ foreach ($parts as $part) {
+ if ((isset($url1_info[$part]) && isset($url2_info[$part]))
+ && $url1_info[$part] != $url2_info[$part]) {
+ return FALSE;
+ } elseif (isset($url1_info[$part]) && !isset($url2_info[$part])) {
+ return FALSE;
+ } elseif (!isset($url1_info[$part]) && isset($url2_info[$part])) {
+ return FALSE;
+ }
+ }
-/**
- * Sticky forms
- */
+ // quick compare of get params
+ if (isset($url1_info['query']) && isset($url2_info['query'])
+ && $url1_info['query'] == $url2_info['query']) {
+ return TRUE;
+ }
-/**
- * Load all the REQUEST variables into the sticky form cache
- *
- * Call this from an action when you want all your submitted variables
- * available if the submission fails validation and is sent back to the form
- */
-function elgg_make_sticky_form($form_name) {
- global $CONFIG;
+ // compare get params that might be out of order
+ $url1_params = array();
+ $url2_params = array();
- $CONFIG->active_sticky_form = $form_name;
- elgg_clear_sticky_form($form_name);
+ if (isset($url1_info['query'])) {
+ if ($url1_info['query'] = html_entity_decode($url1_info['query'])) {
+ $url1_params = elgg_parse_str($url1_info['query']);
+ }
+ }
- if (!isset($_SESSION['sticky_forms'])) {
- $_SESSION['sticky_forms'] = array();
+ if (isset($url2_info['query'])) {
+ if ($url2_info['query'] = html_entity_decode($url2_info['query'])) {
+ $url2_params = elgg_parse_str($url2_info['query']);
+ }
}
- $_SESSION['sticky_forms'][$form_name] = array();
- foreach($_REQUEST as $key => $var) {
- // will go through XSS filtering on the get function
- $_SESSION['sticky_forms'][$form_name][$key] = $var;
+ // drop ignored params
+ foreach ($ignore_params as $param) {
+ if (isset($url1_params[$param])) {
+ unset($url1_params[$param]);
+ }
+ if (isset($url2_params[$param])) {
+ unset($url2_params[$param]);
+ }
}
-}
+ // array_diff_assoc only returns the items in arr1 that aren't in arrN
+ // but not the items that ARE in arrN but NOT in arr1
+ // if arr1 is an empty array, this function will return 0 no matter what.
+ // since we only care if they're different and not how different,
+ // add the results together to get a non-zero (ie, different) result
+ $diff_count = count(array_diff_assoc($url1_params, $url2_params));
+ $diff_count += count(array_diff_assoc($url2_params, $url1_params));
+ if ($diff_count > 0) {
+ return FALSE;
+ }
-/**
- * Clear the sticky form cache
- *
- * Call this if validation is successful in the action handler or
- * when they sticky values have been used to repopulate the form
- * after a validation error.
- *
- * @param string $name Form namespace
- */
-function elgg_clear_sticky_form($form_name) {
- unset($_SESSION['sticky_forms'][$form_name]);
+ return TRUE;
}
/**
- * Has this form been made sticky
+ * Checks for $array[$key] and returns its value if it exists, else
+ * returns $default.
*
- * @param string $name Form namespace
- * @return boolean
- */
-function elgg_is_sticky_form($form_name) {
- return isset($_SESSION['sticky_forms'][$form_name]);
-}
-
-/**
- * Get a specific stick variable
+ * Shorthand for $value = (isset($array['key'])) ? $array['key'] : 'default';
*
- * @param string $variable The name of the variable
- * @param mixed $default Default value if the variable does not exist in sticky cache
- * @param boolean $filter_result Filter for bad input if true
- * @return mixed
+ * @param string $key The key to check.
+ * @param array $array The array to check against.
+ * @param mixed $default Default value to return if nothing is found.
+ * @param bool $strict Return array key if it's set, even if empty. If false,
+ * return $default if the array key is unset or empty.
*
- * @todo should this filter the default value?
+ * @return mixed
+ * @since 1.8.0
*/
-function elgg_get_sticky_value($form_name, $variable, $default = NULL, $filter_result = true) {
- if (isset($_SESSION['sticky_forms'][$form_name][$variable])) {
- $value = $_SESSION['sticky_forms'][$form_name][$variable];
- if ($filter_result) {
- // XSS filter result
- $value = filter_tags($value);
- }
- return $value;
+function elgg_extract($key, array $array, $default = null, $strict = true) {
+ if (!is_array($array)) {
+ return $default;
+ }
+
+ if ($strict) {
+ return (isset($array[$key])) ? $array[$key] : $default;
+ } else {
+ return (isset($array[$key]) && !empty($array[$key])) ? $array[$key] : $default;
}
- return $default;
}
/**
- * Clear a specific sticky variable
+ * Sorts a 3d array by specific element.
*
- * @param string $variable The name of the variable to clear
+ * @warning Will re-index numeric indexes.
+ *
+ * @note This operates the same as the built-in sort functions.
+ * It sorts the array and returns a bool for success.
+ *
+ * Do this: elgg_sort_3d_array_by_value($my_array);
+ * Not this: $my_array = elgg_sort_3d_array_by_value($my_array);
+ *
+ * @param array &$array Array to sort
+ * @param string $element Element to sort by
+ * @param int $sort_order PHP sort order
+ * {@see http://us2.php.net/array_multisort}
+ * @param int $sort_type PHP sort type
+ * {@see http://us2.php.net/sort}
+ *
+ * @return bool
*/
-function elgg_clear_sticky_value($form_name, $variable) {
- unset($_SESSION['sticky_forms'][$form_name][$variable]);
+function elgg_sort_3d_array_by_value(&$array, $element, $sort_order = SORT_ASC,
+$sort_type = SORT_LOCALE_STRING) {
+
+ $sort = array();
+
+ foreach ($array as $v) {
+ if (isset($v[$element])) {
+ $sort[] = strtolower($v[$element]);
+ } else {
+ $sort[] = NULL;
+ }
+ };
+
+ return array_multisort($sort, $sort_order, $sort_type, $array);
}
/**
- * Returns the current active sticky form.
- * @return mixed Str | FALSE
+ * Return the state of a php.ini setting as a bool
+ *
+ * @warning Using this on ini settings that are not boolean
+ * will be inaccurate!
+ *
+ * @param string $ini_get_arg The INI setting
+ *
+ * @return bool Depending on whether it's on or off
*/
-function elgg_get_active_sticky_form() {
- global $CONFIG;
+function ini_get_bool($ini_get_arg) {
+ $temp = strtolower(ini_get($ini_get_arg));
- if (isset($CONFIG->active_sticky_form)) {
- $form_name = $CONFIG->active_sticky_form;
- } else {
- return FALSE;
+ if ($temp == '1' || $temp == 'on' || $temp == 'true') {
+ return true;
}
-
- return (elgg_is_sticky_form($form_name)) ? $form_name : FALSE;
+ return false;
}
-function elgg_set_active_sticky_form($form_name) {
- global $CONFIG;
-
- $CONFIG->active_sticky_form = $form_name;
-}
/**
- * Returns the PHP INI setting in bytes
+ * Returns a PHP INI setting in bytes.
+ *
+ * @tip Use this for arithmetic when determining if a file can be uploaded.
+ *
+ * @param string $setting The php.ini setting
*
- * @param str $setting
* @return int
- * @since 1.7
+ * @since 1.7.0
* @link http://www.php.net/manual/en/function.ini-get.php
*/
function elgg_get_ini_setting_in_bytes($setting) {
@@ -2115,12 +1683,14 @@ function elgg_get_ini_setting_in_bytes($setting) {
$val = ini_get($setting);
// convert INI setting when shorthand notation is used
- $last = strtolower($val[strlen($val)-1]);
+ $last = strtolower($val[strlen($val) - 1]);
switch($last) {
case 'g':
$val *= 1024;
+ // fallthrough intentional
case 'm':
$val *= 1024;
+ // fallthrough intentional
case 'k':
$val *= 1024;
}
@@ -2130,339 +1700,605 @@ function elgg_get_ini_setting_in_bytes($setting) {
}
/**
- * Server javascript pages.
+ * Returns true is string is not empty, false, or null.
+ *
+ * Function to be used in array_filter which returns true if $string is not null.
+ *
+ * @param string $string The string to test
*
- * @param $page
- * @return unknown_type
+ * @return bool
+ * @todo This is used once in metadata.php. Use a lambda function instead.
*/
-function js_page_handler($page) {
- if (is_array($page) && sizeof($page)) {
- $js = str_replace('.js','',$page[0]);
- $return = elgg_view('js/' . $js);
-
- header('Content-type: text/javascript');
- header('Expires: ' . date('r',time() + 864000));
- header("Pragma: public");
- header("Cache-Control: public");
- header("Content-Length: " . strlen($return));
-
- echo $return;
- exit;
+function is_not_null($string) {
+ if (($string === '') || ($string === false) || ($string === null)) {
+ return false;
}
+
+ return true;
}
/**
- * This function is a shutdown hook registered on startup which does nothing more than trigger a
- * shutdown event when the script is shutting down, but before database connections have been dropped etc.
+ * Normalise the singular keys in an options array to plural keys.
*
+ * Used in elgg_get_entities*() functions to support shortcutting plural
+ * names by singular names.
+ *
+ * @param array $options The options array. $options['keys'] = 'values';
+ * @param array $singulars A list of singular words to pluralize by adding 's'.
+ *
+ * @return array
+ * @since 1.7.0
+ * @access private
*/
-function __elgg_shutdown_hook() {
- global $START_MICROTIME;
+function elgg_normalise_plural_options_array($options, $singulars) {
+ foreach ($singulars as $singular) {
+ $plural = $singular . 's';
- trigger_elgg_event('shutdown', 'system');
+ if (array_key_exists($singular, $options)) {
+ if ($options[$singular] === ELGG_ENTITIES_ANY_VALUE) {
+ $options[$plural] = $options[$singular];
+ } else {
+ // Test for array refs #2641
+ if (!is_array($options[$singular])) {
+ $options[$plural] = array($options[$singular]);
+ } else {
+ $options[$plural] = $options[$singular];
+ }
+ }
+ }
+
+ unset($options[$singular]);
+ }
- $time = (float)(microtime(TRUE) - $START_MICROTIME);
- // demoted to NOTICE from DEBUG so javascript is not corrupted
- elgg_log("Page {$_SERVER['REQUEST_URI']} generated in $time seconds", 'NOTICE');
+ return $options;
}
/**
- * Register functions for Elgg core
+ * Emits a shutdown:system event upon PHP shutdown, but before database connections are dropped.
+ *
+ * @tip Register for the shutdown:system event to perform functions at the end of page loads.
*
- * @return unknown_type
+ * @warning Using this event to perform long-running functions is not very
+ * useful. Servers will hold pages until processing is done before sending
+ * them out to the browser.
+ *
+ * @see http://www.php.net/register-shutdown-function
+ *
+ * @return void
+ * @see register_shutdown_hook()
+ * @access private
*/
-function elgg_init() {
- global $CONFIG;
+function _elgg_shutdown_hook() {
+ global $START_MICROTIME;
- // Page handler for JS
- register_page_handler('js','js_page_handler');
+ try {
+ elgg_trigger_event('shutdown', 'system');
- // Register an event triggered at system shutdown
- register_shutdown_function('__elgg_shutdown_hook');
+ $time = (float)(microtime(TRUE) - $START_MICROTIME);
+ // demoted to NOTICE from DEBUG so javascript is not corrupted
+ elgg_log("Page {$_SERVER['REQUEST_URI']} generated in $time seconds", 'NOTICE');
+ } catch (Exception $e) {
+ $message = 'Error: ' . get_class($e) . ' thrown within the shutdown handler. ';
+ $message .= "Message: '{$e->getMessage()}' in file {$e->getFile()} (line {$e->getLine()})";
+ error_log($message);
+ error_log("Exception trace stack: {$e->getTraceAsString()}");
+ }
}
-function elgg_walled_garden_index() {
- $login = elgg_view('account/forms/login_walled_garden');
- echo elgg_view('page_shells/walled_garden', array(
- 'body' => $login,
- 'sysmessages' => system_messages(NULL, ''),
- ));
-
- // @hack Index must exit to keep plugins from continuing to extend
- exit;
- return TRUE;
+/**
+ * Serve javascript pages.
+ *
+ * Searches for views under js/ and outputs them with special
+ * headers for caching control.
+ *
+ * @param array $page The page array
+ *
+ * @return bool
+ * @elgg_pagehandler js
+ * @access private
+ */
+function elgg_js_page_handler($page) {
+ return elgg_cacheable_view_page_handler($page, 'js');
}
/**
- * Boot Elgg
- * @return unknown_type
+ * Serve individual views for Ajax.
+ *
+ * /ajax/view/<name of view>?<key/value params>
+ *
+ * @param array $page The page array
+ *
+ * @return bool
+ * @elgg_pagehandler ajax
+ * @access private
*/
-function elgg_boot() {
- global $CONFIG;
-
- // Actions
- register_action('comments/add');
- register_action('comments/delete');
- register_action('likes/add');
- register_action('likes/delete');
-
- elgg_view_register_simplecache('css');
- elgg_view_register_simplecache('js/friendsPickerv1');
- elgg_view_register_simplecache('js/initialise_elgg');
+function elgg_ajax_page_handler($page) {
+ if (is_array($page) && sizeof($page)) {
+ // throw away 'view' and form the view name
+ unset($page[0]);
+ $view = implode('/', $page);
- // discover the built-in view types
- // @todo cache this
- $view_path = $CONFIG->viewpath;
- $CONFIG->view_types = array();
+ $allowed_views = elgg_get_config('allowed_ajax_views');
+ if (!array_key_exists($view, $allowed_views)) {
+ header('HTTP/1.1 403 Forbidden');
+ exit;
+ }
- $views = scandir($view_path);
+ // pull out GET parameters through filter
+ $vars = array();
+ foreach ($_GET as $name => $value) {
+ $vars[$name] = get_input($name);
+ }
- foreach ($views as $view) {
- if ('.' !== substr($view, 0, 1) && is_dir($view_path . $view)) {
- $CONFIG->view_types[] = $view;
+ if (isset($vars['guid'])) {
+ $vars['entity'] = get_entity($vars['guid']);
}
- }
+ echo elgg_view($view, $vars);
+ return true;
+ }
+ return false;
}
/**
- * Runs unit tests for the API.
+ * Serve CSS
+ *
+ * Serves CSS from the css views directory with headers for caching control
+ *
+ * @param array $page The page array
+ *
+ * @return bool
+ * @elgg_pagehandler css
+ * @access private
*/
-function elgg_api_test($hook, $type, $value, $params) {
- global $CONFIG;
- $value[] = $CONFIG->path . 'engine/tests/api/entity_getter_functions.php';
- $value[] = $CONFIG->path . 'engine/tests/api/helpers.php';
- $value[] = $CONFIG->path . 'engine/tests/regression/trac_bugs.php';
- return $value;
+function elgg_css_page_handler($page) {
+ if (!isset($page[0])) {
+ // default css
+ $page[0] = 'elgg';
+ }
+
+ return elgg_cacheable_view_page_handler($page, 'css');
}
/**
- * Sorts out the featured URLs and the "more" dropdown
- * @return array ('featured_urls' and 'more')
- * @since 1.8
+ * Serves a JS or CSS view with headers for caching.
+ *
+ * /<css||js>/name/of/view.<last_cache>.<css||js>
+ *
+ * @param array $page The page array
+ * @param string $type The type: js or css
+ *
+ * @return bool
+ * @access private
*/
-function elgg_get_nav_items() {
- $menu_items = get_register('menu');
- $featured_urls_info = get_config('menu_items_featured_urls');
+function elgg_cacheable_view_page_handler($page, $type) {
- $more = array();
- $featured_urls = array();
- $featured_urls_sanitised = array();
+ switch ($type) {
+ case 'js':
+ $content_type = 'text/javascript';
+ break;
+
+ case 'css':
+ $content_type = 'text/css';
+ break;
- // easier to compare with in_array() than embedded foreach()es
- $valid_urls = array();
- foreach ($menu_items as $info) {
- $valid_urls[] = $info->value->url;
+ default:
+ return false;
+ break;
}
- // make sure the url is a valid link.
- // this prevents disabled plugins leaving behind
- // valid links when no using a pagehandler.
- foreach ($featured_urls_info as $info) {
- if (in_array($info->value->url, $valid_urls)) {
- $featured_urls[] = $info->value->url;
- $featured_urls_sanitised[] = $info;
- }
+ if ($page) {
+ // the view file names can have multiple dots
+ // eg: views/default/js/calendars/jquery.fullcalendar.min.php
+ // translates to the url /js/calendars/jquery.fullcalendar.min.<ts>.js
+ // and the view js/calendars/jquery.fullcalendar.min
+ // we ignore the last two dots for the ts and the ext.
+ // Additionally, the timestamp is optional.
+ $page = implode('/', $page);
+ $regex = '|(.+?)\.([\d]+\.)?\w+$|';
+ preg_match($regex, $page, $matches);
+ $view = $matches[1];
+ $return = elgg_view("$type/$view");
+
+ header("Content-type: $content_type");
+
+ // @todo should js be cached when simple cache turned off
+ //header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+10 days")), true);
+ //header("Pragma: public");
+ //header("Cache-Control: public");
+ //header("Content-Length: " . strlen($return));
+
+ echo $return;
+ return true;
}
+ return false;
+}
- // add toolbar entries if not hiding dupes.
- foreach ($menu_items as $name => $info) {
- if (!in_array($info->value->url, $featured_urls)) {
- $more[] = $info;
- }
+/**
+ * Reverses the ordering in an ORDER BY clause. This is achived by replacing
+ * asc with desc, or appending desc to the end of the clause.
+ *
+ * This is used mostly for elgg_get_entities() and other similar functions.
+ *
+ * @param string $order_by An order by clause
+ * @access private
+ * @return string
+ * @access private
+ */
+function elgg_sql_reverse_order_by_clause($order_by) {
+ $order_by = strtolower($order_by);
+
+ if (strpos($order_by, ' asc') !== false) {
+ $return = str_replace(' asc', ' desc', $order_by);
+ } elseif (strpos($order_by, ' desc') !== false) {
+ $return = str_replace(' desc', ' asc', $order_by);
+ } else {
+ // no order specified, so default to desc since mysql defaults to asc
+ $return = $order_by . ' desc';
}
- return array(
- 'featured' => $featured_urls_sanitised,
- 'more' => $more
- );
+ return $return;
}
/**
- * Hook that registers the custom menu items.
- * @since 1.8
+ * Enable objects with an enable() method.
+ *
+ * Used as a callback for ElggBatch.
+ *
+ * @todo why aren't these static methods on ElggBatch?
+ *
+ * @param object $object The object to enable
+ * @return bool
+ * @access private
*/
-function add_custom_menu_items() {
- if ($custom_items = get_config('menu_items_custom_items')) {
- foreach ($custom_items as $url => $name) {
- add_menu($name, $url);
- }
- }
+function elgg_batch_enable_callback($object) {
+ // our db functions return the number of rows affected...
+ return $object->enable() ? true : false;
}
/**
- * Test two URLs to see if they are functionally identical.
+ * Disable objects with a disable() method.
*
- * @param string $url1
- * @param string $url2
- * @param array $ignore_params - GET params to ignore in the comparison
- * @return BOOL
- * @since 1.8
+ * Used as a callback for ElggBatch.
+ *
+ * @param object $object The object to disable
+ * @return bool
+ * @access private
*/
-function elgg_http_url_is_identical($url1, $url2, $ignore_params = array('offset', 'limit')) {
- global $CONFIG;
+function elgg_batch_disable_callback($object) {
+ // our db functions return the number of rows affected...
+ return $object->disable() ? true : false;
+}
- // if the server portion is missing but it starts with / then add the url in.
- if (elgg_substr($url1, 0, 1) == '/') {
- $url1 = $CONFIG->url . ltrim($url1, '/');
- }
+/**
+ * Delete objects with a delete() method.
+ *
+ * Used as a callback for ElggBatch.
+ *
+ * @param object $object The object to disable
+ * @return bool
+ * @access private
+ */
+function elgg_batch_delete_callback($object) {
+ // our db functions return the number of rows affected...
+ return $object->delete() ? true : false;
+}
- if (elgg_substr($url1, 0, 1) == '/') {
- $url2 = $CONFIG->url . ltrim($url2, '/');
+/**
+ * Checks if there are some constraints on the options array for
+ * potentially dangerous operations.
+ *
+ * @param array $options Options array
+ * @param string $type Options type: metadata or annotations
+ * @return bool
+ * @access private
+ */
+function elgg_is_valid_options_for_batch_operation($options, $type) {
+ if (!$options || !is_array($options)) {
+ return false;
}
- // @todo - should probably do something with relative URLs
+ // at least one of these is required.
+ $required = array(
+ // generic restraints
+ 'guid', 'guids'
+ );
- if ($url1 == $url2) {
- return TRUE;
- }
+ switch ($type) {
+ case 'metadata':
+ $metadata_required = array(
+ 'metadata_owner_guid', 'metadata_owner_guids',
+ 'metadata_name', 'metadata_names',
+ 'metadata_value', 'metadata_values'
+ );
- $url1_info = parse_url($url1);
- $url2_info = parse_url($url2);
+ $required = array_merge($required, $metadata_required);
+ break;
- $url1_info['path'] = trim($url1_info['path'], '/');
- $url2_info['path'] = trim($url2_info['path'], '/');
+ case 'annotations':
+ case 'annotation':
+ $annotations_required = array(
+ 'annotation_owner_guid', 'annotation_owner_guids',
+ 'annotation_name', 'annotation_names',
+ 'annotation_value', 'annotation_values'
+ );
- // compare basic bits
- $parts = array('scheme', 'host', 'path');
+ $required = array_merge($required, $annotations_required);
+ break;
- foreach ($parts as $part) {
- if ((isset($url1_info[$part]) && isset($url2_info[$part])) && $url1_info[$part] != $url2_info[$part]) {
- return FALSE;
- } elseif (isset($url1_info[$part]) && !isset($url2_info[$part])) {
- return FALSE;
- } elseif (!isset($url1_info[$part]) && isset($url2_info[$part])) {
- return FALSE;
- }
+ default:
+ return false;
}
- // quick compare of get params
- if (isset($url1_info['query']) && isset($url2_info['query']) && $url1_info['query'] == $url2_info['query']) {
- return TRUE;
+ foreach ($required as $key) {
+ // check that it exists and is something.
+ if (isset($options[$key]) && $options[$key]) {
+ return true;
+ }
}
- // compare get params that might be out of order
- $url1_params = array();
- $url2_params = array();
+ return false;
+}
- if (isset($url1_info['query'])) {
- if ($url1_info['query'] = html_entity_decode($url1_info['query'])) {
- $url1_params = elgg_parse_str($url1_info['query']);
- }
+/**
+ * Intercepts the index page when Walled Garden mode is enabled.
+ *
+ * @link http://docs.elgg.org/Tutorials/WalledGarden
+ * @elgg_plugin_hook index system
+ *
+ * @param string $hook The name of the hook
+ * @param string $type The type of hook
+ * @param bool $value Has a plugin already rendered an index page?
+ * @param array $params Array of parameters (should be empty)
+ * @return bool
+ * @access private
+ */
+function elgg_walled_garden_index($hook, $type, $value, $params) {
+ if ($value) {
+ // do not create a second index page so return
+ return;
}
- if (isset($url2_info['query'])) {
- if ($url2_info['query'] = html_entity_decode($url2_info['query'])) {
- $url2_params = elgg_parse_str($url2_info['query']);
- }
- }
+ elgg_load_css('elgg.walled_garden');
+ elgg_load_js('elgg.walled_garden');
+
+ $content = elgg_view('core/walled_garden/login');
- // drop ignored params
- foreach ($ignore_params as $param) {
- if (isset($url1_params[$param])) {
- unset($url1_params[$param]);
- }
- if (isset($url2_params[$param])) {
- unset($url2_params[$param]);
- }
- }
+ $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');
- // array_diff_assoc only returns the items in arr1 that aren't in arrN
- // but not the items that ARE in arrN but NOT in arr1
- // if arr1 is an empty array, this function will return 0 no matter what.
- // since we only care if they're different and not how different,
- // add the results together to get a non-zero (ie, different) result
- $diff_count = count(array_diff_assoc($url1_params, $url2_params));
- $diff_count += count(array_diff_assoc($url2_params, $url1_params));
- if ($diff_count > 0) {
- return FALSE;
- }
+ // return true to prevent other plugins from adding a front page
+ return true;
+}
- return TRUE;
+/**
+ * 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 walled garden status upon Elgg init.
+ * Checks the status of the Walled Garden and forwards to a login page
+ * if required.
*
- * @since 1.8
+ * If the site is in Walled Garden mode, all page except those registered as
+ * plugin pages by {@elgg_hook public_pages walled_garden} will redirect to
+ * a login page.
+ *
+ * @since 1.8.0
+ * @elgg_event_handler init system
+ * @link http://docs.elgg.org/Tutorials/WalledGarden
+ * @return void
+ * @access private
*/
function elgg_walled_garden() {
global $CONFIG;
+ 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->check_walled_garden();
+ $CONFIG->site->checkWalledGarden();
}
}
/**
- * Checks for $array[$key] and returns its value if it exists, else
- * returns $default.
+ * Remove public access for walled gardens
*
- * Shorthand for $value = (isset($array['key'])) ? $array['key'] : 'default';
+ * @param string $hook
+ * @param string $type
+ * @param array $accesses
+ * @return array
+ * @access private
+ */
+function _elgg_walled_garden_remove_public_access($hook, $type, $accesses) {
+ if (isset($accesses[ACCESS_PUBLIC])) {
+ unset($accesses[ACCESS_PUBLIC]);
+ }
+ return $accesses;
+}
+
+/**
+ * Boots the engine
*
- * @param string $key The key to check.
- * @param array $array The array to check against.
- * @param mixed $default Default value to return if nothing is found.
- * @since 1.8
+ * 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_get_array_value($key, array $array, $default = NULL) {
- return (isset($array[$key])) ? $array[$key] : $default;
+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();
+
+ _elgg_load_site_config();
+
+ _elgg_session_boot();
+
+ _elgg_load_cache();
+
+ _elgg_load_translations();
}
/**
- * Sorts a 3d array by specific element.
- * Will re-index numeric indexes.
- * NB: This operates the same as the built-in sort functions.
- * ie, sorts the array and returns a bool for success.
+ * Elgg's main init.
*
- * Do this: elgg_sort_3d_array_by_value($my_array);
- * Not this: $my_array = elgg_sort_3d_array_by_value($my_array);
+ * Handles core actions for comments, the JS pagehandler, and the shutdown function.
*
- * @param array $array Array to sort
- * @param string $element Element to sort by
- * @param $sort_order
- * @param $sort_type
- * @return bool
+ * @elgg_event_handler init system
+ * @return void
+ * @access private
*/
-function elgg_sort_3d_array_by_value(&$array, $element, $sort_order = SORT_ASC, $sort_type = SORT_LOCALE_STRING) {
- $sort = array();
+function elgg_init() {
+ global $CONFIG;
+
+ elgg_register_action('comments/add');
+ elgg_register_action('comments/delete');
+
+ elgg_register_page_handler('js', 'elgg_js_page_handler');
+ elgg_register_page_handler('css', 'elgg_css_page_handler');
+ elgg_register_page_handler('ajax', 'elgg_ajax_page_handler');
+
+ elgg_register_js('elgg.autocomplete', 'js/lib/ui.autocomplete.js');
+ elgg_register_js('jquery.ui.autocomplete.html', 'vendors/jquery/jquery.ui.autocomplete.html.js');
+ elgg_register_js('elgg.userpicker', 'js/lib/ui.userpicker.js');
+ elgg_register_js('elgg.friendspicker', 'js/lib/ui.friends_picker.js');
+ elgg_register_js('jquery.easing', 'vendors/jquery/jquery.easing.1.3.packed.js');
+ elgg_register_js('elgg.avatar_cropper', 'js/lib/ui.avatar_cropper.js');
+ elgg_register_js('jquery.imgareaselect', 'vendors/jquery/jquery.imgareaselect-0.9.8/scripts/jquery.imgareaselect.min.js');
+ elgg_register_js('elgg.ui.river', 'js/lib/ui.river.js');
+
+ elgg_register_css('jquery.imgareaselect', 'vendors/jquery/jquery.imgareaselect-0.9.8/css/imgareaselect-deprecated.css');
- foreach ($array as $k => $v) {
- if (isset($v[$element])) {
- $sort[] = strtolower($v[$element]);
- } else {
- $sort[] = NULL;
- }
- };
+ // Trigger the shutdown:system event upon PHP shutdown.
+ register_shutdown_function('_elgg_shutdown_hook');
+
+ $logo_url = elgg_get_site_url() . "_graphics/elgg_toolbar_logo.gif";
+ elgg_register_menu_item('topbar', array(
+ 'name' => 'elgg_logo',
+ 'href' => 'http://www.elgg.org/',
+ 'text' => "<img src=\"$logo_url\" alt=\"Elgg logo\" width=\"38\" height=\"20\" />",
+ 'priority' => 1,
+ 'link_class' => 'elgg-topbar-logo',
+ ));
- return array_multisort($sort, $sort_order, $sort_type, $array);
+ // Sets a blacklist of words in the current language.
+ // This is a comma separated list in word:blacklist.
+ // @todo possibly deprecate
+ $CONFIG->wordblacklist = array();
+ $list = explode(',', elgg_echo('word:blacklist'));
+ if ($list) {
+ foreach ($list as $l) {
+ $CONFIG->wordblacklist[] = trim($l);
+ }
+ }
}
/**
- * Some useful constant definitions
+ * Adds unit tests for the general API.
+ *
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param array $value array of test files
+ * @param array $params empty
+ *
+ * @elgg_plugin_hook unit_tests system
+ * @return array
+ * @access private
+ */
+function elgg_api_test($hook, $type, $value, $params) {
+ global $CONFIG;
+ $value[] = $CONFIG->path . 'engine/tests/api/entity_getter_functions.php';
+ $value[] = $CONFIG->path . 'engine/tests/api/helpers.php';
+ $value[] = $CONFIG->path . 'engine/tests/regression/trac_bugs.php';
+ return $value;
+}
+
+/**#@+
+ * Controls access levels on ElggEntity entities, metadata, and annotations.
+ *
+ * @warning ACCESS_DEFAULT is a place holder for the input/access view. Do not
+ * use it when saving an entity.
+ *
+ * @var int
*/
define('ACCESS_DEFAULT', -1);
define('ACCESS_PRIVATE', 0);
define('ACCESS_LOGGED_IN', 1);
define('ACCESS_PUBLIC', 2);
define('ACCESS_FRIENDS', -2);
+/**#@-*/
/**
+ * Constant to request the value of a parameter be ignored in elgg_get_*() functions
+ *
+ * @see elgg_get_entities()
+ * @var NULL
* @since 1.7
*/
define('ELGG_ENTITIES_ANY_VALUE', NULL);
+
+/**
+ * Constant to request the value of a parameter be nothing in elgg_get_*() functions.
+ *
+ * @see elgg_get_entities()
+ * @var int 0
+ * @since 1.7
+ */
define('ELGG_ENTITIES_NO_VALUE', 0);
/**
- * @since 1.7.2
+ * Used in calls to forward() to specify the browser should be redirected to the
+ * referring page.
+ *
+ * @see forward
+ * @var int -1
*/
define('REFERRER', -1);
+
+/**
+ * Alternate spelling for REFERRER. Included because of some bad documentation
+ * in the original HTTP spec.
+ *
+ * @see forward()
+ * @link http://en.wikipedia.org/wiki/HTTP_referrer#Origin_of_the_term_referer
+ * @var int -1
+ */
define('REFERER', -1);
-register_elgg_event_handler('init', 'system', 'elgg_init');
-register_elgg_event_handler('boot', 'system', 'elgg_boot', 1000);
-register_plugin_hook('unit_test', 'system', 'elgg_api_test');
+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');
-register_elgg_event_handler('init', 'system', 'add_custom_menu_items', 1000);
-register_elgg_event_handler('init', 'system', 'elgg_walled_garden', 1000);
+elgg_register_event_handler('init', 'system', 'add_custom_menu_items', 1000);
+elgg_register_event_handler('init', 'system', 'elgg_walled_garden', 1000);
diff --git a/engine/lib/entities.php b/engine/lib/entities.php
index 0f718d162..4fcf1c657 100644
--- a/engine/lib/entities.php
+++ b/engine/lib/entities.php
@@ -1,1270 +1,142 @@
<?php
/**
- * Elgg entities.
- * Functions to manage all elgg entities (sites, collections, objects and users).
+ * Procedural code for creating, loading, and modifying ElggEntity objects.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage DataModel.Entities
+ * @link http://docs.elgg.org/DataModel/Entities
*/
-/// Cache objects in order to minimise database access.
-$ENTITY_CACHE = NULL;
-
-/// Cache subtype searches
-$SUBTYPE_CACHE = NULL;
-
-/// Require the locatable interface TODO: Move this into start.php?
-require_once('location.php');
-
/**
- * ElggEntity The elgg entity superclass
- * This class holds methods for accessing the main entities table.
+ * Cache entities in memory once loaded.
*
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Core
+ * @global array $ENTITY_CACHE
+ * @access private
*/
-abstract class ElggEntity implements
- Notable, // Calendar interface
- Locatable, // Geocoding interface
- Exportable, // Allow export of data
- Importable, // Allow import of data
- Loggable, // Can events related to this object class be logged
- Iterator, // Override foreach behaviour
- ArrayAccess // Override for array access
-{
- /**
- * The main attributes of an entity.
- * Blank entries for all database fields should be created by the constructor.
- * Subclasses should add to this in their constructors.
- * Any field not appearing in this will be viewed as a
- */
- protected $attributes;
-
- /**
- * If set, overrides the value of getURL()
- */
- protected $url_override;
-
- /**
- * Icon override, overrides the value of getIcon().
- */
- protected $icon_override;
-
- /**
- * Temporary cache for metadata, permitting meta data access before a guid has obtained.
- */
- protected $temp_metadata;
-
- /**
- * Temporary cache for annotations, permitting meta data access before a guid has obtained.
- */
- protected $temp_annotations;
-
-
- /**
- * Volatile data structure for this object, allows for storage of data
- * in-memory that isn't sync'd back to the metadata table.
- */
- protected $volatile;
-
- /**
- * Initialise the attributes array.
- * This is vital to distinguish between metadata and base parameters.
- *
- * Place your base parameters here.
- *
- * @return void
- */
- protected function initialise_attributes() {
- initialise_entity_cache();
-
- // Create attributes array if not already created
- if (!is_array($this->attributes)) {
- $this->attributes = array();
- }
- if (!is_array($this->temp_metadata)) {
- $this->temp_metadata = array();
- }
- if (!is_array($this->temp_annotations)) {
- $this->temp_annotations = array();
- }
- if (!is_array($this->volatile)) {
- $this->volatile = array();
- }
-
- $this->attributes['guid'] = "";
- $this->attributes['type'] = "";
- $this->attributes['subtype'] = "";
-
- $this->attributes['owner_guid'] = get_loggedin_userid();
- $this->attributes['container_guid'] = get_loggedin_userid();
-
- $this->attributes['site_guid'] = 0;
- $this->attributes['access_id'] = ACCESS_PRIVATE;
- $this->attributes['time_created'] = "";
- $this->attributes['time_updated'] = "";
- $this->attributes['last_action'] = '';
- $this->attributes['enabled'] = "yes";
-
- // There now follows a bit of a hack
- /* Problem: To speed things up, some objects are split over several tables, this means that it requires
- * n number of database reads to fully populate an entity. This causes problems for caching and create events
- * since it is not possible to tell whether a subclassed entity is complete.
- * Solution: We have two counters, one 'tables_split' which tells whatever is interested how many tables
- * are going to need to be searched in order to fully populate this object, and 'tables_loaded' which is how
- * many have been loaded thus far.
- * If the two are the same then this object is complete.
- *
- * Use: isFullyLoaded() to check
- */
- $this->attributes['tables_split'] = 1;
- $this->attributes['tables_loaded'] = 0;
- }
-
- /**
- * Clone an entity
- *
- * Resets the guid so that the entity can be saved as a distinct entity from
- * the original. Creation time will be set when this new entity is saved.
- * The owner and container guids come from the original entity. The clone
- * method copies metadata but does not copy over annotations, or private settings.
- *
- * Note: metadata will have its owner and access id set when the entity is saved
- * and it will be the same as that of the entity.
- */
- public function __clone() {
-
- $orig_entity = get_entity($this->guid);
- if (!$orig_entity) {
- elgg_log("Failed to clone entity with GUID $this->guid", "ERROR");
- return;
- }
-
- $metadata_array = get_metadata_for_entity($this->guid);
-
- $this->attributes['guid'] = "";
-
- $this->attributes['subtype'] = $orig_entity->getSubtype();
-
- // copy metadata over to new entity - slightly convoluted due to
- // handling of metadata arrays
- if (is_array($metadata_array)) {
- // create list of metadata names
- $metadata_names = array();
- foreach ($metadata_array as $metadata) {
- $metadata_names[] = $metadata['name'];
- }
- // arrays are stored with multiple enties per name
- $metadata_names = array_unique($metadata_names);
-
- // move the metadata over
- foreach ($metadata_names as $name) {
- $this->set($name, $orig_entity->$name);
- }
- }
- }
-
- /**
- * Return the value of a given key.
- * If $name is a key field (as defined in $this->attributes) that value is returned, otherwise it will
- * then look to see if the value is in this object's metadata.
- *
- * Q: Why are we not using __get overload here?
- * A: Because overload operators cause problems during subclassing, so we put the code here and
- * create overloads in subclasses.
- *
- * subtype is returned as an id rather than the subtype string. Use getSubtype()
- * to get the subtype string.
- *
- * @param string $name
- * @return mixed Returns the value of a given value, or null.
- */
- public function get($name) {
- // See if its in our base attribute
- if (isset($this->attributes[$name])) {
- return $this->attributes[$name];
- }
-
- // No, so see if its in the meta data for this entity
- $meta = $this->getMetaData($name);
-
- // getMetaData returns NULL if $name is not found
- return $meta;
- }
-
- /**
- * Set the value of a given key, replacing it if necessary.
- * If $name is a base attribute (as defined in $this->attributes) that value is set, otherwise it will
- * set the appropriate item of metadata.
- *
- * Note: It is important that your class populates $this->attributes with keys for all base attributes, anything
- * not in their gets set as METADATA.
- *
- * Q: Why are we not using __set overload here?
- * A: Because overload operators cause problems during subclassing, so we put the code here and
- * create overloads in subclasses.
- *
- * @param string $name
- * @param mixed $value
- */
- public function set($name, $value) {
- if (array_key_exists($name, $this->attributes)) {
- // Check that we're not trying to change the guid!
- if ((array_key_exists('guid', $this->attributes)) && ($name=='guid')) {
- return false;
- }
-
- $this->attributes[$name] = $value;
- }
- else {
- return $this->setMetaData($name, $value);
- }
-
- return true;
- }
-
- /**
- * Get a given piece of metadata.
- *
- * @param string $name
- */
- public function getMetaData($name) {
- if ((int) ($this->guid) > 0) {
- $md = get_metadata_byname($this->getGUID(), $name);
- } else {
- if (isset($this->temp_metadata[$name])) {
- return $this->temp_metadata[$name];
- }
- }
-
- if ($md && !is_array($md)) {
- return $md->value;
- } else if ($md && is_array($md)) {
- return metadata_array_to_values($md);
- }
-
- return null;
- }
-
- /**
- * Class member get overloading
- *
- * @param string $name
- * @return mixed
- */
- function __get($name) {
- return $this->get($name);
- }
-
- /**
- * Class member set overloading
- *
- * @param string $name
- * @param mixed $value
- * @return mixed
- */
- function __set($name, $value) {
- return $this->set($name, $value);
- }
-
- /**
- * Supporting isset.
- *
- * @param string $name The name of the attribute or metadata.
- * @return bool
- */
- function __isset($name) {
- return $this->$name !== NULL;
- }
-
- /**
- * Supporting unsetting of magic attributes.
- *
- * @param string $name The name of the attribute or metadata.
- */
- function __unset($name) {
- if (array_key_exists($name, $this->attributes)) {
- $this->attributes[$name] = "";
- }
- else {
- $this->clearMetaData($name);
- }
- }
-
- /**
- * Set a piece of metadata.
- *
- * @param string $name Name of the metadata
- * @param mixed $value Value of the metadata
- * @param string $value_type Types supported: integer and string. Will auto-identify if not set
- * @param bool $multiple
- * @return bool
- */
- public function setMetaData($name, $value, $value_type = "", $multiple = false) {
- if (is_array($value)) {
- unset($this->temp_metadata[$name]);
- remove_metadata($this->getGUID(), $name);
- foreach ($value as $v) {
- if ((int) $this->guid > 0) {
- $multiple = true;
- if (!create_metadata($this->getGUID(), $name, $v, $value_type,
- $this->getOwner(), $this->getAccessID(), $multiple)) {
- return false;
- }
- } else {
- if (($multiple) && (isset($this->temp_metadata[$name]))) {
- if (!is_array($this->temp_metadata[$name])) {
- $tmp = $this->temp_metadata[$name];
- $this->temp_metadata[$name] = array();
- $this->temp_metadata[$name][] = $tmp;
- }
-
- $this->temp_metadata[$name][] = $value;
- }
- else {
- $this->temp_metadata[$name] = $value;
- }
- }
- }
-
- return true;
- } else {
- unset($this->temp_metadata[$name]);
- if ((int) $this->guid > 0) {
- return create_metadata($this->getGUID(), $name, $value, $value_type, $this->getOwner(), $this->getAccessID(), $multiple);
- } else {
- if (($multiple) && (isset($this->temp_metadata[$name]))) {
- if (!is_array($this->temp_metadata[$name])) {
- $tmp = $this->temp_metadata[$name];
- $this->temp_metadata[$name] = array();
- $this->temp_metadata[$name][] = $tmp;
- }
-
- $this->temp_metadata[$name][] = $value;
- }
- else {
- $this->temp_metadata[$name] = $value;
- }
-
- return true;
- }
- }
- }
-
- /**
- * Clear metadata.
- */
- public function clearMetaData($name = "") {
- if (empty($name)) {
- return clear_metadata($this->getGUID());
- } else {
- return remove_metadata($this->getGUID(),$name);
- }
- }
-
-
- /**
- * Get a piece of volatile (non-persisted) data on this entity
- */
- public function getVolatileData($name) {
- if (!is_array($this->volatile)) {
- $this->volatile = array();
- }
-
- if (array_key_exists($name, $this->volatile)) {
- return $this->volatile[$name];
- } else {
- return NULL;
- }
- }
-
-
- /**
- * Set a piece of volatile (non-persisted) data on this entity
- */
- public function setVolatileData($name, $value) {
- if (!is_array($this->volatile)) {
- $this->volatile = array();
- }
-
- $this->volatile[$name] = $value;
- }
-
-
- /**
- * Remove all entities associated with this entity
- *
- * @return true
- */
- public function clearRelationships() {
- remove_entity_relationships($this->getGUID());
- remove_entity_relationships($this->getGUID(),"",true);
- return true;
- }
-
- /**
- * Add a relationship.
- *
- * @param int $guid Relationship to link to.
- * @param string $relationship The type of relationship.
- * @return bool
- */
- public function addRelationship($guid, $relationship) {
- return add_entity_relationship($this->getGUID(), $relationship, $guid);
- }
-
- /**
- * Remove a relationship
- *
- * @param int $guid
- * @param str $relationship
- * @return bool
- */
- public function removeRelationship($guid, $relationship) {
- return remove_entity_relationship($this->getGUID(), $relationship, $guid);
- }
-
- /**
- * Adds a private setting to this entity.
- *
- * @param $name
- * @param $value
- * @return unknown_type
- */
- function setPrivateSetting($name, $value) {
- return set_private_setting($this->getGUID(), $name, $value);
- }
-
- /**
- * Gets private setting for this entity
- *
- * @param $name
- * @return unknown_type
- */
- function getPrivateSetting($name) {
- return get_private_setting($this->getGUID(), $name);
- }
-
- /**
- * Removes private setting for this entity.
- *
- * @param $name
- * @return unknown_type
- */
- function removePrivateSetting($name) {
- return remove_private_setting($this->getGUID(), $name);
- }
-
- /**
- * Adds an annotation to an entity. By default, the type is detected automatically; however,
- * it can also be set. Note that by default, annotations are private.
- *
- * @param string $name
- * @param mixed $value
- * @param int $access_id
- * @param int $owner_id
- * @param string $vartype
- */
- function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_id = 0, $vartype = "") {
- if ((int) $this->guid > 0) {
- return create_annotation($this->getGUID(), $name, $value, $vartype, $owner_id, $access_id);
- } else {
- $this->temp_annotations[$name] = $value;
- }
- return true;
- }
-
- /**
- * Get the annotations for an entity.
- *
- * @param string $name
- * @param int $limit
- * @param int $offset
- * @param string $order
- */
- function getAnnotations($name, $limit = 50, $offset = 0, $order="asc") {
- if ((int) ($this->guid) > 0) {
- return get_annotations($this->getGUID(), "", "", $name, "", 0, $limit, $offset, $order);
- } else {
- return $this->temp_annotations[$name];
- }
- }
-
- /**
- * Remove all annotations or all annotations for this entity.
- *
- * @param string $name
- */
- function clearAnnotations($name = "") {
- return clear_annotations($this->getGUID(), $name);
- }
-
- /**
- * Return the annotations for the entity.
- *
- * @param string $name The type of annotation.
- */
- function countAnnotations($name = "") {
- return count_annotations($this->getGUID(), "", "", $name);
- }
-
- /**
- * Get the average of an integer type annotation.
- *
- * @param string $name
- */
- function getAnnotationsAvg($name) {
- return get_annotations_avg($this->getGUID(), "", "", $name);
- }
-
- /**
- * Get the sum of integer type annotations of a given name.
- *
- * @param string $name
- */
- function getAnnotationsSum($name) {
- return get_annotations_sum($this->getGUID(), "", "", $name);
- }
-
- /**
- * Get the minimum of integer type annotations of given name.
- *
- * @param string $name
- */
- function getAnnotationsMin($name) {
- return get_annotations_min($this->getGUID(), "", "", $name);
- }
-
- /**
- * Get the maximum of integer type annotations of a given name.
- *
- * @param string $name
- */
- function getAnnotationsMax($name) {
- return get_annotations_max($this->getGUID(), "", "", $name);
- }
-
- /**
- * Gets an array of entities from a specific relationship type
- *
- * @param string $relationship Relationship type (eg "friends")
- * @param true|false $inverse Is this an inverse relationship?
- * @param int $limit Number of elements to return
- * @param int $offset Indexing offset
- * @return array|false An array of entities or false on failure
- */
- function getEntitiesFromRelationship($relationship, $inverse = false, $limit = 50, $offset = 0) {
- return elgg_get_entities_from_relationship(array(
- 'relationship' => $relationship,
- 'relationship_guid' => $this->getGUID(),
- 'inverse_relationship' => $inverse,
- 'limit' => $limit,
- 'offset' => $offset
- ));
- }
-
- /**
- * Gets the number of of entities from a specific relationship type
- *
- * @param string $relationship Relationship type (eg "friends")
- * @param bool $inverse_relationship
- * @return int|false The number of entities or false on failure
- */
- function countEntitiesFromRelationship($relationship, $inverse_relationship = FALSE) {
- return elgg_get_entities_from_relationship(array(
- 'relationship' => $relationship,
- 'relationship_guid' => $this->getGUID(),
- 'inverse_relationship' => $inverse_relationship,
- 'count' => TRUE
- ));
- }
-
- /**
- * Determines whether or not the specified user (by default the current one) can edit the entity
- *
- * @param int $user_guid The user GUID, optionally (defaults to the currently logged in user)
- * @return true|false
- */
- function canEdit($user_guid = 0) {
- return can_edit_entity($this->getGUID(), $user_guid);
- }
-
- /**
- * Determines whether or not the specified user (by default the current one) can edit metadata on the entity
- *
- * @param ElggMetadata $metadata The piece of metadata to specifically check
- * @param int $user_guid The user GUID, optionally (defaults to the currently logged in user)
- * @return true|false
- */
- function canEditMetadata($metadata = null, $user_guid = 0) {
- return can_edit_entity_metadata($this->getGUID(), $user_guid, $metadata);
- }
-
- /**
- * Returns whether the given user (or current user) has the ability to write to this container.
- *
- * @param int $user_guid The user.
- * @return bool
- */
- public function canWriteToContainer($user_guid = 0) {
- return can_write_to_container($user_guid, $this->getGUID());
- }
-
- /**
- * Obtain this entity's access ID
- *
- * @return int The access ID
- */
- public function getAccessID() {
- return $this->get('access_id');
- }
-
- /**
- * Obtain this entity's GUID
- *
- * @return int GUID
- */
- public function getGUID() {
- return $this->get('guid');
- }
-
- /**
- * Get the owner of this entity
- *
- * @return int The owner GUID
- */
- public function getOwner() {
- return $this->get('owner_guid');
- }
-
- /**
- * Returns the actual entity of the user who owns this entity, if any
- *
- * @return ElggEntity The owning user
- */
- public function getOwnerEntity() {
- return get_entity($this->get('owner_guid'));
- }
-
- /**
- * Gets the type of entity this is
- *
- * @return string Entity type
- */
- public function getType() {
- return $this->get('type');
- }
-
- /**
- * Returns the subtype of this entity
- *
- * @return string The entity subtype
- */
- public function getSubtype() {
- // If this object hasn't been saved, then return the subtype string.
- if (!((int) $this->guid > 0)) {
- return $this->get('subtype');
- }
-
- return get_subtype_from_id($this->get('subtype'));
- }
-
- /**
- * Gets the UNIX epoch time that this entity was created
- *
- * @return int UNIX epoch time
- */
- public function getTimeCreated() {
- return $this->get('time_created');
- }
-
- /**
- * Gets the UNIX epoch time that this entity was last updated
- *
- * @return int UNIX epoch time
- */
- public function getTimeUpdated() {
- return $this->get('time_updated');
- }
-
- /**
- * Gets the display URL for this entity
- *
- * @return string The URL
- */
- public function getURL() {
- if (!empty($this->url_override)) {
- return $this->url_override;
- }
- return get_entity_url($this->getGUID());
- }
-
- /**
- * Overrides the URL returned by getURL
- *
- * @param string $url The new item URL
- * @return string The URL
- */
- public function setURL($url) {
- $this->url_override = $url;
- return $url;
- }
-
- /**
- * Return a url for the entity's icon, trying multiple alternatives.
- *
- * @param string $size Either 'large','medium','small' or 'tiny'
- * @return string The url or false if no url could be worked out.
- */
- public function getIcon($size = 'medium') {
- if (isset($this->icon_override[$size])) {
- return $this->icon_override[$size];
- }
- return get_entity_icon_url($this, $size);
- }
-
- /**
- * Set an icon override for an icon and size.
- *
- * @param string $url The url of the icon.
- * @param string $size The size its for.
- * @return bool
- */
- public function setIcon($url, $size = 'medium') {
- $url = sanitise_string($url);
- $size = sanitise_string($size);
-
- if (!$this->icon_override) {
- $this->icon_override = array();
- }
- $this->icon_override[$size] = $url;
-
- return true;
- }
-
- /**
- * Tests to see whether the object has been fully loaded.
- *
- * @return bool
- */
- public function isFullyLoaded() {
- return ! ($this->attributes['tables_loaded'] < $this->attributes['tables_split']);
- }
-
- /**
- * Save generic attributes to the entities table.
- */
- public function save() {
- $guid = (int) $this->guid;
- if ($guid > 0) {
- cache_entity($this);
-
- return update_entity(
- $this->get('guid'),
- $this->get('owner_guid'),
- $this->get('access_id'),
- $this->get('container_guid')
- );
- } else {
- // Create a new entity (nb: using attribute array directly 'cos set function does something special!)
- $this->attributes['guid'] = create_entity($this->attributes['type'], $this->attributes['subtype'], $this->attributes['owner_guid'], $this->attributes['access_id'], $this->attributes['site_guid'], $this->attributes['container_guid']);
- if (!$this->attributes['guid']) {
- throw new IOException(elgg_echo('IOException:BaseEntitySaveFailed'));
- }
-
- // Save any unsaved metadata TODO: How to capture extra information (access id etc)
- if (sizeof($this->temp_metadata) > 0) {
- foreach($this->temp_metadata as $name => $value) {
- $this->$name = $value;
- unset($this->temp_metadata[$name]);
- }
- }
-
- // Save any unsaved annotations metadata. TODO: How to capture extra information (access id etc)
- if (sizeof($this->temp_annotations) > 0) {
- foreach($this->temp_annotations as $name => $value) {
- $this->annotate($name, $value);
- unset($this->temp_annotations[$name]);
- }
- }
-
- // set the subtype to id now rather than a string
- $this->attributes['subtype'] = get_subtype_id($this->attributes['type'], $this->attributes['subtype']);
-
- // Cache object handle
- if ($this->attributes['guid']) cache_entity($this);
-
- return $this->attributes['guid'];
- }
- }
-
- /**
- * Load the basic entity information and populate base attributes array.
- *
- * @param int $guid
- */
- protected function load($guid) {
- $row = get_entity_as_row($guid);
-
- if ($row) {
- // Create the array if necessary - all subclasses should test before creating
- if (!is_array($this->attributes)) {
- $this->attributes = array();
- }
-
- // Now put these into the attributes array as core values
- $objarray = (array) $row;
- foreach($objarray as $key => $value) {
- $this->attributes[$key] = $value;
- }
-
- // Increment the portion counter
- if (!$this->isFullyLoaded()) {
- $this->attributes['tables_loaded']++;
- }
+global $ENTITY_CACHE;
+$ENTITY_CACHE = array();
- // Cache object handle
- if ($this->attributes['guid']) {
- cache_entity($this);
- }
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Disable this entity.
- *
- * @param string $reason Optional reason
- * @param bool $recursive Recursively disable all contained entities?
- */
- public function disable($reason = "", $recursive = true) {
- return disable_entity($this->get('guid'), $reason, $recursive);
- }
-
- /**
- * Re-enable this entity.
- */
- public function enable() {
- return enable_entity($this->get('guid'));
- }
-
- /**
- * Is this entity enabled?
- *
- * @return boolean
- */
- public function isEnabled() {
- if ($this->enabled == 'yes') {
- return true;
- }
-
- return false;
- }
-
- /**
- * Delete this entity.
- */
- public function delete() {
- return delete_entity($this->get('guid'));
- }
-
- // LOCATABLE INTERFACE /////////////////////////////////////////////////////////////
-
- /** Interface to set the location */
- public function setLocation($location) {
- $location = sanitise_string($location);
-
- $this->location = $location;
-
- return true;
- }
-
- /**
- * Set latitude and longitude tags for a given entity.
- *
- * @param float $lat
- * @param float $long
- */
- public function setLatLong($lat, $long) {
- $lat = sanitise_string($lat);
- $long = sanitise_string($long);
-
- $this->set('geo:lat', $lat);
- $this->set('geo:long', $long);
-
- return true;
- }
-
- /**
- * Get the contents of the ->geo:lat field.
- *
- */
- public function getLatitude() {
- return $this->get('geo:lat');
- }
-
- /**
- * Get the contents of the ->geo:lat field.
- *
- */
- public function getLongitude() {
- return $this->get('geo:long');
- }
-
- /**
- * Get the ->location metadata.
- *
- */
- public function getLocation() {
- return $this->get('location');
- }
-
- // NOTABLE INTERFACE ///////////////////////////////////////////////////////////////
-
- /**
- * Calendar functionality.
- * This function sets the time of an object on a calendar listing.
- *
- * @param int $hour If ommitted, now is assumed.
- * @param int $minute If ommitted, now is assumed.
- * @param int $second If ommitted, now is assumed.
- * @param int $day If ommitted, now is assumed.
- * @param int $month If ommitted, now is assumed.
- * @param int $year If ommitted, now is assumed.
- * @param int $duration Duration of event, remainder of the day is assumed.
- */
- public function setCalendarTimeAndDuration($hour = NULL, $minute = NULL, $second = NULL, $day = NULL, $month = NULL, $year = NULL, $duration = NULL) {
- $start = mktime($hour, $minute, $second, $month, $day, $year);
- $end = $start + abs($duration);
- if (!$duration) {
- $end = get_day_end($day,$month,$year);
- }
-
- $this->calendar_start = $start;
- $this->calendar_end = $end;
-
- return true;
- }
-
- /**
- * Return the start timestamp.
- */
- public function getCalendarStartTime() {
- return (int)$this->calendar_start;
- }
-
- /**
- * Return the end timestamp.
- */
- public function getCalendarEndTime() {
- return (int)$this->calendar_end;
- }
-
- // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an array of fields which can be exported.
- */
- public function getExportableValues() {
- return array(
- 'guid',
- 'type',
- 'subtype',
- 'time_created',
- 'time_updated',
- 'container_guid',
- 'owner_guid',
- 'site_guid'
- );
- }
-
- /**
- * Export this class into an array of ODD Elements containing all necessary fields.
- * Override if you wish to return more information than can be found in $this->attributes (shouldn't happen)
- */
- public function export() {
- $tmp = array();
-
- // Generate uuid
- $uuid = guid_to_uuid($this->getGUID());
-
- // Create entity
- $odd = new ODDEntity(
- $uuid,
- $this->attributes['type'],
- get_subtype_from_id($this->attributes['subtype'])
- );
-
- $tmp[] = $odd;
-
- $exportable_values = $this->getExportableValues();
-
- // Now add its attributes
- foreach ($this->attributes as $k => $v) {
- $meta = NULL;
-
- if (in_array( $k, $exportable_values)) {
- switch ($k) {
- case 'guid' : // Dont use guid in OpenDD
- case 'type' : // Type and subtype already taken care of
- case 'subtype' :
- break;
-
- case 'time_created' : // Created = published
- $odd->setAttribute('published', date("r", $v));
- break;
-
- case 'site_guid' : // Container
- $k = 'site_uuid';
- $v = guid_to_uuid($v);
- $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
- break;
-
- case 'container_guid' : // Container
- $k = 'container_uuid';
- $v = guid_to_uuid($v);
- $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
- break;
-
- case 'owner_guid' : // Convert owner guid to uuid, this will be stored in metadata
- $k = 'owner_uuid';
- $v = guid_to_uuid($v);
- $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
- break;
-
- default :
- $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
- }
-
- // set the time of any metadata created
- if ($meta) {
- $meta->setAttribute('published', date("r",$this->time_created));
- $tmp[] = $meta;
- }
- }
- }
-
- // Now we do something a bit special.
- /*
- * This provides a rendered view of the entity to foreign sites.
- */
-
- elgg_set_viewtype('default');
- $view = elgg_view_entity($this, true);
- elgg_set_viewtype();
-
- $tmp[] = new ODDMetaData($uuid . "volatile/renderedentity/", $uuid, 'renderedentity', $view , 'volatile');
-
- return $tmp;
- }
-
- // IMPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Import data from an parsed xml data array.
- *
- * @param array $data
- * @param int $version
- */
- public function import(ODD $data) {
- if (!($data instanceof ODDEntity)) {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnexpectedODDClass'));
- }
-
- // Set type and subtype
- $this->attributes['type'] = $data->getAttribute('class');
- $this->attributes['subtype'] = $data->getAttribute('subclass');
-
- // Set owner
- $this->attributes['owner_guid'] = get_loggedin_userid(); // Import as belonging to importer.
-
- // Set time
- $this->attributes['time_created'] = strtotime($data->getAttribute('published'));
- $this->attributes['time_updated'] = time();
-
- return true;
- }
-
- // SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an identification for the object for storage in the system log.
- * This id must be an integer.
- *
- * @return int
- */
- public function getSystemLogID() {
- return $this->getGUID();
- }
-
- /**
- * Return the class name of the object.
- */
- public function getClassName() {
- return get_class($this);
- }
-
- /**
- * For a given ID, return the object associated with it.
- * This is used by the river functionality primarily.
- * This is useful for checking access permissions etc on objects.
- */
- public function getObjectFromID($id) {
- return get_entity($id);
- }
-
- /**
- * Return the GUID of the owner of this object.
- */
- public function getObjectOwnerGUID() {
- return $this->owner_guid;
- }
-
- /**
- * Returns tags for this entity.
- *
- * @param array $tag_names Optionally restrict by tag metadata names.
- * @return array
- */
- public function getTags($tag_names = NULL) {
- global $CONFIG;
-
- if ($tag_names && !is_array($tag_names)) {
- $tag_names = array($tag_names);
- }
-
- $valid_tags = elgg_get_registered_tag_metadata_names();
- $entity_tags = array();
-
- foreach ($valid_tags as $tag_name) {
- if (is_array($tag_names) && !in_array($tag_name, $tag_names)) {
- continue;
- }
-
- if ($tags = $this->$tag_name) {
- // if a single tag, metadata returns a string.
- // if multiple tags, metadata returns an array.
- if (is_array($tags)) {
- $entity_tags = array_merge($entity_tags, $tags);
- } else {
- $entity_tags[] = $tags;
- }
- }
- }
-
- return $entity_tags;
- }
-
- // ITERATOR INTERFACE //////////////////////////////////////////////////////////////
- /*
- * This lets an entity's attributes be displayed using foreach as a normal array.
- * Example: http://www.sitepoint.com/print/php5-standard-library
- */
-
- private $valid = FALSE;
-
- function rewind() {
- $this->valid = (FALSE !== reset($this->attributes));
- }
-
- function current() {
- return current($this->attributes);
- }
-
- function key() {
- return key($this->attributes);
- }
-
- function next() {
- $this->valid = (FALSE !== next($this->attributes));
- }
-
- function valid() {
- return $this->valid;
- }
-
- // ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
- /*
- * This lets an entity's attributes be accessed like an associative array.
- * Example: http://www.sitepoint.com/print/php5-standard-library
- */
-
- function offsetSet($key, $value) {
- if ( array_key_exists($key, $this->attributes) ) {
- $this->attributes[$key] = $value;
- }
- }
+/**
+ * GUIDs of entities banned from the entity cache (during this request)
+ *
+ * @global array $ENTITY_CACHE_DISABLED_GUIDS
+ * @access private
+ */
+global $ENTITY_CACHE_DISABLED_GUIDS;
+$ENTITY_CACHE_DISABLED_GUIDS = array();
- function offsetGet($key) {
- if ( array_key_exists($key, $this->attributes) ) {
- return $this->attributes[$key];
- }
- }
+/**
+ * Cache subtypes and related class names.
+ *
+ * @global array|null $SUBTYPE_CACHE array once populated from DB, initially null
+ * @access private
+ */
+global $SUBTYPE_CACHE;
+$SUBTYPE_CACHE = null;
- function offsetUnset($key) {
- if ( array_key_exists($key, $this->attributes) ) {
- $this->attributes[$key] = ""; // Full unsetting is dangerious for our objects
- }
- }
+/**
+ * Remove this entity from the entity cache and make sure it is not re-added
+ *
+ * @param int $guid The entity guid
+ *
+ * @access private
+ * @todo this is a workaround until #5604 can be implemented
+ */
+function _elgg_disable_caching_for_entity($guid) {
+ global $ENTITY_CACHE_DISABLED_GUIDS;
- function offsetExists($offset) {
- return array_key_exists($offset, $this->attributes);
- }
+ _elgg_invalidate_cache_for_entity($guid);
+ $ENTITY_CACHE_DISABLED_GUIDS[$guid] = true;
}
/**
- * Initialise the entity cache.
+ * Allow this entity to be stored in the entity cache
+ *
+ * @param int $guid The entity guid
+ *
+ * @access private
*/
-function initialise_entity_cache() {
- global $ENTITY_CACHE;
+function _elgg_enable_caching_for_entity($guid) {
+ global $ENTITY_CACHE_DISABLED_GUIDS;
- if (!$ENTITY_CACHE) {
- //select_default_memcache('entity_cache'); // TODO: Replace with memcache?
- $ENTITY_CACHE = array();
- }
+ unset($ENTITY_CACHE_DISABLED_GUIDS[$guid]);
}
/**
- * Invalidate this class' entry in the cache.
+ * Invalidate this class's entry in the cache.
*
- * @param int $guid The guid
+ * @param int $guid The entity guid
+ *
+ * @return void
+ * @access private
*/
-function invalidate_cache_for_entity($guid) {
+function _elgg_invalidate_cache_for_entity($guid) {
global $ENTITY_CACHE;
$guid = (int)$guid;
unset($ENTITY_CACHE[$guid]);
- //$ENTITY_CACHE->delete($guid);
+
+ elgg_get_metadata_cache()->clear($guid);
}
/**
* Cache an entity.
*
+ * Stores an entity in $ENTITY_CACHE;
+ *
* @param ElggEntity $entity Entity to cache
+ *
+ * @return void
+ * @see _elgg_retrieve_cached_entity()
+ * @see _elgg_invalidate_cache_for_entity()
+ * @access private
+ * @todo Use an ElggCache object
*/
-function cache_entity(ElggEntity $entity) {
- global $ENTITY_CACHE;
+function _elgg_cache_entity(ElggEntity $entity) {
+ global $ENTITY_CACHE, $ENTITY_CACHE_DISABLED_GUIDS;
+
+ // Don't cache non-plugin entities while access control is off, otherwise they could be
+ // exposed to users who shouldn't see them when control is re-enabled.
+ if (!($entity instanceof ElggPlugin) && elgg_get_ignore_access()) {
+ return;
+ }
+
+ $guid = $entity->getGUID();
+ if (isset($ENTITY_CACHE_DISABLED_GUIDS[$guid])) {
+ return;
+ }
+
+ // Don't store too many or we'll have memory problems
+ // @todo Pick a less arbitrary limit
+ if (count($ENTITY_CACHE) > 256) {
+ $random_guid = array_rand($ENTITY_CACHE);
- $ENTITY_CACHE[$entity->guid] = $entity;
+ unset($ENTITY_CACHE[$random_guid]);
+
+ // Purge separate metadata cache. Original idea was to do in entity destructor, but that would
+ // have caused a bunch of unnecessary purges at every shutdown. Doing it this way we have no way
+ // to know that the expunged entity will be GCed (might be another reference living), but that's
+ // OK; the metadata will reload if necessary.
+ elgg_get_metadata_cache()->clear($random_guid);
+ }
+
+ $ENTITY_CACHE[$guid] = $entity;
}
/**
* Retrieve a entity from the cache.
*
* @param int $guid The guid
+ *
+ * @return ElggEntity|bool false if entity not cached, or not fully loaded
+ * @see _elgg_cache_entity()
+ * @see _elgg_invalidate_cache_for_entity()
+ * @access private
*/
-function retrieve_cached_entity($guid) {
+function _elgg_retrieve_cached_entity($guid) {
global $ENTITY_CACHE;
- $guid = (int)$guid;
-
if (isset($ENTITY_CACHE[$guid])) {
if ($ENTITY_CACHE[$guid]->isFullyLoaded()) {
return $ENTITY_CACHE[$guid];
@@ -1275,191 +147,243 @@ function retrieve_cached_entity($guid) {
}
/**
- * As retrieve_cached_entity, but returns the result as a stdClass (compatible with load functions that
- * expect a database row.)
+ * Return the id for a given subtype.
*
- * @param int $guid The guid
- */
-function retrieve_cached_entity_row($guid) {
- $obj = retrieve_cached_entity($guid);
- if ($obj) {
- $tmp = new stdClass;
-
- foreach ($obj as $k => $v) {
- $tmp->$k = $v;
- }
-
- return $tmp;
- }
-
- return false;
-}
-
-/**
- * Return the integer ID for a given subtype, or false.
+ * ElggEntity objects have a type and a subtype. Subtypes
+ * are defined upon creation and cannot be changed.
*
- * TODO: Move to a nicer place?
+ * Plugin authors generally don't need to use this function
+ * unless writing their own SQL queries. Use {@link ElggEntity::getSubtype()}
+ * to return the string subtype.
*
- * @param string $type
- * @param string $subtype
+ * @warning {@link ElggEntity::subtype} returns the ID. You probably want
+ * {@link ElggEntity::getSubtype()} instead!
+ *
+ * @internal Subtypes are stored in the entity_subtypes table. There is a foreign
+ * key in the entities table.
+ *
+ * @param string $type Type
+ * @param string $subtype Subtype
+ *
+ * @return int Subtype ID
+ * @link http://docs.elgg.org/DataModel/Entities/Subtypes
+ * @see get_subtype_from_id()
+ * @access private
*/
function get_subtype_id($type, $subtype) {
- global $CONFIG, $SUBTYPE_CACHE;
-
- $type = sanitise_string($type);
- $subtype = sanitise_string($subtype);
+ global $SUBTYPE_CACHE;
- if ($subtype=="") {
- //return $subtype;
- return FALSE;
+ if (!$subtype) {
+ return false;
}
- // Todo: cache here? Or is looping less efficient that going to the db each time?
- $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes
- where type='$type' and subtype='$subtype'");
-
- if ($result) {
- if (!$SUBTYPE_CACHE) {
- //select_default_memcache('subtype_cache');
- $SUBTYPE_CACHE = array();
- }
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
+ }
- $SUBTYPE_CACHE[$result->id] = $result;
+ // use the cache before hitting database
+ $result = _elgg_retrieve_cached_subtype($type, $subtype);
+ if ($result !== null) {
return $result->id;
}
- return FALSE;
+ return false;
}
/**
- * For a given subtype ID, return its identifier text.
+ * Return string name for a given subtype ID.
*
- * TODO: Move to a nicer place?
+ * @param int $subtype_id Subtype ID
*
- * @param int $subtype_id
+ * @return string|false Subtype name, false if subtype not found
+ * @link http://docs.elgg.org/DataModel/Entities/Subtypes
+ * @see get_subtype_from_id()
+ * @access private
*/
function get_subtype_from_id($subtype_id) {
- global $CONFIG, $SUBTYPE_CACHE;
-
- $subtype_id = (int)$subtype_id;
+ global $SUBTYPE_CACHE;
if (!$subtype_id) {
return false;
}
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
+ }
+
if (isset($SUBTYPE_CACHE[$subtype_id])) {
return $SUBTYPE_CACHE[$subtype_id]->subtype;
}
- $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes where id=$subtype_id");
- if ($result) {
- if (!$SUBTYPE_CACHE) {
- //select_default_memcache('subtype_cache');
- $SUBTYPE_CACHE = array();
- }
+ return false;
+}
+
+/**
+ * Retrieve subtype from the cache.
+ *
+ * @param string $type
+ * @param string $subtype
+ * @return stdClass|null
+ *
+ * @access private
+ */
+function _elgg_retrieve_cached_subtype($type, $subtype) {
+ global $SUBTYPE_CACHE;
- $SUBTYPE_CACHE[$subtype_id] = $result;
- return $result->subtype;
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
}
- return false;
+ foreach ($SUBTYPE_CACHE as $obj) {
+ if ($obj->type === $type && $obj->subtype === $subtype) {
+ return $obj;
+ }
+ }
+ return null;
}
/**
- * This function tests to see if a subtype has a registered class handler.
+ * Fetch all suptypes from DB to local cache.
*
- * @param string $type The type
- * @param string $subtype The subtype
- * @return a class name or null
+ * @access private
*/
-function get_subtype_class($type, $subtype) {
+function _elgg_populate_subtype_cache() {
global $CONFIG, $SUBTYPE_CACHE;
+
+ $results = get_data("SELECT * FROM {$CONFIG->dbprefix}entity_subtypes");
+
+ $SUBTYPE_CACHE = array();
+ foreach ($results as $row) {
+ $SUBTYPE_CACHE[$row->id] = $row;
+ }
+}
- $type = sanitise_string($type);
- $subtype = sanitise_string($subtype);
-
- // Todo: cache here? Or is looping less efficient that going to the db each time?
- $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes
- where type='$type' and subtype='$subtype'");
-
- if ($result) {
- if (!$SUBTYPE_CACHE) {
- //select_default_memcache('subtype_cache');
- $SUBTYPE_CACHE = array();
- }
+/**
+ * Return the class name for a registered type and subtype.
+ *
+ * Entities can be registered to always be loaded as a certain class
+ * with add_subtype() or update_subtype(). This function returns the class
+ * name if found and NULL if not.
+ *
+ * @param string $type The type
+ * @param string $subtype The subtype
+ *
+ * @return string|null a class name or null
+ * @see get_subtype_from_id()
+ * @see get_subtype_class_from_id()
+ * @access private
+ */
+function get_subtype_class($type, $subtype) {
+ global $SUBTYPE_CACHE;
- $SUBTYPE_CACHE[$result->id] = $result;
- return $result->class;
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
+ }
+
+ // use the cache before going to the database
+ $obj = _elgg_retrieve_cached_subtype($type, $subtype);
+ if ($obj) {
+ return $obj->class;
}
- return NULL;
+ return null;
}
/**
- * This function tests to see if a subtype has a registered class handler by its id.
+ * Returns the class name for a subtype id.
+ *
+ * @param int $subtype_id The subtype id
*
- * @param int $subtype_id The subtype
- * @return a class name or null
+ * @return string|null
+ * @see get_subtype_class()
+ * @see get_subtype_from_id()
+ * @access private
*/
function get_subtype_class_from_id($subtype_id) {
- global $CONFIG, $SUBTYPE_CACHE;
-
- $subtype_id = (int)$subtype_id;
+ global $SUBTYPE_CACHE;
if (!$subtype_id) {
- return false;
+ return null;
}
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
+ }
+
if (isset($SUBTYPE_CACHE[$subtype_id])) {
return $SUBTYPE_CACHE[$subtype_id]->class;
}
- $result = get_data_row("SELECT * from {$CONFIG->dbprefix}entity_subtypes where id=$subtype_id");
-
- if ($result) {
- if (!$SUBTYPE_CACHE) {
- //select_default_memcache('subtype_cache');
- $SUBTYPE_CACHE = array();
- }
- $SUBTYPE_CACHE[$subtype_id] = $result;
- return $result->class;
- }
-
- return NULL;
+ return null;
}
/**
- * This function will register a new subtype, returning its ID as required.
+ * Register ElggEntities with a certain type and subtype to be loaded as a specific class.
+ *
+ * By default entities are loaded as one of the 4 parent objects: site, user, object, or group.
+ * If you subclass any of these you can register the classname with add_subtype() so
+ * it will be loaded as that class automatically when retrieved from the database with
+ * {@link get_entity()}.
*
- * @param string $type The type you're subtyping
- * @param string $subtype The subtype label
- * @param string $class Optional class handler (if you don't want it handled by the generic elgg handler for the type)
+ * @warning This function cannot be used to change the class for a type-subtype pair.
+ * Use update_subtype() for that.
+ *
+ * @param string $type The type you're subtyping (site, user, object, or group)
+ * @param string $subtype The subtype
+ * @param string $class Optional class name for the object
+ *
+ * @return int
+ * @link http://docs.elgg.org/Tutorials/Subclasses
+ * @link http://docs.elgg.org/DataModel/Entities
+ * @see update_subtype()
+ * @see remove_subtype()
+ * @see get_entity()
*/
function add_subtype($type, $subtype, $class = "") {
- global $CONFIG;
- $type = sanitise_string($type);
- $subtype = sanitise_string($subtype);
- $class = sanitise_string($class);
+ global $CONFIG, $SUBTYPE_CACHE;
- // Short circuit if no subtype is given
- if ($subtype == "") {
+ if (!$subtype) {
return 0;
}
$id = get_subtype_id($type, $subtype);
- if ($id==0) {
- return insert_data("insert into {$CONFIG->dbprefix}entity_subtypes (type, subtype, class) values ('$type','$subtype','$class')");
+ if (!$id) {
+ // In cache we store non-SQL-escaped strings because that's what's returned by query
+ $cache_obj = (object) array(
+ 'type' => $type,
+ 'subtype' => $subtype,
+ 'class' => $class,
+ );
+
+ $type = sanitise_string($type);
+ $subtype = sanitise_string($subtype);
+ $class = sanitise_string($class);
+
+ $id = insert_data("INSERT INTO {$CONFIG->dbprefix}entity_subtypes"
+ . " (type, subtype, class) VALUES ('$type', '$subtype', '$class')");
+
+ // add entry to cache
+ $cache_obj->id = $id;
+ $SUBTYPE_CACHE[$id] = $cache_obj;
}
return $id;
}
/**
- * Removes a registered subtype
+ * Removes a registered ElggEntity type, subtype, and classname.
*
- * @param string $type
- * @param string $subtype
+ * @warning You do not want to use this function. If you want to unregister
+ * a class for a subtype, use update_subtype(). Using this function will
+ * permanently orphan all the objects created with the specified subtype.
+ *
+ * @param string $type Type
+ * @param string $subtype Subtype
+ *
+ * @return bool
+ * @see add_subtype()
+ * @see update_subtype()
*/
function remove_subtype($type, $subtype) {
global $CONFIG;
@@ -1467,41 +391,68 @@ function remove_subtype($type, $subtype) {
$type = sanitise_string($type);
$subtype = sanitise_string($subtype);
- return delete_data("DELETE FROM {$CONFIG->dbprefix}entity_subtypes WHERE type = '$type' AND subtype = '$subtype'");
+ return delete_data("DELETE FROM {$CONFIG->dbprefix}entity_subtypes"
+ . " WHERE type = '$type' AND subtype = '$subtype'");
}
/**
- * Update the registered information
+ * Update a registered ElggEntity type, subtype, and class name
*
- * @param string $type
- * @param string $subtype
- * @param string $class
+ * @param string $type Type
+ * @param string $subtype Subtype
+ * @param string $class Class name to use when loading this entity
+ *
+ * @return bool
*/
function update_subtype($type, $subtype, $class = '') {
- global $CONFIG;
+ global $CONFIG, $SUBTYPE_CACHE;
- if (!$id = get_subtype_id($type, $subtype)) {
- return FALSE;
+ $id = get_subtype_id($type, $subtype);
+ if (!$id) {
+ return false;
+ }
+
+ if ($SUBTYPE_CACHE === null) {
+ _elgg_populate_subtype_cache();
}
+
+ $unescaped_class = $class;
+
$type = sanitise_string($type);
$subtype = sanitise_string($subtype);
-
- return update_data("UPDATE {$CONFIG->dbprefix}entity_subtypes
+ $class = sanitise_string($class);
+
+ $success = update_data("UPDATE {$CONFIG->dbprefix}entity_subtypes
SET type = '$type', subtype = '$subtype', class = '$class'
WHERE id = $id
");
-}
+ if ($success && isset($SUBTYPE_CACHE[$id])) {
+ $SUBTYPE_CACHE[$id]->class = $unescaped_class;
+ }
+
+ return $success;
+}
/**
- * Update an existing entity.
+ * Update an entity in the database.
+ *
+ * There are 4 basic entity types: site, user, object, and group.
+ * All entities are split between two tables: the entities table and their type table.
+ *
+ * @warning Plugin authors should never call this directly. Use ->save() instead.
*
- * @param int $guid
- * @param int $owner_guid
- * @param int $access_id
- * @param int $container_guid
+ * @param int $guid The guid of the entity to update
+ * @param int $owner_guid The new owner guid
+ * @param int $access_id The new access id
+ * @param int $container_guid The new container guid
+ * @param int $time_created The time creation timestamp
+ *
+ * @return bool
+ * @throws InvalidParameterException
+ * @access private
*/
-function update_entity($guid, $owner_guid, $access_id, $container_guid = null) {
+function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $time_created = null) {
global $CONFIG, $ENTITY_CACHE;
$guid = (int)$guid;
@@ -1515,12 +466,25 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null) {
$entity = get_entity($guid);
- if ($entity->canEdit()) {
- if (trigger_elgg_event('update',$entity->type,$entity)) {
- $ret = update_data("UPDATE {$CONFIG->dbprefix}entities set owner_guid='$owner_guid', access_id='$access_id', container_guid='$container_guid', time_updated='$time' WHERE guid=$guid");
+ if ($time_created == null) {
+ $time_created = $entity->time_created;
+ } else {
+ $time_created = (int) $time_created;
+ }
+
+ if ($access_id == ACCESS_DEFAULT) {
+ throw new InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h');
+ }
+
+ if ($entity && $entity->canEdit()) {
+ if (elgg_trigger_event('update', $entity->type, $entity)) {
+ $ret = update_data("UPDATE {$CONFIG->dbprefix}entities
+ set owner_guid='$owner_guid', access_id='$access_id',
+ container_guid='$container_guid', time_created='$time_created',
+ time_updated='$time' WHERE guid=$guid");
if ($entity instanceof ElggObject) {
- update_river_access_by_object($guid,$access_id);
+ update_river_access_by_object($guid, $access_id);
}
// If memcache is available then delete this entry from the cache
@@ -1529,11 +493,11 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null) {
$newentity_cache = new ElggMemcache('new_entity_cache');
}
if ($newentity_cache) {
- $new_entity = $newentity_cache->delete($guid);
+ $newentity_cache->delete($guid);
}
// Handle cases where there was no error BUT no rows were updated!
- if ($ret===false) {
+ if ($ret === false) {
return false;
}
@@ -1543,27 +507,38 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null) {
}
/**
- * Determine whether a given user is able to write to a given container.
+ * Determine if a given user can write to an entity container.
+ *
+ * An entity can be a container for any other entity by setting the
+ * container_guid. container_guid can differ from owner_guid.
*
- * @param int $user_guid The user guid, or 0 for get_loggedin_userid()
- * @param int $container_guid The container, or 0 for the current page owner.
+ * A plugin hook container_permissions_check:$entity_type is emitted to allow granular
+ * access controls in plugins.
+ *
+ * @param int $user_guid The user guid, or 0 for logged in user
+ * @param int $container_guid The container, or 0 for the current page owner.
+ * @param string $type The type of entity we're looking to write
+ * @param string $subtype The subtype of the entity we're looking to write
+ *
+ * @return bool
+ * @link http://docs.elgg.org/DataModel/Containers
*/
-function can_write_to_container($user_guid = 0, $container_guid = 0, $entity_type = 'all') {
- global $CONFIG;
-
+function can_write_to_container($user_guid = 0, $container_guid = 0, $type = 'all', $subtype = 'all') {
$user_guid = (int)$user_guid;
$user = get_entity($user_guid);
if (!$user) {
- $user = get_loggedin_user();
+ $user = elgg_get_logged_in_user_entity();
}
$container_guid = (int)$container_guid;
if (!$container_guid) {
- $container_guid = page_owner();
+ $container_guid = elgg_get_page_owner_guid();
}
+ $return = false;
+
if (!$container_guid) {
- $return = TRUE;
+ $return = true;
}
$container = get_entity($container_guid);
@@ -1571,41 +546,62 @@ function can_write_to_container($user_guid = 0, $container_guid = 0, $entity_typ
if ($container) {
// If the user can edit the container, they can also write to it
if ($container->canEdit($user_guid)) {
- $return = TRUE;
+ $return = true;
}
- // Basics, see if the user is a member of the group.
- if ($user && $container instanceof ElggGroup) {
- if (!$container->isMember($user)) {
- $return = FALSE;
- } else {
- $return = TRUE;
+ // If still not approved, see if the user is a member of the group
+ // @todo this should be moved to the groups plugin/library
+ if (!$return && $user && $container instanceof ElggGroup) {
+ /* @var ElggGroup $container */
+ if ($container->isMember($user)) {
+ $return = true;
}
}
}
// See if anyone else has anything to say
- return trigger_plugin_hook('container_permissions_check', $entity_type,
- array('container' => $container, 'user' => $user), $return);
+ return elgg_trigger_plugin_hook(
+ 'container_permissions_check',
+ $type,
+ array(
+ 'container' => $container,
+ 'user' => $user,
+ 'subtype' => $subtype
+ ),
+ $return);
}
/**
- * Create a new entity of a given type.
- *
- * @param string $type The type of the entity (site, user, object).
- * @param string $subtype The subtype of the entity.
- * @param int $owner_guid The GUID of the object's owner.
- * @param int $access_id The access control group to create the entity with.
- * @param int $site_guid The site to add this entity to. Leave as 0 (default) for the current site.
- * @return mixed The new entity's GUID, or false on failure
+ * Create a new entry in the entities table.
+ *
+ * Saves the base information in the entities table for the entity. Saving
+ * the type information is handled in the calling class method.
+ *
+ * @warning Plugin authors should never call this directly. Always use entity objects.
+ *
+ * @warning Entities must have an entry in both the entities table and their type table
+ * or they will throw an exception when loaded.
+ *
+ * @param string $type The type of the entity (site, user, object, group).
+ * @param string $subtype The subtype of the entity.
+ * @param int $owner_guid The GUID of the object's owner.
+ * @param int $access_id The access control group to create the entity with.
+ * @param int $site_guid The site to add this entity to. 0 for current.
+ * @param int $container_guid The container GUID
+ *
+ * @return int|false The new entity's GUID, or false on failure
+ * @throws InvalidParameterException
+ * @link http://docs.elgg.org/DataModel/Entities
+ * @access private
*/
-function create_entity($type, $subtype, $owner_guid, $access_id, $site_guid = 0, $container_guid = 0) {
+function create_entity($type, $subtype, $owner_guid, $access_id, $site_guid = 0,
+$container_guid = 0) {
+
global $CONFIG;
$type = sanitise_string($type);
- $subtype = add_subtype($type, $subtype);
+ $subtype_id = add_subtype($type, $subtype);
$owner_guid = (int)$owner_guid;
- $access_id = (int)$access_id;
$time = time();
if ($site_guid == 0) {
$site_guid = $CONFIG->site_guid;
@@ -1614,31 +610,46 @@ function create_entity($type, $subtype, $owner_guid, $access_id, $site_guid = 0,
if ($container_guid == 0) {
$container_guid = $owner_guid;
}
+ $access_id = (int)$access_id;
+ if ($access_id == ACCESS_DEFAULT) {
+ throw new InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h');
+ }
- $user = get_loggedin_user();
- if (!can_write_to_container($user->guid, $owner_guid, $type)) {
+ $user_guid = elgg_get_logged_in_user_guid();
+ if (!can_write_to_container($user_guid, $owner_guid, $type, $subtype)) {
return false;
}
if ($owner_guid != $container_guid) {
- if (!can_write_to_container($user->guid, $container_guid, $type)) {
+ if (!can_write_to_container($user_guid, $container_guid, $type, $subtype)) {
return false;
}
}
- if ($type=="") {
+ if ($type == "") {
throw new InvalidParameterException(elgg_echo('InvalidParameterException:EntityTypeNotSet'));
}
return insert_data("INSERT into {$CONFIG->dbprefix}entities
- (type, subtype, owner_guid, site_guid, container_guid, access_id, time_created, time_updated, last_action) values
- ('$type',$subtype, $owner_guid, $site_guid, $container_guid, $access_id, $time, $time, $time)");
+ (type, subtype, owner_guid, site_guid, container_guid,
+ access_id, time_created, time_updated, last_action)
+ values
+ ('$type',$subtype_id, $owner_guid, $site_guid, $container_guid,
+ $access_id, $time, $time, $time)");
}
/**
- * Retrieve the entity details for a specific GUID, returning it as a stdClass db row.
+ * Returns a database row from the entities table.
*
- * You will only get an object if a) it exists, b) you have access to it.
+ * @tip Use get_entity() to return the fully loaded entity.
+ *
+ * @warning This will only return results if a) it exists, b) you have access to it.
+ * see {@link get_access_sql_suffix()}.
*
* @param int $guid The GUID of the object to extract
+ *
+ * @return stdClass|false
+ * @link http://docs.elgg.org/DataModel/Entities
+ * @see entity_row_to_elggstar()
+ * @access private
*/
function get_entity_as_row($guid) {
global $CONFIG;
@@ -1655,6 +666,19 @@ function get_entity_as_row($guid) {
/**
* Create an Elgg* object from a given entity row.
+ *
+ * Handles loading all tables into the correct class.
+ *
+ * @param stdClass $row The row of the entry in the entities table.
+ *
+ * @return ElggEntity|false
+ * @link http://docs.elgg.org/DataModel/Entities
+ * @see get_entity_as_row()
+ * @see add_subtype()
+ * @see get_entity()
+ * @access private
+ *
+ * @throws ClassException|InstallationException
*/
function entity_row_to_elggstar($row) {
if (!($row instanceof stdClass)) {
@@ -1679,31 +703,39 @@ function entity_row_to_elggstar($row) {
return $new_entity;
}
+ // load class for entity if one is registered
$classname = get_subtype_class_from_id($row->subtype);
- if ($classname!="") {
+ if ($classname != "") {
if (class_exists($classname)) {
$new_entity = new $classname($row);
if (!($new_entity instanceof ElggEntity)) {
- throw new ClassException(sprintf(elgg_echo('ClassException:ClassnameNotClass'), $classname, 'ElggEntity'));
+ $msg = elgg_echo('ClassException:ClassnameNotClass', array($classname, 'ElggEntity'));
+ throw new ClassException($msg);
}
- }
- else {
- error_log(sprintf(elgg_echo('ClassNotFoundException:MissingClass'), $classname));
+ } else {
+ error_log(elgg_echo('ClassNotFoundException:MissingClass', array($classname)));
}
}
- else {
+
+ if (!$new_entity) {
+ //@todo Make this into a function
switch ($row->type) {
case 'object' :
- $new_entity = new ElggObject($row); break;
+ $new_entity = new ElggObject($row);
+ break;
case 'user' :
- $new_entity = new ElggUser($row); break;
+ $new_entity = new ElggUser($row);
+ break;
case 'group' :
- $new_entity = new ElggGroup($row); break;
+ $new_entity = new ElggGroup($row);
+ break;
case 'site' :
- $new_entity = new ElggSite($row); break;
+ $new_entity = new ElggSite($row);
+ break;
default:
- throw new InstallationException(sprintf(elgg_echo('InstallationException:TypeNotSupported'), $row->type));
+ $msg = elgg_echo('InstallationException:TypeNotSupported', array($row->type));
+ throw new InstallationException($msg);
}
}
@@ -1716,51 +748,134 @@ function entity_row_to_elggstar($row) {
}
/**
- * Return the entity for a given guid as the correct object.
+ * Loads and returns an entity object from a guid.
+ *
* @param int $guid The GUID of the entity
- * @return a child of ElggEntity appropriate for the type.
+ *
+ * @return ElggEntity The correct Elgg or custom object based upon entity type and subtype
+ * @link http://docs.elgg.org/DataModel/Entities
*/
function get_entity($guid) {
- static $newentity_cache;
- $new_entity = false;
- if ((!$newentity_cache) && (is_memcache_available())) {
- $newentity_cache = new ElggMemcache('new_entity_cache');
+ // This should not be a static local var. Notice that cache writing occurs in a completely
+ // different instance outside this function.
+ // @todo We need a single Memcache instance with a shared pool of namespace wrappers. This function would pull an instance from the pool.
+ static $shared_cache;
+
+ // We could also use: if (!(int) $guid) { return FALSE },
+ // but that evaluates to a false positive for $guid = TRUE.
+ // This is a bit slower, but more thorough.
+ if (!is_numeric($guid) || $guid === 0 || $guid === '0') {
+ return false;
+ }
+
+ // Check local cache first
+ $new_entity = _elgg_retrieve_cached_entity($guid);
+ if ($new_entity) {
+ return $new_entity;
}
- if ($newentity_cache) {
- $new_entity = $newentity_cache->load($guid);
+ // Check shared memory cache, if available
+ if (null === $shared_cache) {
+ if (is_memcache_available()) {
+ $shared_cache = new ElggMemcache('new_entity_cache');
+ } else {
+ $shared_cache = false;
+ }
}
- if ($new_entity) {
- return $new_entity;
+ // until ACLs in memcache, DB query is required to determine access
+ $entity_row = get_entity_as_row($guid);
+ if (!$entity_row) {
+ return false;
+ }
+
+ if ($shared_cache) {
+ $cached_entity = $shared_cache->load($guid);
+ // @todo store ACLs in memcache https://github.com/elgg/elgg/issues/3018#issuecomment-13662617
+ if ($cached_entity) {
+ // @todo use ACL and cached entity access_id to determine if user can see it
+ return $cached_entity;
+ }
+ }
+
+ // don't let incomplete entities cause fatal exceptions
+ try {
+ $new_entity = entity_row_to_elggstar($entity_row);
+ } catch (IncompleteEntityException $e) {
+ return false;
}
- return entity_row_to_elggstar(get_entity_as_row($guid));
+ if ($new_entity) {
+ _elgg_cache_entity($new_entity);
+ }
+ return $new_entity;
}
+/**
+ * Does an entity exist?
+ *
+ * This function checks for the existence of an entity independent of access
+ * permissions. It is useful for situations when a user cannot access an entity
+ * and it must be determined whether entity has been deleted or the access level
+ * has changed.
+ *
+ * @param int $guid The GUID of the entity
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+function elgg_entity_exists($guid) {
+ global $CONFIG;
+
+ $guid = sanitize_int($guid);
+
+ $query = "SELECT count(*) as total FROM {$CONFIG->dbprefix}entities WHERE guid = $guid";
+ $result = get_data_row($query);
+ if ($result->total == 0) {
+ return false;
+ } else {
+ return true;
+ }
+}
/**
- * Get all entities. NB: Plural arguments can be written as
- * singular if only specifying a single element. (e.g., 'type' => 'object'
- * vs 'types' => array('object')).
+ * Returns an array of entities with optional filtering.
+ *
+ * Entities are the basic unit of storage in Elgg. This function
+ * provides the simplest way to get an array of entities. There
+ * are many options available that can be passed to filter
+ * what sorts of entities are returned.
+ *
+ * @tip To output formatted strings of entities, use {@link elgg_list_entities()} and
+ * its cousins.
+ *
+ * @tip Plural arguments can be written as singular if only specifying a
+ * single element. ('type' => 'object' vs 'types' => array('object')).
*
* @param array $options Array in format:
*
- * types => NULL|STR entity type (SQL: type IN ('type1', 'type2') Joined with subtypes by AND...see below)
+ * types => NULL|STR entity type (type IN ('type1', 'type2')
+ * 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
*
- * type_subtype_pairs => NULL|ARR (array('type' => 'subtype')) (SQL: type = '$type' AND subtype = '$subtype') pairs
+ * guids => NULL|ARR Array of entity guids
*
- * owner_guids => NULL|INT entity guid
+ * owner_guids => NULL|ARR Array of owner guids
*
- * container_guids => NULL|INT container_guid
+ * container_guids => NULL|ARR Array of container_guids
*
- * site_guids => NULL (current_site)|INT site_guid
+ * site_guids => NULL (current_site)|ARR Array of site_guid
*
* order_by => NULL (time_created desc)|STR SQL order by clause
*
- * limit => NULL (10)|INT SQL limit clause
+ * reverse_order_by => BOOL Reverse the default order by clause
+ *
+ * limit => NULL (10)|INT SQL limit clause (0 means no limit)
*
* offset => NULL (0)|INT SQL offset clause
*
@@ -1778,8 +893,16 @@ function get_entity($guid) {
*
* joins => array() Additional joins
*
- * @return if count, int
- * if not count, array or false if no entities
+ * callback => string A callback function to pass each row through
+ *
+ * @return mixed If count, int. If not count, array. false on errors.
+ * @since 1.7.0
+ * @see elgg_get_entities_from_metadata()
+ * @see elgg_get_entities_from_relationship()
+ * @see elgg_get_entities_from_access_id()
+ * @see elgg_get_entities_from_annotations()
+ * @see elgg_list_entities()
+ * @link http://docs.elgg.org/DataModel/Entities/Getters
*/
function elgg_get_entities(array $options = array()) {
global $CONFIG;
@@ -1789,6 +912,7 @@ function elgg_get_entities(array $options = array()) {
'subtypes' => ELGG_ENTITIES_ANY_VALUE,
'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
+ 'guids' => ELGG_ENTITIES_ANY_VALUE,
'owner_guids' => ELGG_ENTITIES_ANY_VALUE,
'container_guids' => ELGG_ENTITIES_ANY_VALUE,
'site_guids' => $CONFIG->site_guid,
@@ -1798,6 +922,7 @@ function elgg_get_entities(array $options = array()) {
'created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
'created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
+ 'reverse_order_by' => false,
'order_by' => 'e.time_created desc',
'group_by' => ELGG_ENTITIES_ANY_VALUE,
'limit' => 10,
@@ -1805,21 +930,27 @@ function elgg_get_entities(array $options = array()) {
'count' => FALSE,
'selects' => array(),
'wheres' => array(),
- 'joins' => array()
+ 'joins' => array(),
+
+ 'callback' => 'entity_row_to_elggstar',
+
+ '__ElggBatch' => null,
);
$options = array_merge($defaults, $options);
- // can't use helper function with type_subtype_pair because it's already an array...just need to merge it
+ // can't use helper function with type_subtype_pair because
+ // it's already an array...just need to merge it
if (isset($options['type_subtype_pair'])) {
if (isset($options['type_subtype_pairs'])) {
- $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'], $options['type_subtype_pair']);
+ $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'],
+ $options['type_subtype_pair']);
} else {
$options['type_subtype_pairs'] = $options['type_subtype_pair'];
}
}
- $singulars = array('type', 'subtype', 'owner_guid', 'container_guid', 'site_guid');
+ $singulars = array('type', 'subtype', 'guid', 'owner_guid', 'container_guid', 'site_guid');
$options = elgg_normalise_plural_options_array($options, $singulars);
// evaluate where clauses
@@ -1829,16 +960,17 @@ function elgg_get_entities(array $options = array()) {
$wheres = $options['wheres'];
- $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'], $options['subtypes'], $options['type_subtype_pairs']);
- $wheres[] = elgg_get_entity_site_where_sql('e', $options['site_guids']);
- $wheres[] = elgg_get_entity_owner_where_sql('e', $options['owner_guids']);
- $wheres[] = elgg_get_entity_container_where_sql('e', $options['container_guids']);
+ $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'],
+ $options['subtypes'], $options['type_subtype_pairs']);
+
+ $wheres[] = elgg_get_guid_based_where_sql('e.guid', $options['guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']);
+
$wheres[] = elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
$options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
- // remove identical where clauses
- $wheres = array_unique($wheres);
-
// see if any functions failed
// remove empty strings on successful functions
foreach ($wheres as $i => $where) {
@@ -1849,6 +981,9 @@ function elgg_get_entities(array $options = array()) {
}
}
+ // remove identical where clauses
+ $wheres = array_unique($wheres);
+
// evaluate join clauses
if (!is_array($options['joins'])) {
$options['joins'] = array($options['joins']);
@@ -1869,7 +1004,7 @@ function elgg_get_entities(array $options = array()) {
if ($options['selects']) {
$selects = '';
foreach ($options['selects'] as $select) {
- $selects = ", $select";
+ $selects .= ", $select";
}
} else {
$selects = '';
@@ -1895,24 +1030,53 @@ function elgg_get_entities(array $options = array()) {
// Add access controls
$query .= get_access_sql_suffix('e');
+
+ // reverse order by
+ if ($options['reverse_order_by']) {
+ $options['order_by'] = elgg_sql_reverse_order_by_clause($options['order_by']);
+ }
+
if (!$options['count']) {
- if ($options['group_by'] = sanitise_string($options['group_by'])) {
+ if ($options['group_by']) {
$query .= " GROUP BY {$options['group_by']}";
}
- if ($options['order_by'] = sanitise_string($options['order_by'])) {
+ if ($options['order_by']) {
$query .= " ORDER BY {$options['order_by']}";
}
if ($options['limit']) {
- $limit = sanitise_int($options['limit']);
- $offset = sanitise_int($options['offset']);
+ $limit = sanitise_int($options['limit'], false);
+ $offset = sanitise_int($options['offset'], false);
$query .= " LIMIT $offset, $limit";
}
- $dt = get_data($query, "entity_row_to_elggstar");
+ if ($options['callback'] === 'entity_row_to_elggstar') {
+ $dt = _elgg_fetch_entities_from_sql($query, $options['__ElggBatch']);
+ } else {
+ $dt = get_data($query, $options['callback']);
+ }
+
+ if ($dt) {
+ // populate entity and metadata caches
+ $guids = array();
+ foreach ($dt as $item) {
+ // A custom callback could result in items that aren't ElggEntity's, so check for them
+ if ($item instanceof ElggEntity) {
+ _elgg_cache_entity($item);
+ // plugins usually have only settings
+ if (!$item instanceof ElggPlugin) {
+ $guids[] = $item->guid;
+ }
+ }
+ }
+ // @todo Without this, recursive delete fails. See #4568
+ reset($dt);
- //@todo normalize this to array()
+ if ($guids) {
+ elgg_get_metadata_cache()->populateFromEntities($guids);
+ }
+ }
return $dt;
} else {
$total = get_data_row($query);
@@ -1921,100 +1085,114 @@ function elgg_get_entities(array $options = array()) {
}
/**
- * @deprecated 1.7. Use elgg_get_entities().
- * @param $type
- * @param $subtype
- * @param $owner_guid
- * @param $order_by
- * @param $limit
- * @param $offset
- * @param $count
- * @param $site_guid
- * @param $container_guid
- * @param $timelower
- * @param $timeupper
- * @return unknown_type
+ * Return entities from an SQL query generated by elgg_get_entities.
+ *
+ * @param string $sql
+ * @param ElggBatch $batch
+ * @return ElggEntity[]
+ *
+ * @access private
+ * @throws LogicException
*/
-function get_entities($type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, $offset = 0,
-$count = false, $site_guid = 0, $container_guid = null, $timelower = 0, $timeupper = 0) {
- elgg_deprecated_notice('get_entities() was deprecated by elgg_get_entities().', 1.7);
-
- // rewrite owner_guid to container_guid to emulate old functionality
- if ($owner_guid != "") {
- if (is_null($container_guid)) {
- $container_guid = $owner_guid;
- $owner_guid = NULL;
- }
- }
+function _elgg_fetch_entities_from_sql($sql, ElggBatch $batch = null) {
+ static $plugin_subtype;
+ if (null === $plugin_subtype) {
+ $plugin_subtype = get_subtype_id('object', 'plugin');
+ }
+
+ // Keys are types, values are columns that, if present, suggest that the secondary
+ // table is already JOINed
+ $types_to_optimize = array(
+ 'object' => 'title',
+ 'user' => 'password',
+ 'group' => 'name',
+ );
- $options = array();
- if ($type) {
- if (is_array($type)) {
- $options['types'] = $type;
- } else {
- $options['type'] = $type;
- }
+ $rows = get_data($sql);
+
+ // guids to look up in each type
+ $lookup_types = array();
+ // maps GUIDs to the $rows key
+ $guid_to_key = array();
+
+ if (isset($rows[0]->type, $rows[0]->subtype)
+ && $rows[0]->type === 'object'
+ && $rows[0]->subtype == $plugin_subtype) {
+ // Likely the entire resultset is plugins, which have already been optimized
+ // to JOIN the secondary table. In this case we allow retrieving from cache,
+ // but abandon the extra queries.
+ $types_to_optimize = array();
}
- if ($subtype) {
- if (is_array($subtype)) {
- $options['subtypes'] = $subtype;
- } else {
- $options['subtype'] = $subtype;
+ // First pass: use cache where possible, gather GUIDs that we're optimizing
+ foreach ($rows as $i => $row) {
+ if (empty($row->guid) || empty($row->type)) {
+ throw new LogicException('Entity row missing guid or type');
+ }
+ if ($entity = _elgg_retrieve_cached_entity($row->guid)) {
+ $rows[$i] = $entity;
+ continue;
+ }
+ if (isset($types_to_optimize[$row->type])) {
+ // check if row already looks JOINed.
+ if (isset($row->{$types_to_optimize[$row->type]})) {
+ // Row probably already contains JOINed secondary table. Don't make another query just
+ // to pull data that's already there
+ continue;
+ }
+ $lookup_types[$row->type][] = $row->guid;
+ $guid_to_key[$row->guid] = $i;
+ }
+ }
+ // Do secondary queries and merge rows
+ if ($lookup_types) {
+ $dbprefix = elgg_get_config('dbprefix');
+
+ foreach ($lookup_types as $type => $guids) {
+ $set = "(" . implode(',', $guids) . ")";
+ $sql = "SELECT * FROM {$dbprefix}{$type}s_entity WHERE guid IN $set";
+ $secondary_rows = get_data($sql);
+ if ($secondary_rows) {
+ foreach ($secondary_rows as $secondary_row) {
+ $key = $guid_to_key[$secondary_row->guid];
+ // cast to arrays to merge then cast back
+ $rows[$key] = (object)array_merge((array)$rows[$key], (array)$secondary_row);
+ }
+ }
}
}
-
- if ($owner_guid) {
- if (is_array($owner_guid)) {
- $options['owner_guids'] = $owner_guid;
+ // Second pass to finish conversion
+ foreach ($rows as $i => $row) {
+ if ($row instanceof ElggEntity) {
+ continue;
} else {
- $options['owner_guid'] = $owner_guid;
+ try {
+ $rows[$i] = entity_row_to_elggstar($row);
+ } catch (IncompleteEntityException $e) {
+ // don't let incomplete entities throw fatal errors
+ unset($rows[$i]);
+
+ // report incompletes to the batch process that spawned this query
+ if ($batch) {
+ $batch->reportIncompleteEntity($row);
+ }
+ }
}
}
-
- if ($order_by) {
- $options['order_by'] = $order_by;
- }
-
- // need to pass 0 for all option
- $options['limit'] = $limit;
-
- if ($offset) {
- $options['offset'] = $offset;
- }
-
- if ($count) {
- $options['count'] = $count;
- }
-
- if ($site_guid) {
- $options['site_guids'] = $site_guid;
- }
-
- if ($container_guid) {
- $options['container_guids'] = $container_guid;
- }
-
- if ($timeupper) {
- $options['created_time_upper'] = $timeupper;
- }
-
- if ($timelower) {
- $options['created_time_lower'] = $timelower;
- }
-
- $r = elgg_get_entities($options);
- return $r;
+ return $rows;
}
/**
- * Returns type and subtype SQL appropriate for inclusion in an IN clause.
+ * Returns SQL where clause for type and subtype on main entity table
+ *
+ * @param string $table Entity table prefix as defined in SELECT...FROM entities $table
+ * @param NULL|array $types Array of types or NULL if none.
+ * @param NULL|array $subtypes Array of subtypes or NULL if none
+ * @param NULL|array $pairs Array of pairs of types and subtypes
*
- * @param string $table entity table prefix.
- * @param NULL|$types
- * @param NULL|array $subtypes
- * @param NULL|array $pairs
* @return FALSE|string
+ * @since 1.7.0
+ * @access private
*/
function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pairs) {
// subtype depends upon type.
@@ -2028,8 +1206,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();
@@ -2055,7 +1233,7 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
foreach ($types as $type) {
if (!in_array($type, $valid_types)) {
$valid_types_count--;
- unset ($types[array_search($type, $types)]);
+ unset($types[array_search($type, $types)]);
} else {
// do the checking (and decrementing) in the subtype section.
$valid_subtypes_count += count($subtypes);
@@ -2073,13 +1251,24 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
$subtype_ids = array();
if ($subtypes) {
foreach ($subtypes as $subtype) {
- // check that the subtype is valid (with ELGG_ENTITIES_NO_VALUE being a valid subtype)
- if (ELGG_ENTITIES_NO_VALUE === $subtype || $subtype_id = get_subtype_id($type, $subtype)) {
- $subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $subtype) ? ELGG_ENTITIES_NO_VALUE : $subtype_id;
- } else {
- $valid_subtypes_count--;
- elgg_log("Type-subtype $type:$subtype' does not exist!", 'WARNING');
+ // check that the subtype is valid
+ if (!$subtype && ELGG_ENTITIES_NO_VALUE === $subtype) {
+ // subtype value is 0
+ $subtype_ids[] = ELGG_ENTITIES_NO_VALUE;
+ } elseif (!$subtype) {
+ // subtype is ignored.
+ // this handles ELGG_ENTITIES_ANY_VALUE, '', and anything falsy that isn't 0
continue;
+ } else {
+ $subtype_id = get_subtype_id($type, $subtype);
+
+ if ($subtype_id) {
+ $subtype_ids[] = $subtype_id;
+ } else {
+ $valid_subtypes_count--;
+ elgg_log("Type-subtype '$type:$subtype' does not exist!", 'NOTICE');
+ continue;
+ }
}
}
@@ -2107,7 +1296,7 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
foreach ($pairs as $paired_type => $paired_subtypes) {
if (!in_array($paired_type, $valid_types)) {
$valid_pairs_count--;
- unset ($pairs[array_search($paired_type, $pairs)]);
+ unset($pairs[array_search($paired_type, $pairs)]);
} else {
if ($paired_subtypes && !is_array($paired_subtypes)) {
$pairs[$paired_type] = array($paired_subtypes);
@@ -2125,11 +1314,14 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
if (is_array($paired_subtypes)) {
$paired_subtype_ids = array();
foreach ($paired_subtypes as $paired_subtype) {
- if (ELGG_ENTITIES_NO_VALUE === $paired_subtype || ($paired_subtype_id = get_subtype_id($paired_type, $paired_subtype))) {
- $paired_subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $paired_subtype) ? ELGG_ENTITIES_NO_VALUE : $paired_subtype_id;
+ if (ELGG_ENTITIES_NO_VALUE === $paired_subtype
+ || ($paired_subtype_id = get_subtype_id($paired_type, $paired_subtype))) {
+
+ $paired_subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $paired_subtype) ?
+ ELGG_ENTITIES_NO_VALUE : $paired_subtype_id;
} else {
$valid_pairs_subtypes_count--;
- elgg_log("Type-subtype $paired_type:$paired_subtype' does not exist!", 'WARNING');
+ elgg_log("Type-subtype '$paired_type:$paired_subtype' does not exist!", 'NOTICE');
// return false if we're all invalid subtypes in the only valid type
continue;
}
@@ -2142,7 +1334,8 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
if ($paired_subtype_ids_str = implode(',', $paired_subtype_ids)) {
- $wheres[] = "({$table}.type = '$paired_type' AND {$table}.subtype IN ($paired_subtype_ids_str))";
+ $wheres[] = "({$table}.type = '$paired_type'"
+ . " AND {$table}.subtype IN ($paired_subtype_ids_str))";
}
} else {
$wheres[] = "({$table}.type = '$paired_type')";
@@ -2159,78 +1352,47 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
return '';
}
-
-
/**
- * Returns SQL for owner and containers.
+ * Returns SQL where clause for owner and containers.
*
- * @todo Probably DRY up once things are settled.
- * @param str $table
- * @param NULL|array $owner_guids
- * @return FALSE|str
+ * @param string $column Column name the guids should be checked against. Usually
+ * best to provide in table.column format.
+ * @param NULL|array $guids Array of GUIDs.
+ *
+ * @return false|string
+ * @since 1.8.0
+ * @access private
*/
-function elgg_get_entity_owner_where_sql($table, $owner_guids) {
+function elgg_get_guid_based_where_sql($column, $guids) {
// short circuit if nothing requested
- // 0 is a valid owner_guid.
- if (!$owner_guids && $owner_guids !== 0) {
+ // 0 is a valid guid
+ if (!$guids && $guids !== 0) {
return '';
}
// normalize and sanitise owners
- if (!is_array($owner_guids)) {
- $owner_guids = array($owner_guids);
- }
-
- $owner_guids_sanitised = array();
- foreach ($owner_guids as $owner_guid) {
- if (($owner_guid != sanitise_int($owner_guid))) {
- return FALSE;
- }
- $owner_guids_sanitised[] = $owner_guid;
- }
-
- $where = '';
-
- // implode(',', 0) returns 0.
- if (($owner_str = implode(',', $owner_guids_sanitised)) && ($owner_str !== FALSE) && ($owner_str !== '')) {
- $where = "({$table}.owner_guid IN ($owner_str))";
- }
-
- return $where;
-}
-
-/**
- * Returns SQL for containers.
- *
- * @param string $table entity table prefix
- * @param NULL|array $container_guids
- * @return FALSE|string
- */
-function elgg_get_entity_container_where_sql($table, $container_guids) {
- // short circuit if nothing is requested.
- // 0 is a valid container_guid.
- if (!$container_guids && $container_guids !== 0) {
- return '';
+ if (!is_array($guids)) {
+ $guids = array($guids);
}
- // normalize and sanitise containers
- if (!is_array($container_guids)) {
- $container_guids = array($container_guids);
- }
+ $guids_sanitized = array();
+ foreach ($guids as $guid) {
+ if ($guid !== ELGG_ENTITIES_NO_VALUE) {
+ $guid = sanitise_int($guid);
- $container_guids_sanitised = array();
- foreach ($container_guids as $container_guid) {
- if (($container_guid != sanitise_int($container_guid))) {
- return FALSE;
+ if (!$guid) {
+ return false;
+ }
}
- $container_guids_sanitised[] = $container_guid;
+ $guids_sanitized[] = $guid;
}
$where = '';
+ $guid_str = implode(',', $guids_sanitized);
// implode(',', 0) returns 0.
- if (FALSE !== $container_str = implode(',', $container_guids_sanitised)) {
- $where = "({$table}.container_guid IN ($container_str))";
+ if ($guid_str !== FALSE && $guid_str !== '') {
+ $where = "($column IN ($guid_str))";
}
return $where;
@@ -2239,16 +1401,19 @@ function elgg_get_entity_container_where_sql($table, $container_guids) {
/**
* Returns SQL where clause for entity time limits.
*
- * @param string $table Prefix for entity table name.
- * @param NULL|int $time_created_upper
- * @param NULL|int $time_created_lower
- * @param NULL|int $time_updated_upper
- * @param NULL|int $time_updated_lower
+ * @param string $table Entity table prefix as defined in
+ * SELECT...FROM entities $table
+ * @param NULL|int $time_created_upper Time created upper limit
+ * @param NULL|int $time_created_lower Time created lower limit
+ * @param NULL|int $time_updated_upper Time updated upper limit
+ * @param NULL|int $time_updated_lower Time updated lower limit
*
- * @return FALSE|str FALSE on fail, string on success.
+ * @return FALSE|string FALSE on fail, string on success.
+ * @since 1.7.0
+ * @access private
*/
-function elgg_get_entity_time_where_sql($table, $time_created_upper = NULL, $time_created_lower = NULL,
- $time_updated_upper = NULL, $time_updated_lower = NULL) {
+function elgg_get_entity_time_where_sql($table, $time_created_upper = NULL,
+$time_created_lower = NULL, $time_updated_upper = NULL, $time_updated_lower = NULL) {
$wheres = array();
@@ -2278,144 +1443,84 @@ function elgg_get_entity_time_where_sql($table, $time_created_upper = NULL, $tim
}
/**
- * Gets SQL for site entities
- *
- * @param string $table entity table name
- * @param NULL|array $site_guids
- * @return FALSE|string
- */
-function elgg_get_entity_site_where_sql($table, $site_guids) {
- // short circuit if nothing requested
- if (!$site_guids) {
- return '';
- }
-
- if (!is_array($site_guids)) {
- $site_guids = array($site_guids);
- }
-
- $site_guids_sanitised = array();
- foreach ($site_guids as $site_guid) {
- if (!$site_guid || ($site_guid != sanitise_int($site_guid))) {
- return FALSE;
- }
- $site_guids_sanitised[] = $site_guid;
- }
-
- if ($site_guids_str = implode(',', $site_guids_sanitised)) {
- return "({$table}.site_guid IN ($site_guids_str))";
- }
-
- return '';
-}
-
-/**
- * Returns a viewable list of entities
+ * Returns a string of parsed entities.
*
- * @see elgg_view_entity_list
+ * Displays list of entities with formatting specified
+ * by the entity view.
*
- * @param array $options Any elgg_get_entity() options plus:
+ * @tip Pagination is handled automatically.
*
- * full_view => BOOL Display full view entities
+ * @internal This also provides the views for elgg_view_annotation().
*
- * view_type_toggle => BOOL Display gallery / list switch
+ * @param array $options Any options from $getter options plus:
+ * full_view => BOOL Display full view entities
+ * list_type => STR 'list' or 'gallery'
+ * list_type_toggle => BOOL Display gallery / list switch
+ * pagination => BOOL Display pagination links
*
- * pagination => BOOL Display pagination links
+ * @param mixed $getter The entity getter function to use to fetch the entities
+ * @param mixed $viewer The function to use to view the entity list.
*
- * @return str
+ * @return string
+ * @since 1.7
+ * @see elgg_get_entities()
+ * @see elgg_view_entity_list()
+ * @link http://docs.elgg.org/Entities/Output
*/
-function elgg_list_entities($options) {
+function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entities',
+ $viewer = 'elgg_view_entity_list') {
+
+ global $autofeed;
+ $autofeed = true;
+
+ $offset_key = isset($options['offset_key']) ? $options['offset_key'] : 'offset';
+
$defaults = array(
- 'offset' => (int) max(get_input('offset', 0), 0),
+ 'offset' => (int) max(get_input($offset_key, 0), 0),
'limit' => (int) max(get_input('limit', 10), 0),
'full_view' => TRUE,
- 'view_type_toggle' => FALSE,
- 'pagination' => TRUE
+ 'list_type_toggle' => FALSE,
+ 'pagination' => TRUE,
);
- $options = array_merge($defaults, $options);
-
- $count = elgg_get_entities(array_merge(array('count' => TRUE), $options));
- $entities = elgg_get_entities($options);
-
- return elgg_view_entity_list($entities, $count, $options['offset'],
- $options['limit'], $options['full_view'], $options['view_type_toggle'], $options['pagination']);
-}
-
-/**
- * @deprecated 1.7. Use elgg_list_entities().
- * @param $type
- * @param $subtype
- * @param $owner_guid
- * @param $limit
- * @param $fullview
- * @param $viewtypetoggle
- * @param $pagination
- * @return unknown_type
- */
-function list_entities($type= "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = false, $pagination = true) {
- elgg_deprecated_notice('list_entities() was deprecated by elgg_list_entities()!', 1.7);
- $options = array();
-
- // rewrite owner_guid to container_guid to emulate old functionality
- if ($owner_guid) {
- $options['container_guids'] = $owner_guid;
- }
-
- if ($type) {
- $options['types'] = $type;
- }
+ $options = array_merge($defaults, $options);
- if ($subtype) {
- $options['subtypes'] = $subtype;
+ //backwards compatibility
+ if (isset($options['view_type_toggle'])) {
+ $options['list_type_toggle'] = $options['view_type_toggle'];
}
- if ($limit) {
- $options['limit'] = $limit;
- }
+ $options['count'] = TRUE;
+ $count = $getter($options);
- if ($offset = sanitise_int(get_input('offset', null))) {
- $options['offset'] = $offset;
- }
+ $options['count'] = FALSE;
+ $entities = $getter($options);
- $options['full_view'] = $fullview;
- $options['view_type_toggle'] = $viewtypetoggle;
- $options['pagination'] = $pagination;
+ $options['count'] = $count;
- return elgg_list_entities($options);
+ return $viewer($entities, $options);
}
/**
- * Returns a viewable list of entities contained in a number of groups.
- *
- * @param string $subtype The arbitrary subtype of the entity
- * @param int $owner_guid The GUID of the owning user
- * @param int $container_guid The GUID of the containing group
- * @param int $limit The number of entities to display per page (default: 10)
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow gallery view (default: true)
- * @param true|false $pagination Whether to display pagination (default: true)
- * @return string A viewable list of entities
+ * Returns a list of months in which entities were updated or created.
+ *
+ * @tip Use this to generate a list of archives by month for when entities were added or updated.
+ *
+ * @todo document how to pass in array for $subtype
+ *
+ * @warning Months are returned in the form YYYYMM.
+ *
+ * @param string $type The type of entity
+ * @param string $subtype The subtype of entity
+ * @param int $container_guid The container GUID that the entities belong to
+ * @param int $site_guid The site GUID
+ * @param string $order_by Order_by SQL order by clause
+ *
+ * @return array|false Either an array months as YYYYMM, or false on failure
*/
-function list_entities_groups($subtype = "", $owner_guid = 0, $container_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = true, $pagination = true) {
- $offset = (int) get_input('offset');
- $count = get_objects_in_group($container_guid, $subtype, $owner_guid, 0, "", $limit, $offset, true);
- $entities = get_objects_in_group($container_guid, $subtype, $owner_guid, 0, "", $limit, $offset);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
-}
+function get_entity_dates($type = '', $subtype = '', $container_guid = 0, $site_guid = 0,
+$order_by = 'time_created') {
-/**
- * Returns a list of months containing content specified by the parameters
- *
- * @param string $type The type of entity
- * @param string $subtype The subtype of entity
- * @param int $container_guid The container GUID that the entinties belong to
- * @param int $site_guid The site GUID
- * @param str order_by SQL order by clause
- * @return array|false Either an array of timestamps, or false on failure
- */
-function get_entity_dates($type = '', $subtype = '', $container_guid = 0, $site_guid = 0, $order_by = 'time_created') {
global $CONFIG;
$site_guid = (int) $site_guid;
@@ -2432,16 +1537,19 @@ function get_entity_dates($type = '', $subtype = '', $container_guid = 0, $site_
if (is_array($subtype)) {
$tempwhere = "";
if (sizeof($subtype)) {
- foreach($subtype as $typekey => $subtypearray) {
- foreach($subtypearray as $subtypeval) {
+ foreach ($subtype as $typekey => $subtypearray) {
+ foreach ($subtypearray as $subtypeval) {
$typekey = sanitise_string($typekey);
if (!empty($subtypeval)) {
- if (!$subtypeval = (int) get_subtype_id($typekey, $subtypeval))
+ if (!$subtypeval = (int) get_subtype_id($typekey, $subtypeval)) {
return false;
+ }
} else {
$subtypeval = 0;
}
- if (!empty($tempwhere)) $tempwhere .= " or ";
+ if (!empty($tempwhere)) {
+ $tempwhere .= " or ";
+ }
$tempwhere .= "(type = '{$typekey}' and subtype = {$subtypeval})";
}
}
@@ -2461,10 +1569,10 @@ function get_entity_dates($type = '', $subtype = '', $container_guid = 0, $site_
if ($container_guid !== 0) {
if (is_array($container_guid)) {
- foreach($container_guid as $key => $val) {
+ foreach ($container_guid as $key => $val) {
$container_guid[$key] = (int) $val;
}
- $where[] = "container_guid in (" . implode(",",$container_guid) . ")";
+ $where[] = "container_guid in (" . implode(",", $container_guid) . ")";
} else {
$container_guid = (int) $container_guid;
$where[] = "container_guid = {$container_guid}";
@@ -2487,7 +1595,7 @@ function get_entity_dates($type = '', $subtype = '', $container_guid = 0, $site_
$sql .= "1=1 ORDER BY $order_by";
if ($result = get_data($sql)) {
$endresult = array();
- foreach($result as $res) {
+ foreach ($result as $res) {
$endresult[] = $res->yearmonth;
}
return $endresult;
@@ -2496,10 +1604,26 @@ function get_entity_dates($type = '', $subtype = '', $container_guid = 0, $site_
}
/**
- * Disable an entity but not delete it.
+ * Disable an entity.
*
- * @param int $guid The guid
- * @param string $reason Optional reason
+ * Disabled entities do not show up in list or elgg_get_entity()
+ * calls, but still exist in the database.
+ *
+ * Entities are disabled by setting disabled = yes in the
+ * entities table.
+ *
+ * You can ignore the disabled field by using {@link access_show_hidden_entities()}.
+ *
+ * @note Use ElggEntity::disable() instead.
+ *
+ * @param int $guid The guid
+ * @param string $reason Optional reason
+ * @param bool $recursive Recursively disable all entities owned or contained by $guid?
+ *
+ * @return bool
+ * @see access_show_hidden_entities()
+ * @link http://docs.elgg.org/Entities
+ * @access private
*/
function disable_entity($guid, $reason = "", $recursive = true) {
global $CONFIG;
@@ -2508,35 +1632,41 @@ function disable_entity($guid, $reason = "", $recursive = true) {
$reason = sanitise_string($reason);
if ($entity = get_entity($guid)) {
- if (trigger_elgg_event('disable',$entity->type,$entity)) {
+ if (elgg_trigger_event('disable', $entity->type, $entity)) {
if ($entity->canEdit()) {
if ($reason) {
- create_metadata($guid, 'disable_reason', $reason,'', 0, ACCESS_PUBLIC);
+ create_metadata($guid, 'disable_reason', $reason, '', 0, ACCESS_PUBLIC);
}
if ($recursive) {
- // Temporary token overriding access controls TODO: Do this better.
- static $__RECURSIVE_DELETE_TOKEN;
- // Make it slightly harder to guess
- $__RECURSIVE_DELETE_TOKEN = md5(get_loggedin_userid());
-
- $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');
+ $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
+ OR owner_guid = $guid
+ OR site_guid = $guid
+ ) AND enabled='yes'", 'entity_row_to_elggstar');
if ($sub_entities) {
foreach ($sub_entities as $e) {
+ add_entity_relationship($e->guid, 'disabled_with', $entity->guid);
$e->disable($reason);
}
}
-
- $__RECURSIVE_DELETE_TOKEN = null;
+ access_show_hidden_entities($hidden);
+ elgg_set_ignore_access($ia);
}
+ $entity->disableMetadata();
+ $entity->disableAnnotations();
+ _elgg_invalidate_cache_for_entity($guid);
+
$res = update_data("UPDATE {$CONFIG->dbprefix}entities
- set enabled='no'
- where guid={$guid}");
+ SET enabled = 'no'
+ WHERE guid = $guid");
return $res;
}
@@ -2546,86 +1676,155 @@ function disable_entity($guid, $reason = "", $recursive = true) {
}
/**
- * Enable an entity again.
+ * Enable an entity.
+ *
+ * @warning In order to enable an entity, you must first use
+ * {@link access_show_hidden_entities()}.
*
- * @param int $guid
+ * @param int $guid GUID of entity to enable
+ * @param bool $recursive Recursively enable all entities disabled with the entity?
+ *
+ * @return bool
*/
-function enable_entity($guid) {
+function enable_entity($guid, $recursive = true) {
global $CONFIG;
$guid = (int)$guid;
// Override access only visible entities
- $access_status = access_get_show_hidden_status();
+ $old_access_status = access_get_show_hidden_status();
access_show_hidden_entities(true);
+ $result = false;
if ($entity = get_entity($guid)) {
- if (trigger_elgg_event('enable',$entity->type,$entity)) {
+ if (elgg_trigger_event('enable', $entity->type, $entity)) {
if ($entity->canEdit()) {
- access_show_hidden_entities($access_status);
-
$result = update_data("UPDATE {$CONFIG->dbprefix}entities
- set enabled='yes'
- where guid={$guid}");
- $entity->clearMetaData('disable_reason');
+ SET enabled = 'yes'
+ WHERE guid = $guid");
+
+ $entity->deleteMetadata('disable_reason');
+ $entity->enableMetadata();
+ $entity->enableAnnotations();
- return $result;
+ if ($recursive) {
+ $disabled_with_it = elgg_get_entities_from_relationship(array(
+ 'relationship' => 'disabled_with',
+ 'relationship_guid' => $entity->guid,
+ 'inverse_relationship' => true,
+ 'limit' => 0,
+ ));
+
+ foreach ($disabled_with_it as $e) {
+ $e->enable();
+ remove_entity_relationship($e->guid, 'disabled_with', $entity->guid);
+ }
+ }
}
}
}
- access_show_hidden_entities($access_status);
- return false;
+ access_show_hidden_entities($old_access_status);
+ return $result;
}
/**
- * Delete a given entity.
+ * Delete an entity.
+ *
+ * Removes an entity and its metadata, annotations, relationships, river entries,
+ * and private data.
+ *
+ * Optionally can remove entities contained and owned by $guid.
+ *
+ * @tip Use ElggEntity::delete() instead.
*
- * @param int $guid
- * @param bool $recursive If true (default) then all entities which are owned or contained by $guid will also be deleted.
- * Note: this bypasses ownership of sub items.
+ * @warning If deleting recursively, this bypasses ownership of items contained by
+ * the entity. That means that if the container_guid = $guid, the item will be deleted
+ * regardless of who owns it.
+ *
+ * @param int $guid The guid of the entity to delete
+ * @param bool $recursive If true (default) then all entities which are
+ * owned or contained by $guid will also be deleted.
+ *
+ * @return bool
+ * @access private
*/
function delete_entity($guid, $recursive = true) {
global $CONFIG, $ENTITY_CACHE;
$guid = (int)$guid;
if ($entity = get_entity($guid)) {
- if (trigger_elgg_event('delete', $entity->type, $entity)) {
+ if (elgg_trigger_event('delete', $entity->type, $entity)) {
if ($entity->canEdit()) {
// delete cache
if (isset($ENTITY_CACHE[$guid])) {
- invalidate_cache_for_entity($guid);
+ _elgg_invalidate_cache_for_entity($guid);
+ }
+
+ // If memcache is available then delete this entry from the cache
+ static $newentity_cache;
+ if ((!$newentity_cache) && (is_memcache_available())) {
+ $newentity_cache = new ElggMemcache('new_entity_cache');
+ }
+ if ($newentity_cache) {
+ $newentity_cache->delete($guid);
}
// Delete contained owned and otherwise releated objects (depth first)
if ($recursive) {
- // Temporary token overriding access controls TODO: Do this better.
+ // Temporary token overriding access controls
+ // @todo Do this better.
static $__RECURSIVE_DELETE_TOKEN;
// Make it slightly harder to guess
- $__RECURSIVE_DELETE_TOKEN = md5(get_loggedin_userid());
-
- $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) {
- $e->delete();
- }
+ $__RECURSIVE_DELETE_TOKEN = md5(elgg_get_logged_in_user_guid());
+
+ $entity_disable_override = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $ia = elgg_set_ignore_access(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);
$__RECURSIVE_DELETE_TOKEN = null;
+ elgg_set_ignore_access($ia);
}
+ $entity_disable_override = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $ia = elgg_set_ignore_access(true);
+
// Now delete the entity itself
- $entity->clearMetadata();
- $entity->clearAnnotations();
- $entity->clearRelationships();
- remove_from_river_by_subject($guid);
- remove_from_river_by_object($guid);
+ $entity->deleteMetadata();
+ $entity->deleteOwnedMetadata();
+ $entity->deleteAnnotations();
+ $entity->deleteOwnedAnnotations();
+ $entity->deleteRelationships();
+
+ access_show_hidden_entities($entity_disable_override);
+ elgg_set_ignore_access($ia);
+
+ elgg_delete_river(array('subject_guid' => $guid));
+ elgg_delete_river(array('object_guid' => $guid));
remove_all_private_settings($guid);
+
$res = delete_data("DELETE from {$CONFIG->dbprefix}entities where guid={$guid}");
if ($res) {
$sub_table = "";
@@ -2651,7 +1850,7 @@ function delete_entity($guid, $recursive = true) {
}
}
- return $res;
+ return (bool)$res;
}
}
}
@@ -2660,28 +1859,18 @@ function delete_entity($guid, $recursive = true) {
}
/**
- * Delete multiple entities that match a given query.
- * This function itterates through and calls delete_entity on each one, this is somewhat inefficient but lets
- * the 'delete' even be called for each entity.
- *
- * @deprecated 1.7. This is a dangerous function as it defaults to deleting everything.
- * @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
- */
-function delete_entities($type = "", $subtype = "", $owner_guid = 0) {
- elgg_deprecated_notice('delete_entities() was deprecated because no one should use it.', 1.7);
- return false;
-}
-
-/**
- * A plugin hook to get certain volitile (generated on the fly) attributes about an entity in order to export them.
- *
- * @param unknown_type $hook
- * @param unknown_type $entity_type
- * @param unknown_type $returnvalue
- * @param unknown_type $params The parameters, passed 'guid' and 'varname'
- * @return unknown
+ * Exports attributes generated on the fly (volatile) about an entity.
+ *
+ * @param string $hook volatile
+ * @param string $entity_type metadata
+ * @param string $returnvalue Return value from previous hook
+ * @param array $params The parameters, passed 'guid' and 'varname'
+ *
+ * @return ElggMetadata|null
+ * @elgg_plugin_hook_handler volatile metadata
+ * @todo investigate more.
+ * @access private
+ * @todo document
*/
function volatile_data_export_plugin_hook($hook, $entity_type, $returnvalue, $params) {
$guid = (int)$params['guid'];
@@ -2710,7 +1899,20 @@ function volatile_data_export_plugin_hook($hook, $entity_type, $returnvalue, $pa
}
/**
- * Handler called by trigger_plugin_hook on the "export" event.
+ * Exports all attributes of an entity.
+ *
+ * @warning Only exports fields in the entity and entity type tables.
+ *
+ * @param string $hook export
+ * @param string $entity_type all
+ * @param mixed $returnvalue Previous hook return value
+ * @param array $params Parameters
+ *
+ * @elgg_event_handler export all
+ * @return mixed
+ * @access private
+ *
+ * @throws InvalidParameterException|InvalidClassException
*/
function export_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
// Sanity check values
@@ -2727,7 +1929,8 @@ function export_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
// Get the entity
$entity = get_entity($guid);
if (!($entity instanceof ElggEntity)) {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, get_class()));
+ $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($guid, get_class()));
+ throw new InvalidClassException($msg);
}
$export = $entity->export();
@@ -2744,10 +1947,16 @@ function export_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
}
/**
- * Utility function used by import_entity_plugin_hook() to process an ODDEntity into an unsaved ElggEntity.
+ * Utility function used by import_entity_plugin_hook() to
+ * process an ODDEntity into an unsaved ElggEntity.
*
* @param ODDEntity $element The OpenDD element
+ *
* @return ElggEntity the unsaved entity which should be populated by items.
+ * @todo Remove this.
+ * @access private
+ *
+ * @throws ClassException|InstallationException|ImportException
*/
function oddentity_to_elggentity(ODDEntity $element) {
$class = $element->getAttribute('class');
@@ -2759,18 +1968,18 @@ function oddentity_to_elggentity(ODDEntity $element) {
if (!$tmp) {
// Construct new class with owner from session
$classname = get_subtype_class($class, $subclass);
- if ($classname!="") {
+ if ($classname) {
if (class_exists($classname)) {
$tmp = new $classname();
if (!($tmp instanceof ElggEntity)) {
- throw new ClassException(sprintf(elgg_echo('ClassException:ClassnameNotClass', $classname, get_class())));
+ $msg = elgg_echo('ClassException:ClassnameNotClass', array($classname, get_class()));
+ throw new ClassException($msg);
}
+ } else {
+ error_log(elgg_echo('ClassNotFoundException:MissingClass', array($classname)));
}
- else
- error_log(sprintf(elgg_echo('ClassNotFoundException:MissingClass'), $classname));
- }
- else {
+ } else {
switch ($class) {
case 'object' :
$tmp = new ElggObject($row);
@@ -2785,14 +1994,16 @@ function oddentity_to_elggentity(ODDEntity $element) {
$tmp = new ElggSite($row);
break;
default:
- throw new InstallationException(sprintf(elgg_echo('InstallationException:TypeNotSupported'), $class));
+ $msg = elgg_echo('InstallationException:TypeNotSupported', array($class));
+ throw new InstallationException($msg);
}
}
}
if ($tmp) {
if (!$tmp->import($element)) {
- throw new ImportException(sprintf(elgg_echo('ImportException:ImportFailed'), $element->getAttribute('uuid')));
+ $msg = elgg_echo('ImportException:ImportFailed', array($element->getAttribute('uuid')));
+ throw new ImportException($msg);
}
return $tmp;
@@ -2803,13 +2014,27 @@ function oddentity_to_elggentity(ODDEntity $element) {
/**
* Import an entity.
- * This function checks the passed XML doc (as array) to see if it is a user, if so it constructs a new
- * elgg user and returns "true" to inform the importer that it's been handled.
+ *
+ * This function checks the passed XML doc (as array) to see if it is
+ * a user, if so it constructs a new elgg user and returns "true"
+ * to inform the importer that it's been handled.
+ *
+ * @param string $hook import
+ * @param string $entity_type all
+ * @param mixed $returnvalue Value from previous hook
+ * @param mixed $params Array of params
+ *
+ * @return mixed
+ * @elgg_plugin_hook_handler import all
+ * @todo document
+ * @access private
+ *
+ * @throws ImportException
*/
function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
$element = $params['element'];
- $tmp = NULL;
+ $tmp = null;
if ($element instanceof ODDEntity) {
$tmp = oddentity_to_elggentity($element);
@@ -2817,7 +2042,8 @@ function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
if ($tmp) {
// Make sure its saved
if (!$tmp->save()) {
- throw new ImportException(sprintf(elgg_echo('ImportException:ProblemSaving'), $element->getAttribute('uuid')));
+ $msg = elgg_echo('ImportException:ProblemSaving', array($element->getAttribute('uuid')));
+ throw new ImportException($msg);
}
// Belts and braces
@@ -2834,32 +2060,34 @@ function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {
}
/**
- * Determines whether or not the specified user can edit the specified entity.
+ * Returns if $user_guid is able to edit $entity_guid.
+ *
+ * @tip Can be overridden by by registering for the permissions_check
+ * plugin hook.
*
- * This is extendible by registering a plugin hook taking in the parameters 'entity' and 'user',
- * which are the entity and user entities respectively
+ * @warning If a $user_guid is not passed it will default to the logged in user.
*
- * @see register_plugin_hook
+ * @tip Use ElggEntity::canEdit() instead.
*
* @param int $entity_guid The GUID of the entity
- * @param int $user_guid The GUID of the user
- * @return true|false Whether the specified user can edit the specified entity.
+ * @param int $user_guid The GUID of the user
+ *
+ * @return bool
+ * @link http://docs.elgg.org/Entities/AccessControl
*/
function can_edit_entity($entity_guid, $user_guid = 0) {
- global $CONFIG;
-
$user_guid = (int)$user_guid;
$user = get_entity($user_guid);
if (!$user) {
- $user = get_loggedin_user();
+ $user = elgg_get_logged_in_user_entity();
}
+ $return = false;
if ($entity = get_entity($entity_guid)) {
- $return = false;
// Test user if possible - should default to false unless a plugin hook says otherwise
if ($user) {
- if ($entity->getOwner() == $user->getGUID()) {
+ if ($entity->getOwnerGUID() == $user->getGUID()) {
$return = true;
}
if ($entity->container_guid == $user->getGUID()) {
@@ -2874,120 +2102,62 @@ function can_edit_entity($entity_guid, $user_guid = 0) {
}
}
}
+ }
- return trigger_plugin_hook('permissions_check', $entity->type,
+ return elgg_trigger_plugin_hook('permissions_check', $entity->type,
array('entity' => $entity, 'user' => $user), $return);
-
- } else {
- return false;
- }
}
/**
- * Determines whether or not the specified user can edit metadata on the specified entity.
+ * Returns if $user_guid can edit the metadata on $entity_guid.
*
- * This is extendible by registering a plugin hook taking in the parameters 'entity' and 'user',
- * which are the entity and user entities respectively
+ * @tip Can be overridden by by registering for the permissions_check:metadata
+ * plugin hook.
*
- * @see register_plugin_hook
+ * @warning If a $user_guid isn't specified, the currently logged in user is used.
*
- * @param int $entity_guid The GUID of the entity
- * @param int $user_guid The GUID of the user
- * @param ElggMetadata $metadata The metadata to specifically check (if any; default null)
- * @return true|false Whether the specified user can edit the specified entity.
+ * @param int $entity_guid The GUID of the entity
+ * @param int $user_guid The GUID of the user
+ * @param ElggMetadata $metadata The metadata to specifically check (if any; default null)
+ *
+ * @return bool
+ * @see elgg_register_plugin_hook_handler()
*/
function can_edit_entity_metadata($entity_guid, $user_guid = 0, $metadata = null) {
if ($entity = get_entity($entity_guid)) {
$return = null;
- if ($metadata->owner_guid == 0) {
+ if ($metadata && ($metadata->owner_guid == 0)) {
$return = true;
}
if (is_null($return)) {
$return = can_edit_entity($entity_guid, $user_guid);
}
- $user = get_entity($user_guid);
- $return = trigger_plugin_hook('permissions_check:metadata',$entity->type,array('entity' => $entity, 'user' => $user, 'metadata' => $metadata),$return);
+ if ($user_guid) {
+ $user = get_entity($user_guid);
+ } else {
+ $user = elgg_get_logged_in_user_entity();
+ }
+
+ $params = array('entity' => $entity, 'user' => $user, 'metadata' => $metadata);
+ $return = elgg_trigger_plugin_hook('permissions_check:metadata', $entity->type, $params, $return);
return $return;
} else {
return false;
}
}
-
/**
- * Get the icon for an entity
+ * Returns the URL for an entity.
*
- * @param ElggEntity $entity The entity (passed an entity rather than a guid to handle non-created entities)
- * @param string $size
- */
-function get_entity_icon_url(ElggEntity $entity, $size = 'medium') {
- global $CONFIG;
-
- $size = sanitise_string($size);
- switch (strtolower($size)) {
- case 'master':
- $size = 'master';
- break;
-
- case 'large' :
- $size = 'large';
- break;
-
- case 'topbar' :
- $size = 'topbar';
- break;
-
- case 'tiny' :
- $size = 'tiny';
- break;
-
- case 'small' :
- $size = 'small';
- break;
-
- case 'medium' :
- default:
- $size = 'medium';
- }
-
- $url = false;
-
- $viewtype = elgg_get_viewtype();
-
- // Step one, see if anyone knows how to render this in the current view
- $url = trigger_plugin_hook('entity:icon:url', $entity->getType(), array('entity' => $entity, 'viewtype' => $viewtype, 'size' => $size), $url);
-
- // Fail, so use default
- if (!$url) {
- $type = $entity->getType();
- $subtype = $entity->getSubtype();
-
- if (!empty($subtype)) {
- $overrideurl = elgg_view("icon/{$type}/{$subtype}/{$size}",array('entity' => $entity));
- if (!empty($overrideurl)) {
- return $overrideurl;
- }
- }
-
- $overrideurl = elgg_view("icon/{$type}/default/{$size}",array('entity' => $entity));
- if (!empty($overrideurl)) {
- return $overrideurl;
- }
-
- $url = $CONFIG->url . "_graphics/icons/default/$size.png";
- }
-
- return $url;
-}
-
-/**
- * Gets the URL for an entity, given a particular GUID
+ * @tip Can be overridden with {@link register_entity_url_handler()}.
*
* @param int $entity_guid The GUID of the entity
+ *
* @return string The URL of the entity
+ * @see register_entity_url_handler()
*/
function get_entity_url($entity_guid) {
global $CONFIG;
@@ -2996,27 +2166,27 @@ function get_entity_url($entity_guid) {
$url = "";
if (isset($CONFIG->entity_url_handler[$entity->getType()][$entity->getSubType()])) {
- $function = $CONFIG->entity_url_handler[$entity->getType()][$entity->getSubType()];
+ $function = $CONFIG->entity_url_handler[$entity->getType()][$entity->getSubType()];
if (is_callable($function)) {
- $url = $function($entity);
+ $url = call_user_func($function, $entity);
}
} elseif (isset($CONFIG->entity_url_handler[$entity->getType()]['all'])) {
- $function = $CONFIG->entity_url_handler[$entity->getType()]['all'];
+ $function = $CONFIG->entity_url_handler[$entity->getType()]['all'];
if (is_callable($function)) {
- $url = $function($entity);
+ $url = call_user_func($function, $entity);
}
} elseif (isset($CONFIG->entity_url_handler['all']['all'])) {
- $function = $CONFIG->entity_url_handler['all']['all'];
+ $function = $CONFIG->entity_url_handler['all']['all'];
if (is_callable($function)) {
- $url = $function($entity);
+ $url = call_user_func($function, $entity);
}
}
if ($url == "") {
- $url = $CONFIG->url . "pg/view/" . $entity_guid;
+ $url = "view/" . $entity_guid;
}
- return $url;
+ return elgg_normalize_url($url);
}
return false;
@@ -3025,15 +2195,19 @@ function get_entity_url($entity_guid) {
/**
* Sets the URL handler for a particular entity type and subtype
*
- * @param string $function_name The function to register
- * @param string $entity_type The entity type
+ * @param string $entity_type The entity type
* @param string $entity_subtype The entity subtype
- * @return true|false Depending on success
+ * @param string $function_name The function to register
+ *
+ * @return bool Depending on success
+ * @see get_entity_url()
+ * @see ElggEntity::getURL()
+ * @since 1.8.0
*/
-function register_entity_url_handler($function_name, $entity_type = "all", $entity_subtype = "all") {
+function elgg_register_entity_url_handler($entity_type, $entity_subtype, $function_name) {
global $CONFIG;
- if (!is_callable($function_name)) {
+ if (!is_callable($function_name, true)) {
return false;
}
@@ -3051,88 +2225,101 @@ function register_entity_url_handler($function_name, $entity_type = "all", $enti
}
/**
- * Default Icon URL handler for entities.
- * This will attempt to find a default entity for the current view and return a url. This is registered at
- * a low priority so that other handlers will pick it up first.
- *
- * @param unknown_type $hook
- * @param unknown_type $entity_type
- * @param unknown_type $returnvalue
- * @param unknown_type $params
+ * Registers an entity type and subtype as a public-facing entity that should
+ * be shown in search and by {@link elgg_list_registered_entities()}.
+ *
+ * @warning Entities that aren't registered here will not show up in search.
+ *
+ * @tip Add a language string item:type:subtype to make sure the items are display properly.
+ *
+ * @param string $type The type of entity (object, site, user, group)
+ * @param string $subtype The subtype to register (may be blank)
+ *
+ * @return bool Depending on success
+ * @see get_registered_entity_types()
+ * @link http://docs.elgg.org/Search
+ * @link http://docs.elgg.org/Tutorials/Search
*/
-function default_entity_icon_hook($hook, $entity_type, $returnvalue, $params) {
+function elgg_register_entity_type($type, $subtype = null) {
global $CONFIG;
- if ((!$returnvalue) && ($hook == 'entity:icon:url')) {
- $entity = $params['entity'];
- $type = $entity->type;
- $subtype = get_subtype_from_id($entity->subtype);
- $viewtype = $params['viewtype'];
- $size = $params['size'];
-
- $url = "views/$viewtype/graphics/icons/$type/$subtype/$size.png";
+ $type = strtolower($type);
+ if (!in_array($type, $CONFIG->entity_types)) {
+ return FALSE;
+ }
- if (!@file_exists($CONFIG->path . $url)) {
- $url = "views/$viewtype/graphics/icons/$type/default/$size.png";
- }
+ if (!isset($CONFIG->registered_entities)) {
+ $CONFIG->registered_entities = array();
+ }
- if(!@file_exists($CONFIG->path . $url)) {
- $url = "views/$viewtype/graphics/icons/default/$size.png";
- }
+ if (!isset($CONFIG->registered_entities[$type])) {
+ $CONFIG->registered_entities[$type] = array();
+ }
- if (@file_exists($CONFIG->path . $url)) {
- return $CONFIG->url . $url;
- }
+ if ($subtype) {
+ $CONFIG->registered_entities[$type][] = $subtype;
}
+
+ return TRUE;
}
/**
- * Registers and entity type and subtype to return in search and other places.
- * A description in the elgg_echo languages file of the form item:type:subtype
- * is also expected.
+ * Unregisters an entity type and subtype as a public-facing entity.
+ *
+ * @warning With a blank subtype, it unregisters that entity type including
+ * all subtypes. This must be called after all subtypes have been registered.
*
- * @param string $type The type of entity (object, site, user, group)
+ * @param string $type The type of entity (object, site, user, group)
* @param string $subtype The subtype to register (may be blank)
- * @return true|false Depending on success
+ *
+ * @return bool Depending on success
+ * @see elgg_register_entity_type()
*/
-function register_entity_type($type, $subtype) {
+function unregister_entity_type($type, $subtype) {
global $CONFIG;
$type = strtolower($type);
- if (!in_array($type, array('object','site','group','user'))) {
- return false;
+ if (!in_array($type, $CONFIG->entity_types)) {
+ return FALSE;
}
if (!isset($CONFIG->registered_entities)) {
- $CONFIG->registered_entities = array();
+ return FALSE;
}
if (!isset($CONFIG->registered_entities[$type])) {
- $CONFIG->registered_entities[$type] = array();
+ return FALSE;
}
if ($subtype) {
- $CONFIG->registered_entities[$type][] = $subtype;
+ if (in_array($subtype, $CONFIG->registered_entities[$type])) {
+ $key = array_search($subtype, $CONFIG->registered_entities[$type]);
+ unset($CONFIG->registered_entities[$type][$key]);
+ } else {
+ return FALSE;
+ }
+ } else {
+ unset($CONFIG->registered_entities[$type]);
}
- return true;
+ return TRUE;
}
/**
* Returns registered entity types and subtypes
*
- * @see register_entity_type
- *
* @param string $type The type of entity (object, site, user, group) or blank for all
+ *
* @return array|false Depending on whether entities have been registered
+ * @see elgg_register_entity_type()
*/
-function get_registered_entity_types($type = '') {
+function get_registered_entity_types($type = null) {
global $CONFIG;
if (!isset($CONFIG->registered_entities)) {
return false;
}
- if (!empty($type)) {
+ if ($type) {
$type = strtolower($type);
}
if (!empty($type) && empty($CONFIG->registered_entities[$type])) {
@@ -3147,75 +2334,51 @@ function get_registered_entity_types($type = '') {
}
/**
- * Determines whether or not the specified entity type and subtype have been registered in the system
+ * Returns if the entity type and subtype have been registered with {@see elgg_register_entity_type()}.
*
- * @param string $type The type of entity (object, site, user, group)
+ * @param string $type The type of entity (object, site, user, group)
* @param string $subtype The subtype (may be blank)
- * @return true|false Depending on whether or not the type has been registered
+ *
+ * @return bool Depending on whether or not the type has been registered
*/
-function is_registered_entity_type($type, $subtype) {
+function is_registered_entity_type($type, $subtype = null) {
global $CONFIG;
if (!isset($CONFIG->registered_entities)) {
return false;
}
+
$type = strtolower($type);
- if (empty($CONFIG->registered_entities[$type])) {
+
+ // @todo registering a subtype implicitly registers the type.
+ // see #2684
+ if (!isset($CONFIG->registered_entities[$type])) {
return false;
}
- if (in_array($subtype, $CONFIG->registered_entities[$type])) {
- return true;
- }
+ if ($subtype && !in_array($subtype, $CONFIG->registered_entities[$type])) {
+ return false;
+ }
+ return true;
}
/**
* Page handler for generic entities view system
*
* @param array $page Page elements from pain page handler
+ *
+ * @return bool
+ * @elgg_page_handler view
+ * @access private
*/
function entities_page_handler($page) {
if (isset($page[0])) {
global $CONFIG;
- set_input('guid',$page[0]);
- include($CONFIG->path . "entities/index.php");
- }
-}
-
-/**
- * @deprecated 1.7. Use elgg_list_registered_entities().
- * @param $owner_guid
- * @param $limit
- * @param $fullview
- * @param $viewtypetoggle
- * @param $allowedtypes
- * @return unknown_type
- */
-function list_registered_entities($owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = false, $allowedtypes = true) {
- elgg_deprecated_notice('list_registered_entities() was deprecated by elgg_list_registered_entities().', 1.7);
-
- $options = array();
-
- // don't want to send anything if not being used.
- if ($owner_guid) {
- $options['owner_guid'] = $owner_guid;
- }
-
- if ($limit) {
- $options['limit'] = $limit;
- }
-
- if ($allowedtypes) {
- $options['allowed_types'] = $allowedtypes;
+ set_input('guid', $page[0]);
+ include($CONFIG->path . "pages/entities/index.php");
+ return true;
}
-
- // need to send because might be BOOL
- $options['full_view'] = $fullview;
- $options['view_type_toggle'] = $viewtypetoggle;
-
- $options['offset'] = get_input('offset', 0);
-
- return elgg_list_registered_entities($options);
+ return false;
}
/**
@@ -3227,473 +2390,93 @@ function list_registered_entities($owner_guid = 0, $limit = 10, $fullview = true
*
* full_view => BOOL Display full view entities
*
- * view_type_toggle => BOOL Display gallery / list switch
+ * list_type_toggle => BOOL Display gallery / list switch
*
* allowed_types => TRUE|ARRAY True to show all types or an array of valid types.
*
* pagination => BOOL Display pagination links
*
* @return string A viewable list of entities
+ * @since 1.7.0
*/
-function elgg_list_registered_entities($options) {
+function elgg_list_registered_entities(array $options = array()) {
+ global $autofeed;
+ $autofeed = true;
+
$defaults = array(
'full_view' => TRUE,
'allowed_types' => TRUE,
- 'view_type_toggle' => FALSE,
+ 'list_type_toggle' => FALSE,
'pagination' => TRUE,
- 'offset' => 0
+ 'offset' => 0,
+ 'types' => array(),
+ 'type_subtype_pairs' => array()
);
$options = array_merge($defaults, $options);
- $typearray = array();
-
- if ($object_types = get_registered_entity_types()) {
- foreach($object_types as $object_type => $subtype_array) {
- if (in_array($object_type, $options['allowed_types']) || $options['allowed_types'] === TRUE) {
- $typearray[$object_type] = array();
-
- if (is_array($subtype_array) && count($subtype_array)) {
- foreach ($subtype_array as $subtype) {
- $typearray[$object_type][] = $subtype;
- }
- }
- }
- }
- }
-
- $options['type_subtype_pairs'] = $typearray;
-
- $count = elgg_get_entities(array_merge(array('count' => TRUE), $options));
- $entities = elgg_get_entities($options);
-
- return elgg_view_entity_list($entities, $count, $options['offset'],
- $options['limit'], $options['full_view'], $options['view_type_toggle'], $options['pagination']);
-}
-
-/**
- * Get entities based on their private data, in a similar way to metadata.
- *
- * @param string $name The name of the setting
- * @param string $value The value of the setting
- * @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 rather than the entities themselves (limits and offsets don't apply in this context). Defaults to false.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @param int|array $container_guid The container or containers to get entities from (default: all containers).
- * @return array A list of entities.
- */
-function get_entities_from_private_setting($name = "", $value = "", $type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = null) {
- global $CONFIG;
-
- if ($subtype === false || $subtype === null || $subtype === 0) {
- return false;
- }
- $name = sanitise_string($name);
- $value = sanitise_string($value);
-
- if ($order_by == "") {
- $order_by = "e.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;
+ //backwards compatibility
+ if (isset($options['view_type_toggle'])) {
+ $options['list_type_toggle'] = $options['view_type_toggle'];
}
- $where = array();
+ $types = get_registered_entity_types();
- if (is_array($type)) {
- $tempwhere = "";
- if (sizeof($type)) {
- foreach($type as $typekey => $subtypearray) {
- foreach($subtypearray as $subtypeval) {
- $typekey = sanitise_string($typekey);
- if (!empty($subtypeval)) {
- if (!$subtypeval = (int) get_subtype_id($typekey, $subtypeval)) {
- return false;
- }
- } else {
- $subtypeval = 0;
- }
- if (!empty($tempwhere)) $tempwhere .= " or ";
- $tempwhere .= "(e.type = '{$typekey}' and e.subtype = {$subtypeval})";
+ foreach ($types as $type => $subtype_array) {
+ if (in_array($type, $options['allowed_types']) || $options['allowed_types'] === TRUE) {
+ // you must explicitly register types to show up in here and in search for objects
+ if ($type == 'object') {
+ if (is_array($subtype_array) && count($subtype_array)) {
+ $options['type_subtype_pairs'][$type] = $subtype_array;
}
- }
- }
- if (!empty($tempwhere)) {
- $where[] = "({$tempwhere})";
- }
- } else {
- $type = sanitise_string($type);
- if ($subtype AND !$subtype = get_subtype_id($type, $subtype)) {
- return false;
- }
-
- 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[] = "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 = array_map("sanitise_int", $owner_guid);
- // $owner_guid = implode(",",$owner_guid);
- // $where[] = "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}";
- }
- }
-
- if ($name!="") {
- $where[] = "s.name = '$name'";
- }
-
- if ($value!="") {
- $where[] = "s.value='$value'";
- }
-
- if (!$count) {
- $query = "SELECT distinct e.*
- from {$CONFIG->dbprefix}entities e
- JOIN {$CONFIG->dbprefix}private_settings s ON e.guid=s.entity_guid where ";
- } else {
- $query = "SELECT count(distinct e.guid) as total
- from {$CONFIG->dbprefix}entities e JOIN {$CONFIG->dbprefix}private_settings s
- ON e.guid=s.entity_guid where ";
- }
- foreach ($where as $w) {
- $query .= " $w and ";
- }
- // Add access controls
- $query .= get_access_sql_suffix('e');
- if (!$count) {
- $query .= " order by $order_by";
- if ($limit) {
- // Add order and limit
- $query .= " limit $offset, $limit";
- }
-
- $dt = get_data($query, "entity_row_to_elggstar");
- return $dt;
- } else {
- $total = get_data_row($query);
- return $total->total;
- }
-}
-
-/**
- * Get entities based on their private data by multiple keys, in a similar way to metadata.
- *
- * @param string $name The name of the setting
- * @param string $value The value of the setting
- * @param string|array $type The type of entity (eg "user", "object" etc) or array(type1 => array('subtype1', ...'subtypeN'), ...)
- * @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 rather than the entities themselves (limits and offsets don't apply in this context). Defaults to false.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @param int|array $container_guid The container or containers to get entities from (default: all containers).
- * @return array A list of entities.
- */
-function get_entities_from_private_setting_multi(array $name, $type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid = null) {
- global $CONFIG;
-
- if ($subtype === false || $subtype === null || $subtype === 0) {
- return false;
- }
-
- if ($order_by == "") {
- $order_by = "e.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;
- }
-
- $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)) {
- if (!$subtypeval = (int) get_subtype_id($typekey, $subtypeval)) {
- return false;
- }
- } else {
- $subtypeval = 0;
- }
- if (!empty($tempwhere)) $tempwhere .= " or ";
- $tempwhere .= "(e.type = '{$typekey}' and e.subtype = {$subtypeval})";
+ } else {
+ if (is_array($subtype_array) && count($subtype_array)) {
+ $options['type_subtype_pairs'][$type] = $subtype_array;
+ } else {
+ $options['type_subtype_pairs'][$type] = ELGG_ENTITIES_ANY_VALUE;
}
}
}
- if (!empty($tempwhere)) {
- $where[] = "({$tempwhere})";
- }
-
- } else {
- $type = sanitise_string($type);
- if ($subtype AND !$subtype = get_subtype_id($type, $subtype)) {
- return false;
- }
-
- 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[] = "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 = array_map("sanitise_int", $owner_guid);
- // $owner_guid = implode(",",$owner_guid);
- // $where[] = "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}";
- }
- }
-
- if ($name) {
- $s_join = "";
- $i = 1;
- foreach ($name as $k => $n) {
- $k = sanitise_string($k);
- $s_join .= " JOIN {$CONFIG->dbprefix}private_settings s$i ON e.guid=s$i.entity_guid";
- $where[] = "s$i.name = '$k'";
- $where[] = "s$i.value = '$n'";
- $i++;
- }
- }
-
- if (!$count) {
- $query = "SELECT distinct e.* from {$CONFIG->dbprefix}entities e $s_join where ";
- } else {
- $query = "SELECT count(distinct e.guid) as total
- from {$CONFIG->dbprefix}entities e $s_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";
- // Add order and limit
- if ($limit) {
- $query .= " limit $offset, $limit";
- }
-
- $dt = get_data($query, "entity_row_to_elggstar");
- return $dt;
+ if (!empty($options['type_subtype_pairs'])) {
+ $count = elgg_get_entities(array_merge(array('count' => TRUE), $options));
+ $entities = elgg_get_entities($options);
} else {
- $total = get_data_row($query);
- return $total->total;
+ $count = 0;
+ $entities = array();
}
-}
-
-/**
- * Gets a private setting for an entity.
- *
- * @param int $entity_guid The entity GUID
- * @param string $name The name of the setting
- * @return mixed The setting value, or false on failure
- */
-function get_private_setting($entity_guid, $name) {
- global $CONFIG;
- $entity_guid = (int) $entity_guid;
- $name = sanitise_string($name);
- if ($setting = get_data_row("SELECT value from {$CONFIG->dbprefix}private_settings where name = '{$name}' and entity_guid = {$entity_guid}")) {
- return $setting->value;
- }
- return false;
+ $options['count'] = $count;
+ return elgg_view_entity_list($entities, $options);
}
/**
- * Return an array of all private settings for a given
- *
- * @param int $entity_guid The entity GUID
- */
-function get_all_private_settings($entity_guid) {
- global $CONFIG;
-
- $entity_guid = (int) $entity_guid;
-
- $result = get_data("SELECT * from {$CONFIG->dbprefix}private_settings where entity_guid = {$entity_guid}");
- if ($result) {
- $return = array();
- foreach ($result as $r) {
- $return[$r->name] = $r->value;
- }
-
- return $return;
- }
-
- return false;
-}
-
-/**
- * Sets a private setting for an entity.
- *
- * @param int $entity_guid The entity GUID
- * @param string $name The name of the setting
- * @param string $value The value of the setting
- * @return mixed The setting ID, or false on failure
- */
-function set_private_setting($entity_guid, $name, $value) {
- global $CONFIG;
-
- $entity_guid = (int) $entity_guid;
- $name = sanitise_string($name);
- $value = sanitise_string($value);
-
- $result = insert_data("INSERT into {$CONFIG->dbprefix}private_settings
- (entity_guid, name, value) VALUES
- ($entity_guid, '{$name}', '{$value}')
- ON DUPLICATE KEY UPDATE value='$value'");
- if ($result === 0) {
- return true;
- }
- return $result;
-}
-
-/**
- * Deletes a private setting for an entity.
- *
- * @param int $entity_guid The Entity GUID
- * @param string $name The name of the setting
- * @return true|false depending on success
- *
- */
-function remove_private_setting($entity_guid, $name) {
- global $CONFIG;
-
- $entity_guid = (int) $entity_guid;
- $name = sanitise_string($name);
-
- return delete_data("DELETE from {$CONFIG->dbprefix}private_settings
- where name = '{$name}'
- and entity_guid = {$entity_guid}");
-}
-
-/**
- * Deletes all private settings for an entity.
+ * Checks if $entity is an ElggEntity and optionally for type and subtype.
*
- * @param int $entity_guid The Entity GUID
- * @return true|false depending on success
+ * @tip Use this function in actions and views to check that you are dealing
+ * with the correct type of entity.
*
- */
-function remove_all_private_settings($entity_guid) {
- global $CONFIG;
-
- $entity_guid = (int) $entity_guid;
- return delete_data("DELETE from {$CONFIG->dbprefix}private_settings
- where entity_guid = {$entity_guid}");
-}
-
-/*
- * Check the recurisve delete permissions token.
+ * @param mixed $entity Entity
+ * @param string $type Entity type
+ * @param string $subtype Entity subtype
+ * @param string $class Class name
*
* @return bool
- */
-function recursive_delete_permissions_check($hook, $entity_type, $returnvalue, $params) {
- static $__RECURSIVE_DELETE_TOKEN;
-
- $entity = $params['entity'];
-
- if ((isloggedin()) && ($__RECURSIVE_DELETE_TOKEN) && (strcmp($__RECURSIVE_DELETE_TOKEN, md5(get_loggedin_userid())))) {
- return true;
- }
-
- // consult next function
- return NULL;
-}
-
-/**
- * Checks if $entity is an ElggEntity and optionally for type and subtype.
- *
- * @param $entity
- * @param $type
- * @param $subtype
- * @return Bool
- * @since 1.8
+ * @since 1.8.0
*/
function elgg_instanceof($entity, $type = NULL, $subtype = NULL, $class = NULL) {
$return = ($entity instanceof ElggEntity);
if ($type) {
+ /* @var ElggEntity $entity */
$return = $return && ($entity->getType() == $type);
}
if ($subtype) {
$return = $return && ($entity->getSubtype() == $subtype);
}
-
+
if ($class) {
$return = $return && ($entity instanceof $class);
}
@@ -3701,25 +2484,32 @@ function elgg_instanceof($entity, $type = NULL, $subtype = NULL, $class = NULL)
return $return;
}
-
/**
- * Update last_action on the given entity.
+ * Update the last_action column in the entities table for $guid.
+ *
+ * @warning This is different to time_updated. Time_updated is automatically set,
+ * while last_action is only set when explicitly called.
*
- * @param int $guid Entity annotation|relationship action carried out on
+ * @param int $guid Entity annotation|relationship action carried out on
* @param int $posted Timestamp of last action
- **/
-function update_entity_last_action($guid, $posted = NULL){
+ *
+ * @return bool
+ * @access private
+ */
+function update_entity_last_action($guid, $posted = NULL) {
global $CONFIG;
$guid = (int)$guid;
+ $posted = (int)$posted;
if (!$posted) {
$posted = time();
}
- if ($guid){
+ if ($guid) {
//now add to the river updated table
- $query = update_data("UPDATE {$CONFIG->dbprefix}entities SET last_action = {$posted} WHERE guid = {$guid}");
- if ($query) {
+ $query = "UPDATE {$CONFIG->dbprefix}entities SET last_action = {$posted} WHERE guid = {$guid}";
+ $result = update_data($query);
+ if ($result) {
return TRUE;
} else {
return FALSE;
@@ -3732,24 +2522,38 @@ function update_entity_last_action($guid, $posted = NULL){
/**
* Garbage collect stub and fragments from any broken delete/create calls
*
- * @param unknown_type $hook
- * @param unknown_type $user
- * @param unknown_type $returnvalue
- * @param unknown_type $tag
+ * @return void
+ * @elgg_plugin_hook_handler gc system
+ * @access private
*/
-function entities_gc($hook, $user, $returnvalue, $tag) {
+function entities_gc() {
global $CONFIG;
- $tables = array ('sites_entity', 'objects_entity', 'groups_entity', 'users_entity');
+ $tables = array(
+ 'site' => 'sites_entity',
+ 'object' => 'objects_entity',
+ 'group' => 'groups_entity',
+ 'user' => 'users_entity'
+ );
- foreach ($tables as $table) {
- delete_data("DELETE from {$CONFIG->dbprefix}{$table}
- where guid NOT IN (SELECT guid from {$CONFIG->dbprefix}entities)");
+ foreach ($tables as $type => $table) {
+ delete_data("DELETE FROM {$CONFIG->dbprefix}{$table}
+ WHERE guid NOT IN (SELECT guid FROM {$CONFIG->dbprefix}entities)");
+ delete_data("DELETE FROM {$CONFIG->dbprefix}entities
+ WHERE type = '$type' AND guid NOT IN (SELECT guid FROM {$CONFIG->dbprefix}{$table})");
}
}
/**
- * Runs unit tests for the entities object.
+ * Runs unit tests for the entity objects.
+ *
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of tests
+ * @param mixed $params Params
+ *
+ * @return array
+ * @access private
*/
function entities_test($hook, $type, $value, $params) {
global $CONFIG;
@@ -3758,33 +2562,29 @@ function entities_test($hook, $type, $value, $params) {
}
/**
- * Entities init function; establishes the page handler
+ * Entities init function; establishes the default entity page handler
*
+ * @return void
+ * @elgg_event_handler init system
+ * @access private
*/
function entities_init() {
- register_page_handler('view','entities_page_handler');
+ elgg_register_page_handler('view', 'entities_page_handler');
- register_plugin_hook('unit_test', 'system', 'entities_test');
+ elgg_register_plugin_hook_handler('unit_test', 'system', 'entities_test');
- // Allow a permission override for recursive entity deletion
- // TODO: Can this be done better?
- register_plugin_hook('permissions_check','all','recursive_delete_permissions_check');
- register_plugin_hook('permissions_check:metadata','all','recursive_delete_permissions_check');
-
- register_plugin_hook('gc','system','entities_gc');
+ elgg_register_plugin_hook_handler('gc', 'system', 'entities_gc');
}
/** Register the import hook */
-register_plugin_hook("import", "all", "import_entity_plugin_hook", 0);
+elgg_register_plugin_hook_handler("import", "all", "import_entity_plugin_hook", 0);
/** Register the hook, ensuring entities are serialised first */
-register_plugin_hook("export", "all", "export_entity_plugin_hook", 0);
+elgg_register_plugin_hook_handler("export", "all", "export_entity_plugin_hook", 0);
/** Hook to get certain named bits of volatile data about an entity */
-register_plugin_hook('volatile', 'metadata', 'volatile_data_export_plugin_hook');
-
-/** Hook for rendering a default icon for entities */
-register_plugin_hook('entity:icon:url', 'all', 'default_entity_icon_hook', 1000);
+elgg_register_plugin_hook_handler('volatile', 'metadata', 'volatile_data_export_plugin_hook');
/** Register init system event **/
-register_elgg_event_handler('init','system','entities_init');
+elgg_register_event_handler('init', 'system', 'entities_init');
+
diff --git a/engine/lib/exceptions.php b/engine/lib/exceptions.php
deleted file mode 100644
index ccf017062..000000000
--- a/engine/lib/exceptions.php
+++ /dev/null
@@ -1,161 +0,0 @@
-<?php
-/**
- * Exceptions.
- * Define some globally useful exception classes.
- *
- * @package Elgg
- * @subpackage Exceptions
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
- */
-
-// Top level //////////////////////////////////////////////////////////////////////////////
-
-/**
- * IOException
- * An IO Exception, throw when an IO Exception occurs. Subclass for specific IO Exceptions.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class IOException extends Exception {}
-
-/**
- * ClassException
- * A class Exception, throw when there is a class error.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class ClassException extends Exception {}
-
-/**
- * ConfigurationException
- * There is a configuration error
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class ConfigurationException extends Exception {}
-
-/**
- * SecurityException
- * An Security Exception, throw when a Security Exception occurs. Subclass for specific Security Execeptions (access problems etc)
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class SecurityException extends Exception {}
-
-/**
- * ClassNotFoundException
- * An database exception, throw when a database exception happens, subclass if more detail is needed.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class DatabaseException extends Exception {}
-
-/**
- * APIException
- * The API Exception class, thrown by the API layer when an API call has an issue.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class APIException extends Exception {}
-
-/**
- * CallException
- * An exception thrown when there is a problem calling something.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class CallException extends Exception {}
-
-/**
- * Data format exception
- * An exception thrown when there is a problem in the format of some data.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class DataFormatException extends Exception {}
-
-// Class exceptions ///////////////////////////////////////////////////////////////////////
-
-/**
- * InvalidClassException
- * An invalid class Exception, throw when a class is invalid.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class InvalidClassException extends ClassException {}
-
-/**
- * ClassNotFoundException
- * An Class not found Exception, throw when an class can not be found occurs.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class ClassNotFoundException extends ClassException {}
-
-// Configuration exceptions ///////////////////////////////////////////////////////////////
-
-/**
- * InstallationException
- * Thrown when there is a major problem with the installation.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class InstallationException extends ConfigurationException {}
-
-// Call exceptions ////////////////////////////////////////////////////////////////////////
-
-/**
- * NotImplementedException
- * Thrown when a method or function has not been implemented, primarily used in development... you should
- * not see these!
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class NotImplementedException extends CallException {}
-
-/**
- * InvalidParameterException
- * A parameter is invalid.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class InvalidParameterException extends CallException {}
-
-// Installation exception /////////////////////////////////////////////////////////////////
-
-/**
- * RegistrationException
- * Could not register a new user for whatever reason.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Exceptions
- */
-class RegistrationException extends InstallationException {} \ No newline at end of file
diff --git a/engine/lib/export.php b/engine/lib/export.php
index c541b583b..ecc894e63 100644
--- a/engine/lib/export.php
+++ b/engine/lib/export.php
@@ -2,71 +2,16 @@
/**
* Elgg Data import export functionality.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage DataModel.Export
*/
/**
- * Define an interface for all ODD exportable objects.
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- */
-interface Exportable {
- /**
- * This must take the contents of the object and convert it to exportable ODD
- * @return object or array of objects.
- */
- public function export();
-
- /**
- * Return a list of all fields that can be exported.
- * This should be used as the basis for the values returned by export()
- */
- public function getExportableValues();
-}
-
-/**
- * Define an interface for all ODD importable objects.
- * @author Curverider Ltd
- */
-interface Importable {
- /**
- * Accepts an array of data to import, this data is parsed from the XML produced by export.
- * The function should return the constructed object data, or NULL.
- *
- * @param ODD $data
- * @return bool
- * @throws ImportException if there was a critical error importing data.
- */
- public function import(ODD $data);
-}
-
-/**
- * Export exception
- *
- * @package Elgg
- * @subpackage Exceptions
- *
- */
-class ExportException extends DataFormatException {}
-
-/**
- * Import exception
- *
- * @package Elgg
- * @subpackage Exceptions
- */
-class ImportException extends DataFormatException {}
-
-/**
* Get a UUID from a given object.
*
- * @param $object The object either an ElggEntity, ElggRelationship or ElggExtender
- * @return the UUID or false
+ * @param mixed $object The object either an ElggEntity, ElggRelationship or ElggExtender
+ *
+ * @return string|false the UUID or false
*/
function get_uuid_from_object($object) {
if ($object instanceof ElggEntity) {
@@ -74,9 +19,9 @@ function get_uuid_from_object($object) {
} else if ($object instanceof ElggExtender) {
$type = $object->type;
if ($type == 'volatile') {
- $uuid = guid_to_uuid($object->entity_guid). $type . "/{$object->name}/";
+ $uuid = guid_to_uuid($object->entity_guid) . $type . "/{$object->name}/";
} else {
- $uuid = guid_to_uuid($object->entity_guid). $type . "/{$object->id}/";
+ $uuid = guid_to_uuid($object->entity_guid) . $type . "/{$object->id}/";
}
return $uuid;
@@ -91,22 +36,22 @@ function get_uuid_from_object($object) {
* Generate a UUID from a given GUID.
*
* @param int $guid The GUID of an object.
+ *
+ * @return string
*/
function guid_to_uuid($guid) {
- global $CONFIG;
-
- return $CONFIG->wwwroot . "export/opendd/$guid/";
+ return elgg_get_site_url() . "export/opendd/$guid/";
}
/**
* Test to see if a given uuid is for this domain, returning true if so.
- * @param $uuid
+ *
+ * @param string $uuid A unique ID
+ *
* @return bool
*/
function is_uuid_this_domain($uuid) {
- global $CONFIG;
-
- if (strpos($uuid, $CONFIG->wwwroot) === 0) {
+ if (strpos($uuid, elgg_get_site_url()) === 0) {
return true;
}
@@ -116,12 +61,15 @@ function is_uuid_this_domain($uuid) {
/**
* This function attempts to retrieve a previously imported entity via its UUID.
*
- * @param $uuid
+ * @param string $uuid A unique ID
+ *
+ * @return ElggEntity|false
*/
function get_entity_from_uuid($uuid) {
$uuid = sanitise_string($uuid);
- $entities = elgg_get_entities_from_metadata(array('metadata_name' => 'import_uuid', 'metadata_value' => $uuid));
+ $options = array('metadata_name' => 'import_uuid', 'metadata_value' => $uuid);
+ $entities = elgg_get_entities_from_metadata($options);
if ($entities) {
return $entities[0];
@@ -133,14 +81,17 @@ function get_entity_from_uuid($uuid) {
/**
* Tag a previously created guid with the uuid it was imported on.
*
- * @param int $guid
- * @param string $uuid
+ * @param int $guid A GUID
+ * @param string $uuid A Unique ID
+ *
+ * @return bool
*/
function add_uuid_to_guid($guid, $uuid) {
$guid = (int)$guid;
$uuid = sanitise_string($uuid);
- return create_metadata($guid, "import_uuid", $uuid);
+ $result = create_metadata($guid, "import_uuid", $uuid);
+ return (bool)$result;
}
@@ -154,40 +105,50 @@ $IMPORTED_OBJECT_COUNTER = 0;
* If nobody processes the top level element, the sub level elements are processed.
*
* @param ODD $odd The odd element to process
+ *
+ * @return bool
+ * @access private
*/
-function __process_element(ODD $odd) {
+function _process_element(ODD $odd) {
global $IMPORTED_DATA, $IMPORTED_OBJECT_COUNTER;
// See if anyone handles this element, return true if it is.
+ $to_be_serialised = null;
if ($odd) {
- $handled = trigger_plugin_hook("import", "all", array("element" => $odd), $to_be_serialised);
- }
+ $handled = elgg_trigger_plugin_hook("import", "all", array("element" => $odd), $to_be_serialised);
- // If not, then see if any of its sub elements are handled
- if ($handled) {
- // Increment validation counter
- $IMPORTED_OBJECT_COUNTER ++;
- // Return the constructed object
- $IMPORTED_DATA[] = $handled;
+ // If not, then see if any of its sub elements are handled
+ if ($handled) {
+ // Increment validation counter
+ $IMPORTED_OBJECT_COUNTER ++;
+ // Return the constructed object
+ $IMPORTED_DATA[] = $handled;
- return true;
+ return true;
+ }
}
return false;
}
+/**
+ * Exports an entity as an array
+ *
+ * @param int $guid Entity GUID
+ *
+ * @return array
+ * @throws ExportException
+ * @access private
+ */
function exportAsArray($guid) {
$guid = (int)$guid;
- // Initialise the array
- $to_be_serialised = array();
-
// Trigger a hook to
- $to_be_serialised = trigger_plugin_hook("export", "all", array("guid" => $guid), $to_be_serialised);
+ $to_be_serialised = elgg_trigger_plugin_hook("export", "all", array("guid" => $guid), array());
// Sanity check
- if ((!is_array($to_be_serialised)) || (count($to_be_serialised)==0)) {
- throw new ExportException(sprintf(elgg_echo('ExportException:NoSuchEntity'), $guid));
+ if ((!is_array($to_be_serialised)) || (count($to_be_serialised) == 0)) {
+ throw new ExportException(elgg_echo('ExportException:NoSuchEntity', array($guid)));
}
return $to_be_serialised;
@@ -201,10 +162,11 @@ function exportAsArray($guid) {
* This function makes use of the "serialise" plugin hook, which is passed an array to which plugins
* should add data to be serialised to.
*
- * @see ElggEntity for an example of its usage.
* @param int $guid The GUID.
- * @param ODDWrapperFactory $wrapper Optional wrapper permitting the export process to embed ODD in other document formats.
- * @return xml
+ *
+ * @return string XML
+ * @see ElggEntity for an example of its usage.
+ * @access private
*/
function export($guid) {
$odd = new ODDDocument(exportAsArray($guid));
@@ -216,9 +178,11 @@ function export($guid) {
* Import an XML serialisation of an object.
* This will make a best attempt at importing a given xml doc.
*
- * @param string $xml
+ * @param string $xml XML string
+ *
* @return bool
- * @throws Exception if there was a problem importing the data.
+ * @throws ImportException if there was a problem importing the data.
+ * @access private
*/
function import($xml) {
global $IMPORTED_DATA, $IMPORTED_OBJECT_COUNTER;
@@ -232,10 +196,10 @@ function import($xml) {
}
foreach ($document as $element) {
- __process_element($element);
+ _process_element($element);
}
- if ($IMPORTED_OBJECT_COUNTER!= count($IMPORTED_DATA)) {
+ if ($IMPORTED_OBJECT_COUNTER != count($IMPORTED_DATA)) {
throw new ImportException(elgg_echo('ImportException:NotAllImported'));
}
@@ -245,12 +209,15 @@ function import($xml) {
/**
* Register the OpenDD import action
+ *
+ * @return void
+ * @access private
*/
function export_init() {
global $CONFIG;
- register_action("import/opendd", false);
+ elgg_register_action("import/opendd");
}
// Register a startup event
-register_elgg_event_handler('init', 'system', 'export_init', 100);
+elgg_register_event_handler('init', 'system', 'export_init', 100);
diff --git a/engine/lib/extender.php b/engine/lib/extender.php
index cbcdd6eb7..8323bd3ce 100644
--- a/engine/lib/extender.php
+++ b/engine/lib/extender.php
@@ -3,274 +3,23 @@
* Elgg Entity Extender.
* This file contains ways of extending an Elgg entity in custom ways.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage DataModel.Extender
*/
/**
- * ElggExtender
- *
- * @author Curverider Ltd
- * @package Elgg
- * @subpackage Core
- */
-abstract class ElggExtender implements
- Exportable,
- Loggable, // Can events related to this object class be logged
- Iterator, // Override foreach behaviour
- ArrayAccess // Override for array access
-{
- /**
- * This contains the site's main properties (id, etc)
- * @var array
- */
- protected $attributes;
-
- /**
- * Get an attribute
- *
- * @param string $name
- * @return mixed
- */
- protected function get($name) {
- if (isset($this->attributes[$name])) {
- // Sanitise value if necessary
- if ($name=='value') {
- switch ($this->attributes['value_type']) {
- case 'integer' :
- return (int)$this->attributes['value'];
-
- //case 'tag' :
- //case 'file' :
- case 'text' :
- return ($this->attributes['value']);
-
- default :
- throw new InstallationException(sprintf(elgg_echo('InstallationException:TypeNotSupported'), $this->attributes['value_type']));
- }
- }
-
- return $this->attributes[$name];
- }
- return null;
- }
-
- /**
- * Set an attribute
- *
- * @param string $name
- * @param mixed $value
- * @param string $value_type
- * @return boolean
- */
- protected function set($name, $value, $value_type = "") {
- $this->attributes[$name] = $value;
- if ($name == 'value') {
- $this->attributes['value_type'] = detect_extender_valuetype($value, $value_type);
- }
-
- return true;
- }
-
- /**
- * Return the owner of this annotation.
- *
- * @return mixed
- */
- public function getOwner() {
- return $this->owner_guid;
- }
-
- /**
- * Return the owner entity
- *
- * @return mixed
- */
- public function getOwnerEntity() {
- return get_user($this->owner_guid);
- }
-
- /**
- * Returns the entity this is attached to
- *
- * @return ElggEntity The enttiy
- */
- public function getEntity() {
- return get_entity($this->entity_guid);
- }
-
- /**
- * Save this data to the appropriate database table.
- */
- abstract public function save();
-
- /**
- * Delete this data.
- */
- abstract public function delete();
-
- /**
- * Determines whether or not the specified user can edit this
- *
- * @param int $user_guid The GUID of the user (defaults to currently logged in user)
- * @return true|false
- */
- public function canEdit($user_guid = 0) {
- return can_edit_extender($this->id,$this->type,$user_guid);
- }
-
- /**
- * Return a url for this extender.
- *
- * @return string
- */
- public abstract function getURL();
-
- // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an array of fields which can be exported.
- */
- public function getExportableValues() {
- return array(
- 'id',
- 'entity_guid',
- 'name',
- 'value',
- 'value_type',
- 'owner_guid',
- 'type',
- );
- }
-
- /**
- * Export this object
- *
- * @return array
- */
- public function export() {
- $uuid = get_uuid_from_object($this);
-
- $meta = new ODDMetadata($uuid, guid_to_uuid($this->entity_guid), $this->attributes['name'], $this->attributes['value'], $this->attributes['type'], guid_to_uuid($this->owner_guid));
- $meta->setAttribute('published', date("r", $this->time_created));
-
- return $meta;
- }
-
- // SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an identification for the object for storage in the system log.
- * This id must be an integer.
- *
- * @return int
- */
- public function getSystemLogID() {
- return $this->id;
- }
-
- /**
- * Return the class name of the object.
- */
- public function getClassName() {
- return get_class($this);
- }
-
- /**
- * Return the GUID of the owner of this object.
- */
- public function getObjectOwnerGUID() {
- return $this->owner_guid;
- }
-
- /**
- * Return a type of the object - eg. object, group, user, relationship, metadata, annotation etc
- */
- public function getType() {
- return $this->type;
- }
-
- /**
- * Return a subtype. For metadata & annotations this is the 'name' and
- * for relationship this is the relationship type.
- */
- public function getSubtype() {
- return $this->name;
- }
-
-
- // ITERATOR INTERFACE //////////////////////////////////////////////////////////////
- /*
- * This lets an entity's attributes be displayed using foreach as a normal array.
- * Example: http://www.sitepoint.com/print/php5-standard-library
- */
-
- private $valid = FALSE;
-
- function rewind() {
- $this->valid = (FALSE !== reset($this->attributes));
- }
-
- function current() {
- return current($this->attributes);
- }
-
- function key() {
- return key($this->attributes);
- }
-
- function next() {
- $this->valid = (FALSE !== next($this->attributes));
- }
-
- function valid() {
- return $this->valid;
- }
-
- // ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
- /*
- * This lets an entity's attributes be accessed like an associative array.
- * Example: http://www.sitepoint.com/print/php5-standard-library
- */
-
- function offsetSet($key, $value) {
- if ( array_key_exists($key, $this->attributes) ) {
- $this->attributes[$key] = $value;
- }
- }
-
- function offsetGet($key) {
- if ( array_key_exists($key, $this->attributes) ) {
- return $this->attributes[$key];
- }
- }
-
- function offsetUnset($key) {
- if ( array_key_exists($key, $this->attributes) ) {
- // Full unsetting is dangerious for our objects
- $this->attributes[$key] = "";
- }
- }
-
- function offsetExists($offset) {
- return array_key_exists($offset, $this->attributes);
- }
-}
-
-/**
* Detect the value_type for a given value.
* Currently this is very crude.
*
- * TODO: Make better!
+ * @todo Make better!
*
- * @param mixed $value
+ * @param mixed $value The value
* @param string $value_type If specified, overrides the detection.
+ *
* @return string
*/
function detect_extender_valuetype($value, $value_type = "") {
- if ($value_type!="") {
+ if ($value_type != "" && ($value_type == 'integer' || $value_type == 'text')) {
return $value_type;
}
@@ -287,12 +36,15 @@ function detect_extender_valuetype($value, $value_type = "") {
}
/**
- * Utility function used by import_extender_plugin_hook() to process an ODDMetaData and add it to an entity.
- * This function does not hit ->save() on the entity (this lets you construct in memory)
+ * Utility function used by import_extender_plugin_hook() to process
+ * an ODDMetaData and add it to an entity. This function does not
+ * hit ->save() on the entity (this lets you construct in memory)
*
- * @param ElggEntity The entity to add the data to.
+ * @param ElggEntity $entity The entity to add the data to.
* @param ODDMetaData $element The OpenDD element
+ *
* @return bool
+ * @access private
*/
function oddmetadata_to_elggextender(ElggEntity $entity, ODDMetaData $element) {
// Get the type of extender (metadata, type, attribute etc)
@@ -325,6 +77,17 @@ function oddmetadata_to_elggextender(ElggEntity $entity, ODDMetaData $element) {
/**
* Handler called by trigger_plugin_hook on the "import" event.
+ *
+ * @param string $hook volatile
+ * @param string $entity_type metadata
+ * @param string $returnvalue Return value from previous hook
+ * @param array $params The parameters
+ *
+ * @return null
+ * @elgg_plugin_hook_handler volatile metadata
+ * @todo investigate more.
+ * @throws ImportException
+ * @access private
*/
function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params) {
$element = $params['element'];
@@ -332,18 +95,21 @@ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params)
$tmp = NULL;
if ($element instanceof ODDMetaData) {
+ /* @var ODDMetaData $element */
// Recall entity
$entity_uuid = $element->getAttribute('entity_uuid');
$entity = get_entity_from_uuid($entity_uuid);
if (!$entity) {
- throw new ImportException(sprintf(elgg_echo('ImportException:GUIDNotFound'), $entity_uuid));
+ throw new ImportException(elgg_echo('ImportException:GUIDNotFound', array($entity_uuid)));
}
oddmetadata_to_elggextender($entity, $element);
// Save
if (!$entity->save()) {
- throw new ImportException(sprintf(elgg_echo('ImportException:ProblemUpdatingMeta'), $attr_name, $entity_uuid));
+ $attr_name = $element->getAttribute('name');
+ $msg = elgg_echo('ImportException:ProblemUpdatingMeta', array($attr_name, $entity_uuid));
+ throw new ImportException($msg);
}
return true;
@@ -353,61 +119,72 @@ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params)
/**
* Determines whether or not the specified user can edit the specified piece of extender
*
- * @param int $extender_id The ID of the piece of extender
- * @param string $type 'metadata' or 'annotation'
- * @param int $user_guid The GUID of the user
- * @return true|false
+ * @param int $extender_id The ID of the piece of extender
+ * @param string $type 'metadata' or 'annotation'
+ * @param int $user_guid The GUID of the user
+ *
+ * @return bool
*/
function can_edit_extender($extender_id, $type, $user_guid = 0) {
- if (!isloggedin()) {
- return false;
+ // @todo Since Elgg 1.0, Elgg has returned false from can_edit_extender()
+ // if no user was logged in. This breaks the access override. This is a
+ // temporary work around. This function needs to be rewritten in Elgg 1.9
+ if (!elgg_check_access_overrides($user_guid)) {
+ if (!elgg_is_logged_in()) {
+ return false;
+ }
}
$user_guid = (int)$user_guid;
- $user = get_entity($user_guid);
+ $user = get_user($user_guid);
if (!$user) {
- $user = get_loggedin_user();
+ $user = elgg_get_logged_in_user_entity();
+ $user_guid = elgg_get_logged_in_user_guid();
}
- $functionname = "get_{$type}";
+ $functionname = "elgg_get_{$type}_from_id";
if (is_callable($functionname)) {
- $extender = $functionname($extender_id);
+ $extender = call_user_func($functionname, $extender_id);
} else {
return false;
}
- if (!is_a($extender,"ElggExtender")) {
+ if (!($extender instanceof ElggExtender)) {
return false;
}
+ /* @var ElggExtender $extender */
// If the owner is the specified user, great! They can edit.
- if ($extender->getOwner() == $user->getGUID()) {
+ if ($extender->getOwnerGUID() == $user_guid) {
return true;
}
// If the user can edit the entity this is attached to, great! They can edit.
- if (can_edit_entity($extender->entity_guid,$user->getGUID())) {
+ if (can_edit_entity($extender->entity_guid, $user_guid)) {
return true;
}
- // Trigger plugin hooks
- return trigger_plugin_hook('permissions_check',$type,array('entity' => $entity, 'user' => $user),false);
+ // Trigger plugin hook - note that $user may be null
+ $params = array('entity' => $extender->getEntity(), 'user' => $user);
+ return elgg_trigger_plugin_hook('permissions_check', $type, $params, false);
}
/**
* Sets the URL handler for a particular extender type and name.
- * It is recommended that you do not call this directly, instead use one of the wrapper functions in the
- * subtype files.
+ * It is recommended that you do not call this directly, instead use
+ * one of the wrapper functions such as elgg_register_annotation_url_handler().
*
- * @param string $function_name The function to register
- * @param string $extender_type Extender type
+ * @param string $extender_type Extender type ('annotation', 'metadata')
* @param string $extender_name The name of the extender
- * @return true|false Depending on success
+ * @param string $function_name The function to register
+ *
+ * @return bool
*/
-function register_extender_url_handler($function_name, $extender_type = "all", $extender_name = "all") {
+function elgg_register_extender_url_handler($extender_type, $extender_name, $function_name) {
+
global $CONFIG;
- if (!is_callable($function_name)) {
+ if (!is_callable($function_name, true)) {
return false;
}
@@ -426,7 +203,9 @@ function register_extender_url_handler($function_name, $extender_type = "all", $
* Get the URL of a given elgg extender.
* Used by get_annotation_url and get_metadata_url.
*
- * @param ElggExtender $extender
+ * @param ElggExtender $extender An extender object
+ *
+ * @return string
*/
function get_extender_url(ElggExtender $extender) {
global $CONFIG;
@@ -452,18 +231,19 @@ function get_extender_url(ElggExtender $extender) {
}
if (is_callable($function)) {
- $url = $function($extender);
+ $url = call_user_func($function, $extender);
}
if ($url == "") {
$nameid = $extender->id;
if ($type == 'volatile') {
- $nameid== $extender->name;
+ $nameid = $extender->name;
}
- $url = $CONFIG->wwwroot . "export/$view/$guid/$type/$nameid/";
+ $url = "export/$view/$guid/$type/$nameid/";
}
- return $url;
+
+ return elgg_normalize_url($url);
}
/** Register the hook */
-register_plugin_hook("import", "all", "import_extender_plugin_hook", 2); \ No newline at end of file
+elgg_register_plugin_hook_handler("import", "all", "import_extender_plugin_hook", 2);
diff --git a/engine/lib/filestore.php b/engine/lib/filestore.php
index d131154c6..a3c7ba439 100644
--- a/engine/lib/filestore.php
+++ b/engine/lib/filestore.php
@@ -1,723 +1,30 @@
<?php
/**
* Elgg filestore.
- * This file contains classes, interfaces and functions for saving and retrieving data to various file
- * stores.
+ * This file contains classes, interfaces and functions for
+ * saving and retrieving data to various file stores.
*
- * @package Elgg
- * @subpackage API
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage DataModel.FileStorage
*/
-include_once("objects.php");
-
-/**
- * @class ElggFilestore
- * This class defines the interface for all elgg data repositories.
- * @author Curverider Ltd
- */
-abstract class ElggFilestore {
- /**
- * Attempt to open the file $file for storage or writing.
- *
- * @param ElggFile $file
- * @param string $mode "read", "write", "append"
- * @return mixed A handle to the opened file or false on error.
- */
- abstract public function open(ElggFile $file, $mode);
-
- /**
- * Write data to a given file handle.
- *
- * @param mixed $f The file handle - exactly what this is depends on the file system
- * @param string $data The binary string of data to write
- * @return int Number of bytes written.
- */
- abstract public function write($f, $data);
-
- /**
- * Read data from a filestore.
- *
- * @param mixed $f The file handle
- * @param int $length Length in bytes to read.
- * @param int $offset The optional offset.
- * @return mixed String of data or false on error.
- */
- abstract public function read($f, $length, $offset = 0);
-
- /**
- * Seek a given position within a file handle.
- *
- * @param mixed $f The file handle.
- * @param int $position The position.
- */
- abstract public function seek($f, $position);
-
- /**
- * Return a whether the end of a file has been reached.
- *
- * @param mixed $f The file handle.
- * @return boolean
- */
- abstract public function eof($f);
-
- /**
- * Return the current position in an open file.
- *
- * @param mixed $f The file handle.
- * @return int
- */
- abstract public function tell($f);
-
- /**
- * Close a given file handle.
- *
- * @param mixed $f
- */
- abstract public function close($f);
-
- /**
- * Delete the file associated with a given file handle.
- *
- * @param ElggFile $file
- */
- abstract public function delete(ElggFile $file);
-
- /**
- * Return the size in bytes for a given file.
- *
- * @param ElggFile $file
- */
- abstract public function getFileSize(ElggFile $file);
-
- /**
- * Return the filename of a given file as stored on the filestore.
- *
- * @param ElggFile $file
- */
- abstract public function getFilenameOnFilestore(ElggFile $file);
-
- /**
- * Get the filestore's creation parameters as an associative array.
- * Used for serialisation and for storing the creation details along side a file object.
- *
- * @return array
- */
- abstract public function getParameters();
-
- /**
- * Set the parameters from the associative array produced by $this->getParameters().
- */
- abstract public function setParameters(array $parameters);
-
- /**
- * Get the contents of the whole file.
- *
- * @param mixed $file The file handle.
- * @return mixed The file contents.
- */
- abstract public function grabFile(ElggFile $file);
-
- /**
- * Return whether a file physically exists or not.
- *
- * @param ElggFile $file
- */
- abstract public function exists(ElggFile $file);
-}
-
-/**
- * @class ElggDiskFilestore
- * This class uses disk storage to save data.
- * @author Curverider Ltd
- */
-class ElggDiskFilestore extends ElggFilestore {
- /**
- * Directory root.
- */
- private $dir_root;
-
- /**
- * Default depth of file directory matrix
- */
- private $matrix_depth = 5;
-
- /**
- * Construct a disk filestore using the given directory root.
- *
- * @param string $directory_root Root directory, must end in "/"
- */
- public function __construct($directory_root = "") {
- global $CONFIG;
-
- if ($directory_root) {
- $this->dir_root = $directory_root;
- } else {
- $this->dir_root = $CONFIG->dataroot;
- }
- }
-
- public function open(ElggFile $file, $mode) {
- $fullname = $this->getFilenameOnFilestore($file);
-
- // Split into path and name
- $ls = strrpos($fullname,"/");
- if ($ls===false) {
- $ls = 0;
- }
-
- $path = substr($fullname, 0, $ls);
- $name = substr($fullname, $ls);
-
- // Try and create the directory
- try {
- $this->make_directory_root($path);
- } catch (Exception $e) {
-
- }
-
- if (($mode!='write') && (!file_exists($fullname))) {
- return false;
- }
-
- switch ($mode) {
- case "read" :
- $mode = "rb";
- break;
- case "write" :
- $mode = "w+b";
- break;
- case "append" :
- $mode = "a+b";
- break;
- default:
- throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:UnrecognisedFileMode'), $mode));
- }
-
- return fopen($fullname, $mode);
-
- }
-
- public function write($f, $data) {
- return fwrite($f, $data);
- }
-
- public function read($f, $length, $offset = 0) {
- if ($offset) {
- $this->seek($f, $offset);
- }
-
- return fread($f, $length);
- }
-
- public function close($f) {
- return fclose($f);
- }
-
- public function delete(ElggFile $file) {
- $filename = $this->getFilenameOnFilestore($file);
- if (file_exists($filename)) {
- return unlink($filename);
- } else {
- return true;
- }
- }
-
- public function seek($f, $position) {
- return fseek($f, $position);
- }
-
- public function tell($f) {
- return ftell($f);
- }
-
- public function eof($f) {
- return feof($f);
- }
-
- public function getFileSize(ElggFile $file) {
- return filesize($this->getFilenameOnFilestore($file));
- }
-
- public function getFilenameOnFilestore(ElggFile $file) {
- $owner = $file->getOwnerEntity();
- if (!$owner) {
- $owner = get_loggedin_user();
- }
-
- if ((!$owner) || (!$owner->username)) {
- throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:MissingOwner'), $file->getFilename(), $file->guid));
- }
-
- return $this->dir_root . $this->make_file_matrix($owner->guid) . $file->getFilename();
- }
-
- public function grabFile(ElggFile $file) {
- return file_get_contents($file->getFilenameOnFilestore());
- }
-
- public function exists(ElggFile $file) {
- return file_exists($this->getFilenameOnFilestore($file));
- }
-
- public function getSize($prefix,$container_guid) {
- if ($container_guid) {
- return get_dir_size($this->dir_root.$this->make_file_matrix($container_guid).$prefix);
- } else {
- return false;
- }
- }
-
- /**
- * Make the directory root.
- *
- * @param string $dirroot
- */
- protected function make_directory_root($dirroot) {
- if (!file_exists($dirroot)) {
- if (!@mkdir($dirroot, 0700, true)) {
- throw new IOException(sprintf(elgg_echo('IOException:CouldNotMake'), $dirroot));
- }
- }
-
- return true;
- }
-
- /**
- * Multibyte string tokeniser.
- *
- * Splits a string into an array. Will fail safely if mbstring is not installed (although this may still
- * not handle .
- *
- * @param string $string String
- * @param string $charset The charset, defaults to UTF8
- * @return array
- */
- private function mb_str_split($string, $charset = 'UTF8') {
- if (is_callable('mb_substr')) {
- $length = mb_strlen($string);
- $array = array();
-
- while ($length) {
- $array[] = mb_substr($string, 0, 1, $charset);
- $string = mb_substr($string, 1, $length, $charset);
-
- $length = mb_strlen($string);
- }
-
- return $array;
- } else {
- return str_split($string);
- }
-
- return false;
- }
-
- /**
- * Construct the filename matrix.
- *
- * @param int | string $identifier
- * @return str
- */
- protected function make_file_matrix($identifier) {
- if (is_numeric($identifier)) {
- return $this->user_file_matrix($identifier);
- }
-
- return $this->deprecated_file_matrix($identifier);
- }
-
- /**
- * Construct the filename matrix with user info
- *
- * This method will generate a matrix using the entity's creation time and
- * unique guid. This is intended only to determine a user's data directory.
- *
- * @param int $guid
- * @return str
- */
- protected function user_file_matrix($guid) {
- // lookup the entity
- $user = get_entity($guid);
- if ($user->type != 'user')
- {
- // only to be used for user directories
- return FALSE;
- }
-
- if (!$user->time_created) {
- // fall back to deprecated method
- return $this->deprecated_file_matrix($user->username);
- }
-
- $time_created = date('Y/m/d', $user->time_created);
- return "$time_created/$user->guid/";
- }
-
- /**
- * Construct the filename matrix using a string
- *
- * Particularly, this is used with a username to generate the file storage
- * location.
- *
- * @deprecated for user directories: use user_file_matrix() instead.
- *
- * @param str $filename
- * @return str
- */
- protected function deprecated_file_matrix($filename) {
- // throw a warning for using deprecated method
- $error = 'Deprecated use of ElggDiskFilestore::make_file_matrix. ';
- $error .= 'Username passed instead of guid.';
- elgg_log($error, WARNING);
-
- $user = new ElggUser($filename);
- return $this->user_file_matrix($user->guid);
- }
-
- public function getParameters() {
- return array("dir_root" => $this->dir_root);
- }
-
- public function setParameters(array $parameters) {
- if (isset($parameters['dir_root'])) {
- $this->dir_root = $parameters['dir_root'];
- return true;
- }
-
- return false;
- }
-}
-
-/**
- * @class ElggFile
- * This class represents a physical file.
- *
- * Usage:
- * Create a new ElggFile object and specify a filename, and optionally a FileStore (if one isn't specified
- * then the default is assumed.
- *
- * Open the file using the appropriate mode, and you will be able to read and write to the file.
- *
- * Optionally, you can also call the file's save() method, this will turn the file into an entity in the
- * system and permit you to do things like attach tags to the file etc. This is not done automatically since
- * there are many occasions where you may want access to file data on datastores using the ElggFile interface
- * but do not want to create an Entity reference to it in the system (temporary files for example).
- *
- * @author Curverider Ltd
- */
-class ElggFile extends ElggObject {
- /** Filestore */
- private $filestore;
-
- /** File handle used to identify this file in a filestore. Created by open. */
- private $handle;
-
- protected function initialise_attributes() {
- parent::initialise_attributes();
-
- $this->attributes['subtype'] = "file";
- }
-
- public function __construct($guid = null) {
- parent::__construct($guid);
-
- // Set default filestore
- $this->filestore = $this->getFilestore();
- }
-
- /**
- * Set the filename of this file.
- *
- * @param string $name The filename.
- */
- public function setFilename($name) {
- $this->filename = $name;
- }
-
- /**
- * Return the filename.
- */
- public function getFilename() {
- return $this->filename;
- }
-
- /**
- * Return the filename of this file as it is/will be stored on the filestore, which may be different
- * to the filename.
- */
- public function getFilenameOnFilestore() {
- return $this->filestore->getFilenameOnFilestore($this);
- }
-
- /*
- * Return the size of the filestore associated with this file
- *
- */
- public function getFilestoreSize($prefix='',$container_guid=0) {
- if (!$container_guid) {
- $container_guid = $this->container_guid;
- }
- $fs = $this->getFilestore();
- return $fs->getSize($prefix,$container_guid);
- }
-
- /**
- * Get the mime type of the file.
- */
- public function getMimeType() {
- if ($this->mimetype) {
- return $this->mimetype;
- }
-
- // TODO : Guess mimetype if not here
- }
-
- /**
- * Set the mime type of the file.
- *
- * @param $mimetype The mimetype
- */
- public function setMimeType($mimetype) {
- return $this->mimetype = $mimetype;
- }
-
- /**
- * Set the optional file description.
- *
- * @param string $description The description.
- */
- public function setDescription($description) {
- $this->description = $description;
- }
-
- /**
- * Open the file with the given mode
- *
- * @param string $mode Either read/write/append
- */
- public function open($mode) {
- if (!$this->getFilename()) {
- throw new IOException(elgg_echo('IOException:MissingFileName'));
- }
-
- // See if file has already been saved
- // seek on datastore, parameters and name?
-
- // Sanity check
- if (
- ($mode!="read") &&
- ($mode!="write") &&
- ($mode!="append")
- ) {
- throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:UnrecognisedFileMode'), $mode));
- }
-
- // Get the filestore
- $fs = $this->getFilestore();
-
- // Ensure that we save the file details to object store
- //$this->save();
-
- // Open the file handle
- $this->handle = $fs->open($this, $mode);
-
- return $this->handle;
- }
-
- /**
- * Write some data.
- *
- * @param string $data The data
- */
- public function write($data) {
- $fs = $this->getFilestore();
-
- return $fs->write($this->handle, $data);
- }
-
- /**
- * Read some data.
- *
- * @param int $length Amount to read.
- * @param int $offset The offset to start from.
- */
- public function read($length, $offset = 0) {
- $fs = $this->getFilestore();
-
- return $fs->read($this->handle, $length, $offset);
- }
-
- /**
- * Gets the full contents of this file.
- *
- * @return mixed The file contents.
- */
- public function grabFile() {
- $fs = $this->getFilestore();
- return $fs->grabFile($this);
- }
-
- /**
- * Close the file and commit changes
- */
- public function close() {
- $fs = $this->getFilestore();
-
- if ($fs->close($this->handle)) {
- $this->handle = NULL;
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Delete this file.
- */
- public function delete() {
- $fs = $this->getFilestore();
- if ($fs->delete($this)) {
- return parent::delete();
- }
- }
-
- /**
- * Seek a position in the file.
- *
- * @param int $position
- */
- public function seek($position) {
- $fs = $this->getFilestore();
-
- return $fs->seek($this->handle, $position);
- }
-
- /**
- * Return the current position of the file.
- *
- * @return int The file position
- */
- public function tell() {
- $fs = $this->getFilestore();
-
- return $fs->tell($this->handle);
- }
-
- /**
- * Return the size of the file in bytes.
- */
- public function size() {
- return $this->filestore->getFileSize($this);
- }
-
- /**
- * Return a boolean value whether the file handle is at the end of the file
- */
- public function eof() {
- $fs = $this->getFilestore();
-
- return $fs->eof($this->handle);
- }
-
- public function exists() {
- $fs = $this->getFilestore();
-
- return $fs->exists($this);
- }
-
- /**
- * Set a filestore.
- *
- * @param ElggFilestore $filestore The file store.
- */
- public function setFilestore(ElggFilestore $filestore) {
- $this->filestore = $filestore;
- }
-
- /**
- * Return a filestore suitable for saving this file.
- * This filestore is either a pre-registered filestore, a filestore loaded from metatags saved
- * along side this file, or the system default.
- */
- protected function getFilestore() {
- // Short circuit if already set.
- if ($this->filestore) {
- return $this->filestore;
- }
-
- // If filestore meta set then retrieve filestore TODO: Better way of doing this?
- $metas = get_metadata_for_entity($this->guid);
- $parameters = array();
- if (is_array($metas)) {
- foreach ($metas as $meta) {
- if (strpos($meta->name, "filestore::")!==false) {
- // Filestore parameter tag
- $comp = explode("::", $meta->name);
- $name = $comp[1];
-
- $parameters[$name] = $meta->value;
- }
- }
- }
-
- // If parameters loaded then create new filestore
- if (count($parameters)!=0) {
- // Create new filestore object
- if ((!isset($parameters['filestore'])) || (!class_exists($parameters['filestore']))) {
- throw new ClassNotFoundException(elgg_echo('ClassNotFoundException:NotFoundNotSavedWithFile'));
- }
-
- $this->filestore = new $parameters['filestore']();
-
- // Set parameters
- $this->filestore->setParameters($parameters);
- }
-
-
- // if still nothing then set filestore to default
- if (!$this->filestore) {
- $this->filestore = get_default_filestore();
- }
-
- return $this->filestore;
- }
-
- public function save() {
- if (!parent::save()) {
- return false;
- }
-
- // Save datastore metadata
- $params = $this->filestore->getParameters();
- foreach ($params as $k => $v) {
- $this->setMetaData("filestore::$k", $v);
- }
-
- // Now make a note of the filestore class
- $this->setMetaData("filestore::filestore", get_class($this->filestore));
-
- return true;
- }
-}
-
/**
* Get the size of the specified directory.
*
- * @param string $dir The full path of the directory
+ * @param string $dir The full path of the directory
+ * @param int $totalsize Add to current dir size
+ *
* @return int The size of the directory.
*/
-function get_dir_size($dir, $totalsize = 0){
+function get_dir_size($dir, $totalsize = 0) {
$handle = @opendir($dir);
- while ($file = @readdir ($handle)){
+ while ($file = @readdir($handle)) {
if (eregi("^\.{1,2}$", $file)) {
continue;
}
- if(is_dir($dir . $file)) {
+ if (is_dir($dir . $file)) {
$totalsize = get_dir_size($dir . $file . "/", $totalsize);
- } else{
+ } else {
$totalsize += filesize($dir . $file);
}
}
@@ -731,6 +38,7 @@ function get_dir_size($dir, $totalsize = 0){
* (Returns false if there was an issue.)
*
* @param string $input_name The name of the file input field on the submission form
+ *
* @return mixed|false The contents of the file, or false on failure.
*/
function get_uploaded_file($input_name) {
@@ -746,15 +54,22 @@ function get_uploaded_file($input_name) {
* (Returns false if the uploaded file was not an image)
*
* @param string $input_name The name of the file input field on the submission form
- * @param int $maxwidth The maximum width of the resized image
- * @param int $maxheight The maximum height of the resized image
- * @param true|false $square If set to true, will take the smallest of maxwidth and maxheight and use it to set the dimensions on all size; the image will be cropped.
+ * @param int $maxwidth The maximum width of the resized image
+ * @param int $maxheight The maximum height of the resized image
+ * @param bool $square If set to true, will take the smallest
+ * of maxwidth and maxheight and use it to set the
+ * dimensions on all size; the image will be cropped.
+ * @param bool $upscale Resize images smaller than $maxwidth x $maxheight?
+ *
* @return false|mixed The contents of the resized image, or false on failure
*/
-function get_resized_image_from_uploaded_file($input_name, $maxwidth, $maxheight, $square = false) {
+function get_resized_image_from_uploaded_file($input_name, $maxwidth, $maxheight,
+$square = false, $upscale = false) {
+
// If our file exists ...
if (isset($_FILES[$input_name]) && $_FILES[$input_name]['error'] == 0) {
- return get_resized_image_from_existing_file($_FILES[$input_name]['tmp_name'], $maxwidth, $maxheight, $square);
+ return get_resized_image_from_existing_file($_FILES[$input_name]['tmp_name'], $maxwidth,
+ $maxheight, $square, 0, 0, 0, 0, $upscale);
}
return false;
@@ -765,32 +80,33 @@ function get_resized_image_from_uploaded_file($input_name, $maxwidth, $maxheight
* (Returns false if the file was not an image)
*
* @param string $input_name The name of the file on the disk
- * @param int $maxwidth The desired width of the resized image
- * @param int $maxheight The desired height of the resized image
- * @param true|false $square If set to true, takes the smallest of maxwidth and
- * maxheight and use it to set the dimensions on the new image. If no
- * crop parameters are set, the largest square that fits in the image
- * centered will be used for the resize. If square, the crop must be a
- * square region.
- * @param int $x1 x coordinate for top, left corner
- * @param int $y1 y coordinate for top, left corner
- * @param int $x2 x coordinate for bottom, right corner
- * @param int $y2 y coordinate for bottom, right corner
- * @param bool $upscale Resize images smaller than $maxwidth x $maxheight?
+ * @param int $maxwidth The desired width of the resized image
+ * @param int $maxheight The desired height of the resized image
+ * @param bool $square If set to true, takes the smallest of maxwidth and
+ * maxheight and use it to set the dimensions on the new image.
+ * If no crop parameters are set, the largest square that fits
+ * in the image centered will be used for the resize. If square,
+ * the crop must be a square region.
+ * @param int $x1 x coordinate for top, left corner
+ * @param int $y1 y coordinate for top, left corner
+ * @param int $x2 x coordinate for bottom, right corner
+ * @param int $y2 y coordinate for bottom, right corner
+ * @param bool $upscale Resize images smaller than $maxwidth x $maxheight?
+ *
* @return false|mixed The contents of the resized image, or false on failure
*/
-function get_resized_image_from_existing_file($input_name, $maxwidth, $maxheight, $square = FALSE, $x1 = 0, $y1 = 0, $x2 = 0, $y2 = 0, $upscale = FALSE) {
+function get_resized_image_from_existing_file($input_name, $maxwidth, $maxheight, $square = FALSE,
+$x1 = 0, $y1 = 0, $x2 = 0, $y2 = 0, $upscale = FALSE) {
+
// Get the size information from the image
$imgsizearray = getimagesize($input_name);
if ($imgsizearray == FALSE) {
return FALSE;
}
- // Get width and height
$width = $imgsizearray[0];
$height = $imgsizearray[1];
- // make sure we can read the image
$accepted_formats = array(
'image/jpeg' => 'jpeg',
'image/pjpeg' => 'jpeg',
@@ -805,6 +121,94 @@ function get_resized_image_from_existing_file($input_name, $maxwidth, $maxheight
return FALSE;
}
+ // get the parameters for resizing the image
+ $options = array(
+ 'maxwidth' => $maxwidth,
+ 'maxheight' => $maxheight,
+ 'square' => $square,
+ 'upscale' => $upscale,
+ 'x1' => $x1,
+ 'y1' => $y1,
+ 'x2' => $x2,
+ 'y2' => $y2,
+ );
+ $params = get_image_resize_parameters($width, $height, $options);
+ if ($params == FALSE) {
+ return FALSE;
+ }
+
+ // load original image
+ $original_image = $load_function($input_name);
+ if (!$original_image) {
+ return FALSE;
+ }
+
+ // allocate the new image
+ $new_image = imagecreatetruecolor($params['newwidth'], $params['newheight']);
+ if (!$new_image) {
+ return FALSE;
+ }
+
+ // color transparencies white (default is black)
+ imagefilledrectangle(
+ $new_image, 0, 0, $params['newwidth'], $params['newheight'],
+ imagecolorallocate($new_image, 255, 255, 255)
+ );
+
+ $rtn_code = imagecopyresampled( $new_image,
+ $original_image,
+ 0,
+ 0,
+ $params['xoffset'],
+ $params['yoffset'],
+ $params['newwidth'],
+ $params['newheight'],
+ $params['selectionwidth'],
+ $params['selectionheight']);
+ if (!$rtn_code) {
+ return FALSE;
+ }
+
+ // grab a compressed jpeg version of the image
+ ob_start();
+ imagejpeg($new_image, NULL, 90);
+ $jpeg = ob_get_clean();
+
+ imagedestroy($new_image);
+ imagedestroy($original_image);
+
+ return $jpeg;
+}
+
+/**
+ * Calculate the parameters for resizing an image
+ *
+ * @param int $width Width of the original image
+ * @param int $height Height of the original image
+ * @param array $options See $defaults for the options
+ *
+ * @return array or FALSE
+ * @since 1.7.2
+ */
+function get_image_resize_parameters($width, $height, $options) {
+
+ $defaults = array(
+ 'maxwidth' => 100,
+ 'maxheight' => 100,
+
+ 'square' => FALSE,
+ 'upscale' => FALSE,
+
+ 'x1' => 0,
+ 'y1' => 0,
+ 'x2' => 0,
+ 'y2' => 0,
+ );
+
+ $options = array_merge($defaults, $options);
+
+ extract($options);
+
// crop image first?
$crop = TRUE;
if ($x1 == 0 && $y1 == 0 && $x2 == 0 && $y2 == 0) {
@@ -813,12 +217,12 @@ function get_resized_image_from_existing_file($input_name, $maxwidth, $maxheight
// how large a section of the image has been selected
if ($crop) {
- $region_width = $x2 - $x1;
- $region_height = $y2 - $y1;
+ $selection_width = $x2 - $x1;
+ $selection_height = $y2 - $y1;
} else {
// everything selected if no crop parameters
- $region_width = $width;
- $region_height = $height;
+ $selection_width = $width;
+ $selection_height = $height;
}
// determine cropping offsets
@@ -826,7 +230,7 @@ function get_resized_image_from_existing_file($input_name, $maxwidth, $maxheight
// asking for a square image back
// detect case where someone is passing crop parameters that are not for a square
- if ($crop == TRUE && $region_width != $region_height) {
+ if ($crop == TRUE && $selection_width != $selection_height) {
return FALSE;
}
@@ -834,7 +238,7 @@ function get_resized_image_from_existing_file($input_name, $maxwidth, $maxheight
$new_width = $new_height = min($maxwidth, $maxheight);
// find largest square that fits within the selected region
- $region_width = $region_height = min($region_width, $region_height);
+ $selection_width = $selection_height = min($selection_width, $selection_height);
// set offsets for crop
if ($crop) {
@@ -844,20 +248,19 @@ function get_resized_image_from_existing_file($input_name, $maxwidth, $maxheight
$height = $width;
} else {
// place square region in the center
- $widthoffset = floor(($width - $region_width) / 2);
- $heightoffset = floor(($height - $region_height) / 2);
+ $widthoffset = floor(($width - $selection_width) / 2);
+ $heightoffset = floor(($height - $selection_height) / 2);
}
} else {
// non-square new image
-
$new_width = $maxwidth;
- $new_height = $maxwidth;
+ $new_height = $maxheight;
// maintain aspect ratio of original image/crop
- if (($region_height / (float)$new_height) > ($region_width / (float)$new_width)) {
- $new_width = floor($new_height * $region_width / (float)$region_height);
+ if (($selection_height / (float)$new_height) > ($selection_width / (float)$new_width)) {
+ $new_width = floor($new_height * $selection_width / (float)$selection_height);
} else {
- $new_height = floor($new_width * $region_height / (float)$region_width);
+ $new_height = floor($new_width * $selection_height / (float)$selection_width);
}
// by default, use entire image
@@ -870,75 +273,41 @@ function get_resized_image_from_existing_file($input_name, $maxwidth, $maxheight
}
}
- // check for upscaling
- // @todo This ignores squares, coordinates, and cropping. It's probably not the best idea.
- // Size checking should be done in action code, but for backward compatibility
- // this duplicates the previous behavior.
- if (!$upscale && ($height < $new_height || $width < $new_width)) {
- // zero out offsets
- $widthoffset = $heightoffset = 0;
-
- // determine if we can scale it down at all
- // (ie, if only one dimension is too small)
- // if not, just use original size.
- if ($height < $new_height && $width < $new_width) {
- $ratio = 1;
- } elseif ($height < $new_height) {
- $ratio = $new_width / $width;
- } elseif ($width < $new_width) {
- $ratio = $new_height / $height;
+ if (!$upscale && ($selection_height < $new_height || $selection_width < $new_width)) {
+ // we cannot upscale and selected area is too small so we decrease size of returned image
+ if ($square) {
+ $new_height = $selection_height;
+ $new_width = $selection_width;
+ } else {
+ if ($selection_height < $new_height && $selection_width < $new_width) {
+ $new_height = $selection_height;
+ $new_width = $selection_width;
+ }
}
- $region_height = $height;
- $region_width = $width;
- $new_height = floor($height * $ratio);
- $new_width = floor($width * $ratio);
}
- // load original image
- $orig_image = $load_function($input_name);
- if (!$orig_image) {
- return FALSE;
- }
-
- // allocate the new image
- $newimage = imagecreatetruecolor($new_width, $new_height);
- if (!$newimage) {
- return FALSE;
- }
-
- // create the new image
- $rtn_code = imagecopyresampled( $newimage,
- $orig_image,
- 0,
- 0,
- $widthoffset,
- $heightoffset,
- $new_width,
- $new_height,
- $region_width,
- $region_height );
- if (!$rtn_code) {
- return FALSE;
- }
-
- // grab contents for return
- ob_start();
- imagejpeg($newimage, null, 90);
- $jpeg = ob_get_clean();
-
- imagedestroy($newimage);
- imagedestroy($orig_image);
+ $params = array(
+ 'newwidth' => $new_width,
+ 'newheight' => $new_height,
+ 'selectionwidth' => $selection_width,
+ 'selectionheight' => $selection_height,
+ 'xoffset' => $widthoffset,
+ 'yoffset' => $heightoffset,
+ );
- return $jpeg;
+ return $params;
}
-
-// putting these here for now
+/**
+ * Delete an ElggFile file
+ *
+ * @param int $guid ElggFile GUID
+ *
+ * @return bool
+ */
function file_delete($guid) {
if ($file = get_entity($guid)) {
if ($file->canEdit()) {
- $container = get_entity($file->container_guid);
-
$thumbnail = $file->thumbnail;
$smallthumb = $file->smallthumb;
$largethumb = $file->largethumb;
@@ -972,6 +341,7 @@ function file_delete($guid) {
* Returns an overall file type from the mimetype
*
* @param string $mimetype The MIME type
+ *
* @return string The overall type
*/
function file_get_general_file_type($mimetype) {
@@ -985,397 +355,35 @@ function file_get_general_file_type($mimetype) {
break;
}
- if (substr_count($mimetype,'text/')) {
+ if (substr_count($mimetype, 'text/')) {
return "document";
}
- if (substr_count($mimetype,'audio/')) {
+ if (substr_count($mimetype, 'audio/')) {
return "audio";
}
- if (substr_count($mimetype,'image/')) {
+ if (substr_count($mimetype, 'image/')) {
return "image";
}
- if (substr_count($mimetype,'video/')) {
+ if (substr_count($mimetype, 'video/')) {
return "video";
}
- if (substr_count($mimetype,'opendocument')) {
+ if (substr_count($mimetype, 'opendocument')) {
return "document";
}
return "general";
}
-function file_handle_upload($prefix,$subtype,$plugin) {
- $desc = get_input("description");
- $tags = get_input("tags");
- $tags = explode(",", $tags);
- $folder = get_input("folder_text");
- if (!$folder) {
- $folder = get_input("folder_select");
- }
- $access_id = (int) get_input("access_id");
- $container_guid = (int) get_input('container_guid', 0);
- if (!$container_guid) {
- $container_guid == get_loggedin_userid();
- }
-
- // Extract file from, save to default filestore (for now)
-
- // see if a plugin has set a quota for this user
- $file_quota = trigger_plugin_hook("$plugin:quotacheck",'user',array('container_guid'=>$container_guid));
- if (!$file_quota) {
- // no, see if there is a generic quota set
- $file_quota = get_plugin_setting('quota', $plugin);
- }
- if ($file_quota) {
- // convert to megabytes
- $file_quota = $file_quota*1000*1024;
- }
-
- // handle uploaded files
- $number_of_files = get_input('number_of_files',0);
- $quota_exceeded = false;
- $bad_mime_type = false;
-
- for ($i = 0; $i < $number_of_files; $i++) {
- $title = get_input("title_".$i);
- $uploaded = $_FILES["upload_".$i];
- if (!$uploaded || !$uploaded['name']) {
- // no such file, so skip it
- continue;
- }
- if ($plugin == "photo") {
- // do a mime type test
- if (in_array($uploaded['type'],array('image/jpeg','image/gif','image/png','image/jpg','image/jpe','image/pjpeg','image/x-png'))) {
- $file = new PhotoPluginFile();
- } else {
- $bad_mime_type = true;
- break;
- }
- } else {
- $file = new FilePluginFile();
- }
- $dir_size = $file->getFilestoreSize($prefix,$container_guid);
- $filestorename = strtolower(time().$uploaded['name']);
- $file->setFilename($prefix.$filestorename);
- $file->setMimeType($uploaded['type']);
-
- $file->originalfilename = $uploaded['name'];
-
- $file->subtype = $subtype;
-
- $file->access_id = $access_id;
-
- $uf = get_uploaded_file('upload_'.$i);
-
- if ($file_quota) {
- $file_size = strlen($uf);
- if (($dir_size + $file_size) > $file_quota) {
- $quota_exceeded = true;
- }
- }
-
- if (!$quota_exceeded) {
- // all clear, so try to save the data
-
- $file->open("write");
- $file->write($uf);
- $file->close();
-
- $file->title = $title;
- $file->description = $desc;
- if ($container_guid) {
- $file->container_guid = $container_guid;
- }
-
- // Save tags
- $file->tags = $tags;
-
- $file->simpletype = file_get_general_file_type($uploaded['type']);
- $file->folder = $folder;
-
- $result = $file->save();
-
- if ($result) {
-
- // Generate thumbnail (if image)
- if (substr_count($file->getMimeType(),'image/')) {
- $thumbnail = get_resized_image_from_existing_file($file->getFilenameOnFilestore(),60,60, true);
- $thumbsmall = get_resized_image_from_existing_file($file->getFilenameOnFilestore(),153,153, true);
- $thumblarge = get_resized_image_from_existing_file($file->getFilenameOnFilestore(),600,600, false);
- if ($thumbnail) {
- $thumb = new ElggFile();
- $thumb->setMimeType($uploaded['type']);
-
- $thumb->setFilename($prefix."thumb".$filestorename);
- $thumb->open("write");
- $thumb->write($thumbnail);
- $thumb->close();
-
- $file->thumbnail = $prefix."thumb".$filestorename;
-
- $thumb->setFilename($prefix."smallthumb".$filestorename);
- $thumb->open("write");
- $thumb->write($thumbsmall);
- $thumb->close();
- $file->smallthumb = $prefix."smallthumb".$filestorename;
-
- $thumb->setFilename($prefix."largethumb".$filestorename);
- $thumb->open("write");
- $thumb->write($thumblarge);
- $thumb->close();
- $file->largethumb = $prefix."largethumb".$filestorename;
- }
- }
-
- // add to this user's file folders
- file_add_to_folders($folder,$container_guid,$plugin);
-
- add_to_river("river/object/$plugin/create",'create',$_SESSION['user']->guid,$file->guid);
- } else {
- break;
- }
- } else {
- break;
- }
- }
-
- if ($quota_exceeded) {
- echo elgg_echo("$plugin:quotaexceeded");
- } else if ($bad_mime_type) {
- echo elgg_echo("$plugin:badmimetype");
- } else if ($result) {
- if ($number_of_files > 1) {
- echo elgg_echo("$plugin:saved_multi");
- } else {
- echo elgg_echo("$plugin:saved");
- }
- } else {
- if ($number_of_files > 1) {
- echo elgg_echo("$plugin:uploadfailed_multi");
- } else {
- echo elgg_echo("$plugin:uploadfailed");
- }
- }
-}
-
-function file_add_to_folders($folder,$container_guid,$plugin) {
- if ($container_guid && ($container = get_entity($container_guid))) {
- $folder_field_name = 'elgg_'.$plugin.'_folders';
- $folders = $container->$folder_field_name;
- if ($folders) {
- if (is_array($folders)) {
- if (!in_array($folder,$folders)) {
- $folders[] = $folder;
- $container->$folder_field_name = $folders;
- }
- } else {
- if ($folders != $folder) {
- $container->$folder_field_name = array($folders,$folder);
- }
- }
- } else {
- $container->$folder_field_name = $folder;
- }
- }
-}
-
-function file_handle_save($forward,$plugin) {
- // Get variables
- $title = get_input("title");
- $desc = get_input("description");
- $tags = get_input("tags");
- $folder = get_input("folder_text");
- if (!$folder) {
- $folder = get_input("folder_select");
- }
- $access_id = (int) get_input("access_id");
-
- $guid = (int) get_input('file_guid');
-
- if (!$file = get_entity($guid)) {
- register_error(elgg_echo("$plugin:uploadfailed"));
- forward($forward . $_SESSION['user']->username);
- exit;
- }
-
- $result = false;
-
- $container_guid = $file->container_guid;
- $container = get_entity($container_guid);
-
- if ($file->canEdit()) {
- $file->access_id = $access_id;
- $file->title = $title;
- $file->description = $desc;
- $file->folder = $folder;
- // add to this user's file folders
- file_add_to_folders($folder,$container_guid,$plugin);
-
- // Save tags
- $tags = explode(",", $tags);
- $file->tags = $tags;
-
- $result = $file->save();
- }
-
- if ($result) {
- system_message(elgg_echo("$plugin:saved"));
- } else {
- register_error(elgg_echo("$plugin:uploadfailed"));
- }
- forward($forward . $container->username);
-}
-
/**
- * Manage a file download.
+ * Delete a directory and all its contents
*
- * @param unknown_type $plugin
- * @param unknown_type $file_guid If not specified then file_guid will be found in input.
- */
-function file_manage_download($plugin, $file_guid = "") {
- // Get the guid
- $file_guid = (int)$file_guid;
-
- if (!$file_guid) {
- $file_guid = (int)get_input("file_guid");
- }
-
- // Get the file
- $file = get_entity($file_guid);
-
- if ($file) {
- $mime = $file->getMimeType();
- if (!$mime) {
- $mime = "application/octet-stream";
- }
-
- $filename = $file->originalfilename;
-
- header("Content-type: $mime");
- if (strpos($mime, "image/")!==false) {
- header("Content-Disposition: inline; filename=\"$filename\"");
- } else {
- header("Content-Disposition: attachment; filename=\"$filename\"");
- }
-
- echo $file->grabFile();
- exit;
- } else {
- register_error(elgg_echo("$plugin:downloadfailed"));
- }
-}
-
-/**
- * Manage the download of a file icon.
+ * @param string $directory Directory to delete
*
- * @param unknown_type $plugin
- * @param unknown_type $file_guid The guid, if not specified this is obtained from the input.
- */
-function file_manage_icon_download($plugin, $file_guid = "") {
- // Get the guid
- $file_guid = (int)$file_guid;
-
- if (!$file_guid) {
- $file_guid = (int)get_input("file_guid");
- }
-
- // Get the file
- $file = get_entity($file_guid);
-
- if ($file) {
- $mime = $file->getMimeType();
- if (!$mime) {
- $mime = "application/octet-stream";
- }
-
- $filename = $file->thumbnail;
-
- header("Content-type: $mime");
- if (strpos($mime, "image/")!==false) {
- header("Content-Disposition: inline; filename=\"$filename\"");
- } else {
- header("Content-Disposition: attachment; filename=\"$filename\"");
- }
-
- $readfile = new ElggFile();
- $readfile->owner_guid = $file->owner_guid;
- $readfile->setFilename($filename);
-
- /*
- if ($file->open("read"));
- {
- while (!$file->eof())
- {
- echo $file->read(10240, $file->tell());
- }
- }
- */
-
- $contents = $readfile->grabFile();
- if (empty($contents)) {
- echo file_get_contents(dirname(dirname(__FILE__)) . "/graphics/icons/general.jpg" );
- } else {
- echo $contents;
- }
- exit;
- } else {
- register_error(elgg_echo("$plugin:downloadfailed"));
- }
-}
-
-function file_display_thumbnail($file_guid,$size) {
- // Get file entity
- if ($file = get_entity($file_guid)) {
- $simpletype = $file->simpletype;
- if ($simpletype == "image") {
- // Get file thumbnail
- if ($size == "small") {
- $thumbfile = $file->smallthumb;
- } else {
- $thumbfile = $file->largethumb;
- }
-
- // Grab the file
- if ($thumbfile && !empty($thumbfile)) {
- $readfile = new ElggFile();
- $readfile->owner_guid = $file->owner_guid;
- $readfile->setFilename($thumbfile);
- $mime = $file->getMimeType();
- $contents = $readfile->grabFile();
-
- header("Content-type: $mime");
- echo $contents;
- exit;
- }
- }
- }
-}
-
-function file_set_page_owner($file) {
- $page_owner = page_owner_entity();
- if ($page_owner === false || is_null($page_owner)) {
- $container_guid = $file->container_guid;
- if (!empty($container_guid)) {
- if ($page_owner = get_entity($container_guid)) {
- set_page_owner($page_owner->guid);
- }
- }
-
- if (empty($page_owner)) {
- $page_owner = $_SESSION['user'];
- set_page_owner($_SESSION['guid']);
- }
- }
-}
-
-/**
- * Recursively delete a directory
- *
- * @param str $directory
+ * @return bool
*/
function delete_directory($directory) {
// sanity check: must be a directory
@@ -1409,7 +417,11 @@ function delete_directory($directory) {
/**
* Removes all user files
*
- * @param ElggUser $user
+ * @warning This only deletes the physical files and not their entities.
+ * This will result in FileExceptions being thrown. Don't use this function.
+ *
+ * @param ElggUser $user And ElggUser
+ *
* @return void
*/
function clear_user_files($user) {
@@ -1439,6 +451,10 @@ function get_default_filestore() {
/**
* Set the default filestore for the system.
+ *
+ * @param ElggFilestore $filestore An ElggFilestore object.
+ *
+ * @return true
*/
function set_default_filestore(ElggFilestore $filestore) {
global $DEFAULT_FILE_STORE;
@@ -1449,7 +465,11 @@ function set_default_filestore(ElggFilestore $filestore) {
}
/**
- * Run once and only once.
+ * Register entity type objects, subtype file as
+ * ElggFile.
+ *
+ * @return void
+ * @access private
*/
function filestore_run_once() {
// Register a class
@@ -1458,25 +478,43 @@ function filestore_run_once() {
/**
* Initialise the file modules.
- * Listens to system boot and registers any appropriate file types and classes
+ * Listens to system init and configures the default filestore
+ *
+ * @return void
+ * @access private
*/
function filestore_init() {
global $CONFIG;
// Now register a default filestore
- set_default_filestore(new ElggDiskFilestore($CONFIG->dataroot));
-
+ if (isset($CONFIG->dataroot)) {
+ set_default_filestore(new ElggDiskFilestore($CONFIG->dataroot));
+ }
+
// Now run this stuff, but only once
run_function_once("filestore_run_once");
}
-// Register a startup event
-register_elgg_event_handler('init', 'system', 'filestore_init', 100);
-
-// Unit testing
-register_plugin_hook('unit_test', 'system', 'filestore_test');
+/**
+ * Unit tests for files
+ *
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of tests
+ * @param mixed $params Params
+ *
+ * @return array
+ * @access private
+ */
function filestore_test($hook, $type, $value, $params) {
global $CONFIG;
$value[] = "{$CONFIG->path}engine/tests/objects/filestore.php";
return $value;
}
+
+
+// Register a startup event
+elgg_register_event_handler('init', 'system', 'filestore_init', 100);
+
+// Unit testing
+elgg_register_plugin_hook_handler('unit_test', 'system', 'filestore_test');
diff --git a/engine/lib/group.php b/engine/lib/group.php
index 68829dafb..6ded8a825 100644
--- a/engine/lib/group.php
+++ b/engine/lib/group.php
@@ -1,319 +1,20 @@
<?php
/**
* Elgg Groups.
- * Groups contain other entities, or rather act as a placeholder for other entities to mark any given container
- * as their container.
+ * Groups contain other entities, or rather act as a placeholder for other entities to
+ * mark any given container as their container.
*
- * @package Elgg
- * @subpackage Core
-
- * @author Curverider Ltd
-
- * @link http://elgg.org/
- */
-
-/**
- * @class ElggGroup Class representing a container for other elgg entities.
- * @author Curverider Ltd
+ * @package Elgg.Core
+ * @subpackage DataModel.Group
*/
-class ElggGroup extends ElggEntity
- implements Friendable {
-
- protected function initialise_attributes() {
- parent::initialise_attributes();
-
- $this->attributes['type'] = "group";
- $this->attributes['name'] = "";
- $this->attributes['description'] = "";
- $this->attributes['tables_split'] = 2;
- }
-
- /**
- * Construct a new user entity, optionally from a given id value.
- *
- * @param mixed $guid If an int, load that GUID.
- * If a db row then will attempt to load the rest of the data.
- * @throws Exception if there was a problem creating the user.
- */
- function __construct($guid = null) {
- $this->initialise_attributes();
-
- if (!empty($guid)) {
- // Is $guid is a DB row - either a entity row, or a user table row.
- if ($guid instanceof stdClass) {
- // Load the rest
- if (!$this->load($guid->guid)) {
- throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid->guid));
- }
- }
- // Is $guid is an ElggGroup? Use a copy constructor
- else if ($guid instanceof ElggGroup) {
- elgg_deprecated_notice('This type of usage of the ElggGroup constructor was deprecated. Please use the clone method.', 1.7);
-
- foreach ($guid->attributes as $key => $value) {
- $this->attributes[$key] = $value;
- }
- }
- // Is this is an ElggEntity but not an ElggGroup = ERROR!
- else if ($guid instanceof ElggEntity) {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggGroup'));
- }
- // We assume if we have got this far, $guid is an int
- else if (is_numeric($guid)) {
- if (!$this->load($guid)) {
- throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid));
- }
- }
-
- else {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
- }
- }
- }
-
- /**
- * Add an ElggObject to this group.
- *
- * @param ElggObject $object The object.
- * @return bool
- */
- public function addObjectToGroup(ElggObject $object) {
- return add_object_to_group($this->getGUID(), $object->getGUID());
- }
-
- /**
- * Remove an object from the containing group.
- *
- * @param int $guid The guid of the object.
- * @return bool
- */
- public function removeObjectFromGroup($guid) {
- return remove_object_from_group($this->getGUID(), $guid);
- }
-
- public function get($name) {
- if ($name == 'username') {
- return 'group:' . $this->getGUID();
- }
- return parent::get($name);
- }
-
-/**
- * Start friendable compatibility block:
- *
- * public function addFriend($friend_guid);
- public function removeFriend($friend_guid);
- public function isFriend();
- public function isFriendsWith($user_guid);
- public function isFriendOf($user_guid);
- public function getFriends($subtype = "", $limit = 10, $offset = 0);
- public function getFriendsOf($subtype = "", $limit = 10, $offset = 0);
- public function getObjects($subtype="", $limit = 10, $offset = 0);
- public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0);
- public function countObjects($subtype = "");
- */
-
- /**
- * For compatibility with Friendable
- */
- public function addFriend($friend_guid) {
- return $this->join(get_entity($friend_guid));
- }
-
- /**
- * For compatibility with Friendable
- */
- public function removeFriend($friend_guid) {
- return $this->leave(get_entity($friend_guid));
- }
-
- /**
- * For compatibility with Friendable
- */
- public function isFriend() {
- return $this->isMember();
- }
-
- /**
- * For compatibility with Friendable
- */
- public function isFriendsWith($user_guid) {
- return $this->isMember($user_guid);
- }
-
- /**
- * For compatibility with Friendable
- */
- public function isFriendOf($user_guid) {
- return $this->isMember($user_guid);
- }
-
- /**
- * For compatibility with Friendable
- */
- public function getFriends($subtype = "", $limit = 10, $offset = 0) {
- return get_group_members($this->getGUID(), $limit, $offset);
- }
-
- /**
- * For compatibility with Friendable
- */
- public function getFriendsOf($subtype = "", $limit = 10, $offset = 0) {
- return get_group_members($this->getGUID(), $limit, $offset);
- }
-
- /**
- * Get objects contained in this group.
- *
- * @param string $subtype
- * @param int $limit
- * @param int $offset
- * @return mixed
- */
- public function getObjects($subtype="", $limit = 10, $offset = 0) {
- return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);
- }
-
- /**
- * For compatibility with Friendable
- */
- public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) {
- return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);
- }
-
- /**
- * For compatibility with Friendable
- */
- public function countObjects($subtype = "") {
- return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", 10, 0, true);
- }
-
-/**
- * End friendable compatibility block
- */
-
- /**
- * Get a list of group members.
- *
- * @param int $limit
- * @param int $offset
- * @return mixed
- */
- public function getMembers($limit = 10, $offset = 0, $count = false) {
- return get_group_members($this->getGUID(), $limit, $offset, 0 , $count);
- }
-
- /**
- * Returns whether the current group is public membership or not.
- * @return bool
- */
- public function isPublicMembership() {
- if ($this->membership == ACCESS_PUBLIC) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Return whether a given user is a member of this group or not.
- *
- * @param ElggUser $user The user
- * @return bool
- */
- public function isMember($user = 0) {
- if (!($user instanceof ElggUser)) {
- $user = get_loggedin_user();
- }
- if (!($user instanceof ElggUser)) {
- return false;
- }
- return is_group_member($this->getGUID(), $user->getGUID());
- }
-
- /**
- * Join an elgg user to this group.
- *
- * @param ElggUser $user
- * @return bool
- */
- public function join(ElggUser $user) {
- return join_group($this->getGUID(), $user->getGUID());
- }
-
- /**
- * Remove a user from the group.
- *
- * @param ElggUser $user
- */
- public function leave(ElggUser $user) {
- return leave_group($this->getGUID(), $user->getGUID());
- }
-
- /**
- * Override the load function.
- * This function will ensure that all data is loaded (were possible), so
- * if only part of the ElggGroup is loaded, it'll load the rest.
- *
- * @param int $guid
- */
- protected function load($guid) {
- // Test to see if we have the generic stuff
- if (!parent::load($guid)) {
- return false;
- }
-
- // Check the type
- if ($this->attributes['type']!='group') {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, get_class()));
- }
-
- // Load missing data
- $row = get_group_entity_as_row($guid);
- if (($row) && (!$this->isFullyLoaded())) {
- // If $row isn't a cached copy then increment the counter
- $this->attributes['tables_loaded'] ++;
- }
-
- // Now put these into the attributes array as core values
- $objarray = (array) $row;
- foreach($objarray as $key => $value) {
- $this->attributes[$key] = $value;
- }
-
- return true;
- }
-
- /**
- * Override the save function.
- */
- public function save() {
- // Save generic stuff
- if (!parent::save()) {
- return false;
- }
-
- // Now save specific stuff
- return create_group_entity($this->get('guid'), $this->get('name'), $this->get('description'));
- }
-
- // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an array of fields which can be exported.
- */
- public function getExportableValues() {
- return array_merge(parent::getExportableValues(), array(
- 'name',
- 'description',
- ));
- }
-}
/**
* Get the group entity.
*
- * @param int $guid
+ * @param int $guid GUID for a group
+ *
+ * @return array|false
+ * @access private
*/
function get_group_entity_as_row($guid) {
global $CONFIG;
@@ -324,12 +25,15 @@ function get_group_entity_as_row($guid) {
}
/**
- * Create or update the extras table for a given group.
+ * Create or update the entities table for a given group.
* Call create_entity first.
*
- * @param int $guid
- * @param string $name
- * @param string $description
+ * @param int $guid GUID
+ * @param string $name Name
+ * @param string $description Description
+ *
+ * @return bool
+ * @access private
*/
function create_group_entity($guid, $name, $description) {
global $CONFIG;
@@ -342,28 +46,32 @@ function create_group_entity($guid, $name, $description) {
if ($row) {
// Exists and you have access to it
- if ($exists = get_data_row("SELECT guid from {$CONFIG->dbprefix}groups_entity WHERE guid = {$guid}")) {
- $result = update_data("UPDATE {$CONFIG->dbprefix}groups_entity set name='$name', description='$description' where guid=$guid");
- if ($result!=false) {
+ $exists = get_data_row("SELECT guid from {$CONFIG->dbprefix}groups_entity WHERE guid = {$guid}");
+ if ($exists) {
+ $query = "UPDATE {$CONFIG->dbprefix}groups_entity set"
+ . " name='$name', description='$description' where guid=$guid";
+ $result = update_data($query);
+ if ($result != false) {
// Update succeeded, continue
$entity = get_entity($guid);
- if (trigger_elgg_event('update',$entity->type,$entity)) {
+ if (elgg_trigger_event('update', $entity->type, $entity)) {
return $guid;
} else {
$entity->delete();
- //delete_entity($guid);
}
}
} else {
// Update failed, attempt an insert.
- $result = insert_data("INSERT into {$CONFIG->dbprefix}groups_entity (guid, name, description) values ($guid, '$name','$description')");
- if ($result!==false) {
+ $query = "INSERT into {$CONFIG->dbprefix}groups_entity"
+ . " (guid, name, description) values ($guid, '$name', '$description')";
+
+ $result = insert_data($query);
+ if ($result !== false) {
$entity = get_entity($guid);
- if (trigger_elgg_event('create',$entity->type,$entity)) {
+ if (elgg_trigger_event('create', $entity->type, $entity)) {
return $guid;
} else {
$entity->delete();
- //delete_entity($guid);
}
}
}
@@ -372,28 +80,14 @@ function create_group_entity($guid, $name, $description) {
return false;
}
-
-/**
- * THIS FUNCTION IS DEPRECATED.
- *
- * Delete a group's extra data.
- *
- * @param int $guid The guid of the group
- * @return bool
- */
-function delete_group_entity($guid) {
- system_message(sprintf(elgg_echo('deprecatedfunction'), 'delete_user_entity'));
-
- // Always return that we have deleted one row in order to not break existing code.
- return 1;
-}
-
/**
* Add an object to the given group.
*
- * @param int $group_guid The group to add the object to.
+ * @param int $group_guid The group to add the object to.
* @param int $object_guid The guid of the elgg object (must be ElggObject or a child thereof)
+ *
* @return bool
+ * @throws InvalidClassException
*/
function add_object_to_group($group_guid, $object_guid) {
$group_guid = (int)$group_guid;
@@ -407,11 +101,13 @@ function add_object_to_group($group_guid, $object_guid) {
}
if (!($group instanceof ElggGroup)) {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $group_guid, 'ElggGroup'));
+ $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($group_guid, 'ElggGroup'));
+ throw new InvalidClassException($msg);
}
if (!($object instanceof ElggObject)) {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $object_guid, 'ElggObject'));
+ $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($object_guid, 'ElggObject'));
+ throw new InvalidClassException($msg);
}
$object->container_guid = $group_guid;
@@ -421,8 +117,11 @@ function add_object_to_group($group_guid, $object_guid) {
/**
* Remove an object from the given group.
*
- * @param int $group_guid The group to remove the object from
+ * @param int $group_guid The group to remove the object from
* @param int $object_guid The object to remove
+ *
+ * @return bool
+ * @throws InvalidClassException
*/
function remove_object_from_group($group_guid, $object_guid) {
$group_guid = (int)$group_guid;
@@ -436,11 +135,13 @@ function remove_object_from_group($group_guid, $object_guid) {
}
if (!($group instanceof ElggGroup)) {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $group_guid, 'ElggGroup'));
+ $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($group_guid, 'ElggGroup'));
+ throw new InvalidClassException($msg);
}
if (!($object instanceof ElggObject)) {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $object_guid, 'ElggObject'));
+ $msg = elgg_echo('InvalidClassException:NotValidElggStar', array($object_guid, 'ElggObject'));
+ throw new InvalidClassException($msg);
}
$object->container_guid = $object->owner_guid;
@@ -448,301 +149,14 @@ function remove_object_from_group($group_guid, $object_guid) {
}
/**
- * Return an array of objects in a given container.
- * @see get_entities()
- *
- * @param int $group_guid The container (defaults to current page owner)
- * @param string $subtype The subtype
- * @param int $owner_guid Owner
- * @param int $site_guid The site
- * @param string $order_by Order
- * @param unknown_type $limit Limit on number of elements to return, by default 10.
- * @param unknown_type $offset Where to start, by default 0.
- * @param unknown_type $count Whether to return the entities or a count of them.
- */
-function get_objects_in_group($group_guid, $subtype = "", $owner_guid = 0, $site_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false) {
- global $CONFIG;
-
- if ($subtype === false || $subtype === null || $subtype === 0) {
- return false;
- }
-
- $subtype = get_subtype_id('object', $subtype);
-
- if ($order_by == "") {
- $order_by = "e.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;
- }
-
- $container_guid = (int)$group_guid;
- if ($container_guid == 0) {
- $container_guid = page_owner();
- }
-
- $where = array();
-
- $where[] = "e.type='object'";
- if ($subtype!=="") {
- $where[] = "e.subtype=$subtype";
- }
- if ($owner_guid != "") {
- if (!is_array($owner_guid)) {
- $owner_guid = (int) $owner_guid;
- $where[] = "e.container_guid = '$owner_guid'";
- } else if (sizeof($owner_guid) > 0) {
- // Cast every element to the owner_guid array to int
- $owner_guid = array_map("sanitise_int", $owner_guid);
- $owner_guid = implode(",",$owner_guid);
- $where[] = "e.container_guid in ({$owner_guid})";
- }
- }
- if ($site_guid > 0) {
- $where[] = "e.site_guid = {$site_guid}";
- }
-
- if ($container_guid > 0) {
- $where[] = "e.container_guid = {$container_guid}";
- }
-
- if (!$count) {
- $query = "SELECT * from {$CONFIG->dbprefix}entities e join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid where ";
- } else {
- $query = "SELECT count(e.guid) as total from {$CONFIG->dbprefix}entities e join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid where ";
- }
- foreach ($where as $w) {
- $query .= " $w and ";
- }
-
- // Add access controls
- $query .= get_access_sql_suffix('e');
- if (!$count) {
- $query .= " order by $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;
- }
-}
-
-/**
- * Get all the entities from metadata from a group.
- *
- * @param int $group_guid The ID of the group.
- * @param mixed $meta_name
- * @param mixed $meta_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 $limit
- * @param int $offset
- * @param string $order_by Optional ordering.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @param true|false $count If set to true, returns the total number of entities rather than a list. (Default: false)
- */
-function get_entities_from_metadata_groups($group_guid, $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);
-
- $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)) {
- 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;
- }
-
- $container_guid = (int)$group_guid;
- if ($container_guid == 0) {
- $container_guid = page_owner();
- }
-
- //$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 ($container_guid > 0) {
- $where[] = "e.container_guid = {$container_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}";
-
- if (!$count) {
- $query = "SELECT distinct e.* ";
- } else {
- $query = "SELECT count(e.guid) as total ";
- }
-
- $query .= "from {$CONFIG->dbprefix}entities e JOIN {$CONFIG->dbprefix}metadata m on e.guid = m.entity_guid join {$CONFIG->dbprefix}objects_entity o on e.guid = o.guid 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 ($row = get_data_row($query)) {
- return $row->total;
- }
- }
- return false;
-}
-
-/**
- * As get_entities_from_metadata_groups() but with multiple entities.
- *
- * @param int $group_guid The ID of the group.
- * @param array $meta_array Array of 'name' => 'value' pairs
- * @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 $limit
- * @param int $offset
- * @param string $order_by Optional ordering.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @param true|false $count If set to true, returns the total number of entities rather than a list. (Default: false)
- * @return int|array List of ElggEntities, or the total number if count is set to false
- */
-function get_entities_from_metadata_groups_multi($group_guid, $meta_array, $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0, $count = false) {
- global $CONFIG;
-
- if (!is_array($meta_array) || sizeof($meta_array) == 0) {
- return false;
- }
-
- $where = array();
-
- $mindex = 1;
- $join = "";
- foreach($meta_array as $meta_name => $meta_value) {
- $meta_n = get_metastring_id($meta_name);
- $meta_v = get_metastring_id($meta_value);
- $join .= " JOIN {$CONFIG->dbprefix}metadata m{$mindex} on e.guid = m{$mindex}.entity_guid join {$CONFIG->dbprefix}objects_entity o on e.guid = o.guid ";
- if ($meta_name!="") {
- $where[] = "m{$mindex}.name_id='$meta_n'";
- }
-
- if ($meta_value!="") {
- $where[] = "m{$mindex}.value_id='$meta_v'";
- }
-
- $mindex++;
- }
-
- $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);
- $owner_guid = (int) $owner_guid;
-
- $site_guid = (int) $site_guid;
- if ($site_guid == 0) {
- $site_guid = $CONFIG->site_guid;
- }
-
- //$access = get_access_list();
-
- if ($entity_type!="") {
- $where[] = "e.type = '{$entity_type}'";
- }
-
- if ($entity_subtype) {
- $where[] = "e.subtype = {$entity_subtype}";
- }
-
- if ($site_guid > 0) {
- $where[] = "e.site_guid = {$site_guid}";
- }
-
- if ($owner_guid > 0) {
- $where[] = "e.owner_guid = {$owner_guid}";
- }
-
- if ($container_guid > 0) {
- $where[] = "e.container_guid = {$container_guid}";
- }
-
- if ($count) {
- $query = "SELECT count(e.guid) as total ";
- } else {
- $query = "SELECT distinct e.* ";
- }
-
- $query .= " from {$CONFIG->dbprefix}entities e {$join} where";
- foreach ($where as $w) {
- $query .= " $w and ";
- }
- $query .= get_access_sql_suffix("e"); // Add access controls
-
- 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;
-}
-
-/**
* Return a list of this group's members.
*
- * @param int $group_guid The ID of the container/group.
- * @param int $limit The limit
- * @param int $offset The offset
- * @param int $site_guid The site
- * @param bool $count Return the users (false) or the count of them (true)
+ * @param int $group_guid The ID of the container/group.
+ * @param int $limit The limit
+ * @param int $offset The offset
+ * @param int $site_guid The site
+ * @param bool $count Return the users (false) or the count of them (true)
+ *
* @return mixed
*/
function get_group_members($group_guid, $limit = 10, $offset = 0, $site_guid = 0, $count = false) {
@@ -756,7 +170,7 @@ function get_group_members($group_guid, $limit = 10, $offset = 0, $site_guid = 0
'relationship' => 'member',
'relationship_guid' => $group_guid,
'inverse_relationship' => TRUE,
- 'types' => 'user',
+ 'type' => 'user',
'limit' => $limit,
'offset' => $offset,
'count' => $count,
@@ -768,7 +182,8 @@ function get_group_members($group_guid, $limit = 10, $offset = 0, $site_guid = 0
* Return whether a given user is a member of the group or not.
*
* @param int $group_guid The group ID
- * @param int $user_guid The user guid
+ * @param int $user_guid The user guid
+ *
* @return bool
*/
function is_group_member($group_guid, $user_guid) {
@@ -783,12 +198,19 @@ function is_group_member($group_guid, $user_guid) {
/**
* Join a user to a group.
*
- * @param int $group_guid The group.
- * @param int $user_guid The user.
+ * @param int $group_guid The group GUID.
+ * @param int $user_guid The user GUID.
+ *
+ * @return bool
*/
function join_group($group_guid, $user_guid) {
$result = add_entity_relationship($user_guid, 'member', $group_guid);
- trigger_elgg_event('join', 'group', array('group' => get_entity($group_guid), 'user' => get_entity($user_guid)));
+
+ if ($result) {
+ $params = array('group' => get_entity($group_guid), 'user' => get_entity($user_guid));
+ elgg_trigger_event('join', 'group', $params);
+ }
+
return $result;
}
@@ -796,11 +218,15 @@ function join_group($group_guid, $user_guid) {
* Remove a user from a group.
*
* @param int $group_guid The group.
- * @param int $user_guid The user.
+ * @param int $user_guid The user.
+ *
+ * @return bool
*/
function leave_group($group_guid, $user_guid) {
// event needs to be triggered while user is still member of group to have access to group acl
- trigger_elgg_event('leave', 'group', array('group' => get_entity($group_guid), 'user' => get_entity($user_guid)));
+ $params = array('group' => get_entity($group_guid), 'user' => get_entity($user_guid));
+
+ elgg_trigger_event('leave', 'group', $params);
$result = remove_entity_relationship($user_guid, 'member', $group_guid);
return $result;
}
@@ -808,58 +234,73 @@ function leave_group($group_guid, $user_guid) {
/**
* Return all groups a user is a member of.
*
- * @param unknown_type $user_guid
+ * @param int $user_guid GUID of user
+ *
+ * @return array|false
*/
function get_users_membership($user_guid) {
- return elgg_get_entities_from_relationship(array('relationship' => 'member', 'relationship_guid' => $user_guid, 'inverse_relationship' => FALSE));
+ $options = array(
+ 'type' => 'group',
+ 'relationship' => 'member',
+ 'relationship_guid' => $user_guid,
+ 'inverse_relationship' => false,
+ 'limit' => false,
+ );
+ return elgg_get_entities_from_relationship($options);
}
/**
- * Checks access to a group.
+ * May the current user access item(s) on this page? If the page owner is a group,
+ * membership, visibility, and logged in status are taken into account.
+ *
+ * @param boolean $forward If set to true (default), will forward the page;
+ * if set to false, will return true or false.
*
- * @param boolean $forward If set to true (default), will forward the page; if set to false, will return true or false.
- * @return true|false If $forward is set to false.
+ * @return bool If $forward is set to false.
*/
function group_gatekeeper($forward = true) {
- $allowed = true;
- $url = '';
-
- if ($group = page_owner_entity()) {
- if ($group instanceof ElggGroup) {
- $url = $group->getURL();
- if (
- ((!isloggedin()) && (!$group->isPublicMembership())) ||
- ((!$group->isMember(get_loggedin_user()) && (!$group->isPublicMembership())))
- ) {
- $allowed = false;
- }
- // Admin override
- if (isadminloggedin()) {
- $allowed = true;
- }
- }
+ $page_owner_guid = elgg_get_page_owner_guid();
+ if (!$page_owner_guid) {
+ return true;
}
+ $visibility = ElggGroupItemVisibility::factory($page_owner_guid);
- if ($forward && $allowed == false) {
- register_error(elgg_echo('membershiprequired'));
- forward($url);
- exit;
+ if (!$visibility->shouldHideItems) {
+ return true;
}
+ if ($forward) {
+ // only forward to group if user can see it
+ $group = get_entity($page_owner_guid);
+ $forward_url = $group ? $group->getURL() : '';
- return $allowed;
+ if (!elgg_is_logged_in()) {
+ $_SESSION['last_forward_from'] = current_page_url();
+ $forward_reason = 'login';
+ } else {
+ $forward_reason = 'member';
+ }
+
+ register_error(elgg_echo($visibility->reasonHidden));
+ forward($forward_url, $forward_reason);
+ }
+
+ return false;
}
/**
- * Manages group tool options
+ * Adds a group tool option
*
- * @param string $name Name of the group tool option
- * @param string $label Used for the group edit form
- * @param boolean $default_on True if this option should be active by default
+ * @see remove_group_tool_option().
*
- **/
-
-function add_group_tool_option($name,$label,$default_on=true) {
+ * @param string $name Name of the group tool option
+ * @param string $label Used for the group edit form
+ * @param bool $default_on True if this option should be active by default
+ *
+ * @return void
+ * @since 1.5.0
+ */
+function add_group_tool_option($name, $label, $default_on = true) {
global $CONFIG;
if (!isset($CONFIG->group_tool_options)) {
@@ -876,104 +317,25 @@ function add_group_tool_option($name,$label,$default_on=true) {
}
/**
- * Searches for a group based on a complete or partial name or description
+ * Removes a group tool option based on name
*
- * @param string $criteria The partial or full name or description
- * @param int $limit Limit of the search.
- * @param int $offset Offset.
- * @param string $order_by The order.
- * @param boolean $count Whether to return the count of results or just the results.
- * @deprecated 1.7
+ * @see add_group_tool_option()
+ *
+ * @param string $name Name of the group tool option
+ *
+ * @return void
+ * @since 1.7.5
*/
-function search_for_group($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) {
- elgg_deprecated_notice('search_for_group() was deprecated by new search plugin.', 1.7);
+function remove_group_tool_option($name) {
global $CONFIG;
- $criteria = sanitise_string($criteria);
- $limit = (int)$limit;
- $offset = (int)$offset;
- $order_by = sanitise_string($order_by);
-
- $access = get_access_sql_suffix("e");
-
- if ($order_by == "") {
- $order_by = "e.time_created desc";
- }
-
- if ($count) {
- $query = "SELECT count(e.guid) as total ";
- } else {
- $query = "SELECT e.* ";
- }
- $query .= "from {$CONFIG->dbprefix}entities e join {$CONFIG->dbprefix}groups_entity g on e.guid=g.guid where ";
- // $query .= " match(u.name,u.username) against ('$criteria') ";
- $query .= "(g.name like \"%{$criteria}%\" or g.description like \"%{$criteria}%\")";
- $query .= " and $access";
-
- 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;
- }
+ if (!isset($CONFIG->group_tool_options)) {
+ return;
}
- return false;
-}
-
-/**
- * Returns a formatted list of groups suitable for injecting into search.
- * @deprecated 1.7
- */
-function search_list_groups_by_name($hook, $user, $returnvalue, $tag) {
- elgg_deprecated_notice('search_list_groups_by_name() was deprecated by new search plugin', 1.7);
- // Change this to set the number of groups that display on the search page
- $threshold = 4;
- $object = get_input('object');
-
- if (!get_input('offset') && (empty($object) || $object == 'group')) {
- if ($groups = search_for_group($tag,$threshold)) {
- $countgroups = search_for_group($tag,0,0,"",true);
-
- $return = elgg_view('group/search/startblurb',array('count' => $countgroups, 'tag' => $tag));
- foreach($groups as $group) {
- $return .= elgg_view_entity($group);
- }
- $return .= elgg_view('group/search/finishblurb',array('count' => $countgroups, 'threshold' => $threshold, 'tag' => $tag));
- return $return;
+ foreach ($CONFIG->group_tool_options as $i => $option) {
+ if ($option->name == $name) {
+ unset($CONFIG->group_tool_options[$i]);
}
}
}
-
-/**
- * Displays a list of group objects that have been searched for.
- *
- * @see elgg_view_entity_list
- *
- * @param string $tag Search criteria
- * @param int $limit The number of entities to display on a page
- * @return string The list in a form suitable to display
- * @deprecated 1.7
- */
-function list_group_search($tag, $limit = 10) {
- elgg_deprecated_notice('list_group_search() was deprecated by new search plugin.', 1.7);
- $offset = (int) get_input('offset');
- $limit = (int) $limit;
- $count = (int) search_for_group($tag, 10, 0, '', true);
- $entities = search_for_group($tag, $limit, $offset);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, false);
-
-}
-
-/**
- * Performs initialisation functions for groups
- *
- */
-function group_init() {
- // Register an entity type
- register_entity_type('group','');
-}
-
-register_elgg_event_handler('init','system','group_init');
diff --git a/engine/lib/input.php b/engine/lib/input.php
index e5daa70ea..80b0b8766 100644
--- a/engine/lib/input.php
+++ b/engine/lib/input.php
@@ -3,51 +3,56 @@
* Parameter input functions.
* This file contains functions for getting input from get/post variables.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Input
*/
/**
- * Get some input from variables passed on the GET or POST line.
+ * Get some input from variables passed submitted through GET or POST.
+ *
+ * If using any data obtained from get_input() in a web page, please be aware that
+ * it is a possible vector for a reflected XSS attack. If you are expecting an
+ * integer, cast it to an int. If it is a string, escape quotes.
*
* Note: this function does not handle nested arrays (ex: form input of param[m][n])
* because of the filtering done in htmlawed from the filter_tags call.
+ * @todo Is this ^ still true?
+ *
+ * @param string $variable The variable name we want.
+ * @param mixed $default A default value for the variable if it is not found.
+ * @param bool $filter_result If true, then the result is filtered for bad tags.
*
- * @param $variable string The variable we want to return.
- * @param $default mixed A default value for the variable if it is not found.
- * @param $filter_result If true then the result is filtered for bad tags.
+ * @return mixed
*/
function get_input($variable, $default = NULL, $filter_result = TRUE) {
global $CONFIG;
+ $result = $default;
+
+ elgg_push_context('input');
+
if (isset($CONFIG->input[$variable])) {
- $var = $CONFIG->input[$variable];
+ $result = $CONFIG->input[$variable];
if ($filter_result) {
- $var = filter_tags($var);
+ $result = filter_tags($result);
}
-
- return $var;
- }
-
- if (isset($_REQUEST[$variable])) {
+ } elseif (isset($_REQUEST[$variable])) {
if (is_array($_REQUEST[$variable])) {
- $var = $_REQUEST[$variable];
+ $result = $_REQUEST[$variable];
} else {
- $var = trim($_REQUEST[$variable]);
+ $result = trim($_REQUEST[$variable]);
}
if ($filter_result) {
- $var = filter_tags($var);
+ $result = filter_tags($result);
}
-
- return $var;
}
- return $default;
+ elgg_pop_context();
+
+ return $result;
}
/**
@@ -55,8 +60,10 @@ function get_input($variable, $default = NULL, $filter_result = TRUE) {
*
* Note: this function does not handle nested arrays (ex: form input of param[m][n])
*
- * @param string $variable The name of the variable
- * @param string $value The value of the variable
+ * @param string $variable The name of the variable
+ * @param string|string[] $value The value of the variable
+ *
+ * @return void
*/
function set_input($variable, $value) {
global $CONFIG;
@@ -65,10 +72,7 @@ function set_input($variable, $value) {
}
if (is_array($value)) {
- foreach ($value as $key => $val) {
- $value[$key] = trim($val);
- }
-
+ array_walk_recursive($value, create_function('&$v, $k', '$v = trim($v);'));
$CONFIG->input[trim($variable)] = $value;
} else {
$CONFIG->input[trim($variable)] = trim($value);
@@ -79,46 +83,171 @@ function set_input($variable, $value) {
* Filter tags from a given string based on registered hooks.
*
* @param mixed $var Anything that does not include an object (strings, ints, arrays)
- * This includes multi-dimensional arrays.
+ * This includes multi-dimensional arrays.
+ *
* @return mixed The filtered result - everything will be strings
*/
function filter_tags($var) {
- return trigger_plugin_hook('validate', 'input', null, $var);
+ return elgg_trigger_plugin_hook('validate', 'input', null, $var);
+}
+
+/**
+ * Validates an email address.
+ *
+ * @param string $address Email address.
+ *
+ * @return bool
+ */
+function is_email_address($address) {
+ return filter_var($address, FILTER_VALIDATE_EMAIL) === $address;
+}
+
+/**
+ * Load all the REQUEST variables into the sticky form cache
+ *
+ * Call this from an action when you want all your submitted variables
+ * available if the submission fails validation and is sent back to the form
+ *
+ * @param string $form_name Name of the sticky form
+ *
+ * @return void
+ * @link http://docs.elgg.org/Tutorials/UI/StickyForms
+ * @since 1.8.0
+ */
+function elgg_make_sticky_form($form_name) {
+
+ elgg_clear_sticky_form($form_name);
+
+ if (!isset($_SESSION['sticky_forms'])) {
+ $_SESSION['sticky_forms'] = array();
+ }
+ $_SESSION['sticky_forms'][$form_name] = array();
+
+ foreach ($_REQUEST as $key => $var) {
+ // will go through XSS filtering on the get function
+ $_SESSION['sticky_forms'][$form_name][$key] = $var;
+ }
+}
+
+/**
+ * Clear the sticky form cache
+ *
+ * Call this if validation is successful in the action handler or
+ * when they sticky values have been used to repopulate the form
+ * after a validation error.
+ *
+ * @param string $form_name Form namespace
+ *
+ * @return void
+ * @link http://docs.elgg.org/Tutorials/UI/StickyForms
+ * @since 1.8.0
+ */
+function elgg_clear_sticky_form($form_name) {
+ unset($_SESSION['sticky_forms'][$form_name]);
+}
+
+/**
+ * Has this form been made sticky?
+ *
+ * @param string $form_name Form namespace
+ *
+ * @return boolean
+ * @link http://docs.elgg.org/Tutorials/UI/StickyForms
+ * @since 1.8.0
+ */
+function elgg_is_sticky_form($form_name) {
+ return isset($_SESSION['sticky_forms'][$form_name]);
+}
+
+/**
+ * Get a specific sticky variable
+ *
+ * @param string $form_name The name of the form
+ * @param string $variable The name of the variable
+ * @param mixed $default Default value if the variable does not exist in sticky cache
+ * @param boolean $filter_result Filter for bad input if true
+ *
+ * @return mixed
+ *
+ * @todo should this filter the default value?
+ * @link http://docs.elgg.org/Tutorials/UI/StickyForms
+ * @since 1.8.0
+ */
+function elgg_get_sticky_value($form_name, $variable = '', $default = NULL, $filter_result = true) {
+ if (isset($_SESSION['sticky_forms'][$form_name][$variable])) {
+ $value = $_SESSION['sticky_forms'][$form_name][$variable];
+ if ($filter_result) {
+ // XSS filter result
+ $value = filter_tags($value);
+ }
+ return $value;
+ }
+ return $default;
}
/**
- * Sanitise file paths for input, ensuring that they begin and end with slashes etc.
+ * Get all the values in a sticky form in an array
*
- * @param string $path The path
- * @return string
+ * @param string $form_name The name of the form
+ * @param bool $filter_result Filter for bad input if true
+ *
+ * @return array
+ * @since 1.8.0
*/
-function sanitise_filepath($path) {
- // Convert to correct UNIX paths
- $path = str_replace('\\', '/', $path);
+function elgg_get_sticky_values($form_name, $filter_result = true) {
+ if (!isset($_SESSION['sticky_forms'][$form_name])) {
+ return array();
+ }
- // Sort trailing slash
- $path = trim($path);
- // rtrim defaults plus /
- $path = rtrim($path, " \n\t\0\x0B/");
- $path = $path . "/";
+ $values = $_SESSION['sticky_forms'][$form_name];
+ if ($filter_result) {
+ foreach ($values as $key => $value) {
+ // XSS filter result
+ $values[$key] = filter_tags($value);
+ }
+ }
+ return $values;
+}
- return $path;
+/**
+ * Clear a specific sticky variable
+ *
+ * @param string $form_name The name of the form
+ * @param string $variable The name of the variable to clear
+ *
+ * @return void
+ * @link http://docs.elgg.org/Tutorials/UI/StickyForms
+ * @since 1.8.0
+ */
+function elgg_clear_sticky_value($form_name, $variable) {
+ unset($_SESSION['sticky_forms'][$form_name][$variable]);
}
/**
* Page handler for autocomplete endpoint.
*
- * @param $page
- * @return unknown_type
+ * @todo split this into functions/objects, this is way too big
+ *
+ * /livesearch?q=<query>
+ *
+ * Other options include:
+ * match_on string all or array(groups|users|friends)
+ * match_owner int 0/1
+ * limit int default is 10
+ *
+ * @param array $page
+ * @return string JSON string is returned and then exit
+ * @access private
*/
function input_livesearch_page_handler($page) {
global $CONFIG;
+
// only return results to logged in users.
- if (!$user = get_loggedin_user()) {
+ if (!$user = elgg_get_logged_in_user_entity()) {
exit;
}
- if (!$q = get_input('q')) {
+ if (!$q = get_input('term', get_input('q'))) {
exit;
}
@@ -128,134 +257,203 @@ function input_livesearch_page_handler($page) {
$q = str_replace(array('_', '%'), array('\_', '\%'), $q);
$match_on = get_input('match_on', 'all');
- if ($match_on == 'all' || $match_on[0] == 'all') {
- $match_on = array('users', 'groups');
- }
if (!is_array($match_on)) {
$match_on = array($match_on);
}
+ // all = users and groups
+ if (in_array('all', $match_on)) {
+ $match_on = array('users', 'groups');
+ }
+
if (get_input('match_owner', false)) {
- $owner_guid = $user->getGUID();
$owner_where = 'AND e.owner_guid = ' . $user->getGUID();
} else {
- $owner_guid = null;
$owner_where = '';
}
- $limit = get_input('limit', 10);
+ $limit = sanitise_int(get_input('limit', 10));
// grab a list of entities and send them in json.
$results = array();
- foreach ($match_on as $type) {
- switch ($type) {
- case 'all':
- // only need to pull up title from objects.
-
- if (!$entities = elgg_get_entities(array('owner_guid' => $owner_guid, 'limit' => $limit)) AND is_array($entities)) {
- $results = array_merge($results, $entities);
- }
- break;
-
+ foreach ($match_on as $match_type) {
+ switch ($match_type) {
case 'users':
$query = "SELECT * FROM {$CONFIG->dbprefix}users_entity as ue, {$CONFIG->dbprefix}entities as e
WHERE e.guid = ue.guid
AND e.enabled = 'yes'
AND ue.banned = 'no'
- AND (ue.name LIKE '$q%' OR ue.username LIKE '$q%')
+ AND (ue.name LIKE '$q%' OR ue.name LIKE '% $q%' OR ue.username LIKE '$q%')
LIMIT $limit
";
if ($entities = get_data($query)) {
foreach ($entities as $entity) {
- $json = json_encode(array(
+ // @todo use elgg_get_entities (don't query in a loop!)
+ $entity = get_entity($entity->guid);
+ /* @var ElggUser $entity */
+ if (!$entity) {
+ continue;
+ }
+
+ if (in_array('groups', $match_on)) {
+ $value = $entity->guid;
+ } else {
+ $value = $entity->username;
+ }
+
+ $output = elgg_view_list_item($entity, array(
+ 'use_hover' => false,
+ 'class' => 'elgg-autocomplete-item',
+ ));
+
+ $icon = elgg_view_entity_icon($entity, 'tiny', array(
+ 'use_hover' => false,
+ ));
+
+ $result = array(
'type' => 'user',
'name' => $entity->name,
'desc' => $entity->username,
- 'icon' => '<img class="livesearch_icon" src="' . get_entity($entity->guid)->getIcon('tiny') . '" />',
- 'guid' => $entity->guid
- ));
- $results[$entity->name . rand(1,100)] = $json;
+ 'guid' => $entity->guid,
+ 'label' => $output,
+ 'value' => $value,
+ 'icon' => $icon,
+ 'url' => $entity->getURL(),
+ );
+ $results[$entity->name . rand(1, 100)] = $result;
}
}
break;
case 'groups':
// don't return results if groups aren't enabled.
- if (!is_plugin_enabled('groups')) {
+ if (!elgg_is_active_plugin('groups')) {
continue;
}
$query = "SELECT * FROM {$CONFIG->dbprefix}groups_entity as ge, {$CONFIG->dbprefix}entities as e
WHERE e.guid = ge.guid
AND e.enabled = 'yes'
$owner_where
- AND (ge.name LIKE '$q%' OR ge.description LIKE '%$q%')
+ AND (ge.name LIKE '$q%' OR ge.name LIKE '% $q%' OR ge.description LIKE '% $q%')
LIMIT $limit
";
if ($entities = get_data($query)) {
foreach ($entities as $entity) {
- $json = json_encode(array(
+ // @todo use elgg_get_entities (don't query in a loop!)
+ $entity = get_entity($entity->guid);
+ /* @var ElggGroup $entity */
+ if (!$entity) {
+ continue;
+ }
+
+ $output = elgg_view_list_item($entity, array(
+ 'use_hover' => false,
+ 'class' => 'elgg-autocomplete-item',
+ ));
+
+ $icon = elgg_view_entity_icon($entity, 'tiny', array(
+ 'use_hover' => false,
+ ));
+
+ $result = array(
'type' => 'group',
'name' => $entity->name,
'desc' => strip_tags($entity->description),
- 'icon' => '<img class="livesearch_icon" src="' . get_entity($entity->guid)->getIcon('tiny') . '" />',
- 'guid' => $entity->guid
- ));
- //$results[$entity->name . rand(1,100)] = "$json|{$entity->guid}";
- $results[$entity->name . rand(1,100)] = $json;
+ 'guid' => $entity->guid,
+ 'label' => $output,
+ 'value' => $entity->guid,
+ 'icon' => $icon,
+ 'url' => $entity->getURL(),
+ );
+
+ $results[$entity->name . rand(1, 100)] = $result;
}
}
break;
case 'friends':
- $access = get_access_sql_suffix();
- $query = "SELECT * FROM {$CONFIG->dbprefix}users_entity as ue, {$CONFIG->dbprefix}entity_relationships as er, {$CONFIG->dbprefix}entities as e
+ $query = "SELECT * FROM
+ {$CONFIG->dbprefix}users_entity as ue,
+ {$CONFIG->dbprefix}entity_relationships as er,
+ {$CONFIG->dbprefix}entities as e
WHERE er.relationship = 'friend'
AND er.guid_one = {$user->getGUID()}
AND er.guid_two = ue.guid
AND e.guid = ue.guid
AND e.enabled = 'yes'
AND ue.banned = 'no'
- AND (ue.name LIKE '$q%' OR ue.username LIKE '$q%')
+ AND (ue.name LIKE '$q%' OR ue.name LIKE '% $q%' OR ue.username LIKE '$q%')
LIMIT $limit
";
if ($entities = get_data($query)) {
foreach ($entities as $entity) {
- $json = json_encode(array(
+ // @todo use elgg_get_entities (don't query in a loop!)
+ $entity = get_entity($entity->guid);
+ /* @var ElggUser $entity */
+ if (!$entity) {
+ continue;
+ }
+
+ $output = elgg_view_list_item($entity, array(
+ 'use_hover' => false,
+ 'class' => 'elgg-autocomplete-item',
+ ));
+
+ $icon = elgg_view_entity_icon($entity, 'tiny', array(
+ 'use_hover' => false,
+ ));
+
+ $result = array(
'type' => 'user',
'name' => $entity->name,
'desc' => $entity->username,
- 'icon' => '<img class="livesearch_icon" src="' . get_entity($entity->guid)->getIcon('tiny') . '" />',
- 'guid' => $entity->guid
- ));
- $results[$entity->name . rand(1,100)] = $json;
+ 'guid' => $entity->guid,
+ 'label' => $output,
+ 'value' => $entity->username,
+ 'icon' => $icon,
+ 'url' => $entity->getURL(),
+ );
+ $results[$entity->name . rand(1, 100)] = $result;
}
}
break;
default:
- // arbitrary subtype.
- //@todo you cannot specify a subtype without a type.
- // did this ever work?
- elgg_get_entities(array('subtype' => $type, 'owner_guid' => $owner_guid));
+ header("HTTP/1.0 400 Bad Request", true);
+ echo "livesearch: unknown match_on of $match_type";
+ exit;
break;
}
}
ksort($results);
- echo implode($results, "\n");
+ header("Content-Type: application/json");
+ echo json_encode(array_values($results));
exit;
}
-
+/**
+ * Register input functions and sanitize input
+ *
+ * @return void
+ * @access private
+ */
function input_init() {
// register an endpoint for live search / autocomplete.
- register_page_handler('livesearch', 'input_livesearch_page_handler');
+ elgg_register_page_handler('livesearch', 'input_livesearch_page_handler');
+
+ if (ini_get_bool('magic_quotes_gpc')) {
- if (ini_get_bool('magic_quotes_gpc') ) {
- //do keys as well, cos array_map ignores them
+ /**
+ * do keys as well, cos array_map ignores them
+ *
+ * @param array $array Array of values
+ *
+ * @return array Sanitized array
+ */
function stripslashes_arraykeys($array) {
if (is_array($array)) {
$array2 = array();
@@ -272,6 +470,13 @@ function input_init() {
}
}
+ /**
+ * Strip slashes on everything
+ *
+ * @param mixed $value The value to remove slashes from
+ *
+ * @return mixed
+ */
function stripslashes_deep($value) {
if (is_array($value)) {
$value = stripslashes_arraykeys($value);
@@ -312,4 +517,4 @@ function input_init() {
}
}
-register_elgg_event_handler('init','system','input_init');
+elgg_register_event_handler('init', 'system', 'input_init');
diff --git a/engine/lib/install.php b/engine/lib/install.php
deleted file mode 100644
index e2b0c5251..000000000
--- a/engine/lib/install.php
+++ /dev/null
@@ -1,140 +0,0 @@
-<?php
-
-/**
- * Elgg installation
- * Various functions to assist with installing and upgrading the system
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-
-/**
- * Check that the installed version of PHP meets the minimum requirements (currently 5.2 or greater).
- *
- * @return bool
- */
-function php_check_version() {
- if (version_compare(phpversion(), '5.1.2', '>=')) {
- return true;
- }
-
- return false;
-}
-
-/**
- * Validate the platform Elgg is being installed on.
- *
- * @throws ConfigurationException if the validation fails.
- * @return bool
- */
-function validate_platform() {
- // Get database version
- if (!db_check_version()) {
- throw new ConfigurationException(elgg_echo('ConfigurationException:BadDatabaseVersion'));
- }
-
- // Now check PHP
- if (!php_check_version()) {
- throw new ConfigurationException(elgg_echo('ConfigurationException:BadPHPVersion'));
- }
-
- // TODO: Consider checking for installed modules etc
- return true;
-}
-
-/**
- * Confirm the settings for the database
- *
- * @param string $user
- * @param string $password
- * @param string $dbname
- * @param string $host
- * @return bool
- */
-function db_check_settings($user, $password, $dbname, $host) {
- $mysql_dblink = mysql_connect($host, $user, $password, true);
- if ($mysql_dblink == FALSE) {
- return $FALSE;
- }
-
- $result = mysql_select_db($dbname, $mysql_dblink);
-
- mysql_close($mysql_dblink);
-
- return $result;
-}
-
-
-/**
- * Returns whether or not the database has been installed
- *
- * @return true|false Whether the database has been installed
- */
-function is_db_installed() {
- global $CONFIG;
-
- if (isset($CONFIG->db_installed)) {
- return $CONFIG->db_installed;
- }
-
- if ($dblink = get_db_link('read')) {
- mysql_query("select name from {$CONFIG->dbprefix}datalists limit 1", $dblink);
- if (mysql_errno($dblink) > 0) {
- return false;
- }
- } else {
- return false;
- }
-
- // Set flag if db is installed (if false then we want to check every time)
- $CONFIG->db_installed = true;
-
- return true;
-}
-
-/**
- * Returns whether or not other settings have been set
- *
- * @return true|false Whether or not the rest of the installation has been followed through with
- */
-function is_installed() {
- global $CONFIG;
- return datalist_get('installed');
-}
-
-/**
- * Copy and create a new settings.php from settings.example.php, substituting the variables in
- * $vars where appropriate.
- *
- * $vars is an associate array of $key => $value, where $key is the variable text you wish to substitute (eg
- * CONFIG_DBNAME will replace {{CONFIG_DBNAME}} in the settings file.
- *
- * @param array $vars The array of vars
- * @param string $in_file Optional input file (if not settings.example.php)
- * @return string The file containing substitutions.
- */
-function create_settings(array $vars, $in_file="engine/settings.example.php") {
- $file = file_get_contents($in_file);
-
- if (!$file) {
- return false;
- }
-
- foreach ($vars as $k => $v) {
- $file = str_replace("{{".$k."}}", $v, $file);
- }
-
- return $file;
-}
-
-/**
- * Initialisation for installation functions
- *
- */
-function install_init() {
- register_action("systemsettings/install",true);
-}
-
-register_elgg_event_handler("boot","system","install_init"); \ No newline at end of file
diff --git a/engine/lib/languages.php b/engine/lib/languages.php
index 98283f141..61ba91ddb 100644
--- a/engine/lib/languages.php
+++ b/engine/lib/languages.php
@@ -3,13 +3,70 @@
* Elgg language module
* Functions to manage language and translations.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Languages
*/
/**
+ * Given a message key, returns an appropriately translated full-text string
+ *
+ * @param string $message_key The short message code
+ * @param array $args An array of arguments to pass through vsprintf().
+ * @param string $language Optionally, the standard language code
+ * (defaults to site/user default, then English)
+ *
+ * @return string Either the translated string, the English string,
+ * or the original language string.
+ */
+function elgg_echo($message_key, $args = array(), $language = "") {
+ global $CONFIG;
+
+ static $CURRENT_LANGUAGE;
+
+ // old param order is deprecated
+ if (!is_array($args)) {
+ elgg_deprecated_notice(
+ 'As of Elgg 1.8, the 2nd arg to elgg_echo() is an array of string replacements and the 3rd arg is the language.',
+ 1.8
+ );
+
+ $language = $args;
+ $args = array();
+ }
+
+ if (!isset($CONFIG->translations)) {
+ // this means we probably had an exception before translations were initialized
+ register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/");
+ }
+
+ if (!$CURRENT_LANGUAGE) {
+ $CURRENT_LANGUAGE = get_language();
+ }
+ if (!$language) {
+ $language = $CURRENT_LANGUAGE;
+ }
+
+ if (isset($CONFIG->translations[$language][$message_key])) {
+ $string = $CONFIG->translations[$language][$message_key];
+ } else if (isset($CONFIG->translations["en"][$message_key])) {
+ $string = $CONFIG->translations["en"][$message_key];
+ $lang = $CONFIG->translations["en"][$language];
+ elgg_log(sprintf('Missing %s translation for "%s" language key', $lang, $message_key), 'NOTICE');
+ } else {
+ $string = $message_key;
+ elgg_log(sprintf('Missing English translation for "%s" language key', $message_key), 'NOTICE');
+ }
+
+ // only pass through if we have arguments to allow backward compatibility
+ // with manual sprintf() calls.
+ if ($args) {
+ $string = vsprintf($string, $args);
+ }
+
+ return $string;
+}
+
+/**
* Add a translation.
*
* Translations are arrays in the Zend Translation array format, eg:
@@ -17,9 +74,10 @@
* $english = array('message1' => 'message1', 'message2' => 'message2');
* $german = array('message1' => 'Nachricht1','message2' => 'Nachricht2');
*
- * @param string $country_code Standard country code (eg 'en', 'nl', 'es')
- * @param array $language_array Formatted array of strings
- * @return true|false Depending on success
+ * @param string $country_code Standard country code (eg 'en', 'nl', 'es')
+ * @param array $language_array Formatted array of strings
+ *
+ * @return bool Depending on success
*/
function add_translation($country_code, $language_array) {
global $CONFIG;
@@ -46,8 +104,6 @@ function add_translation($country_code, $language_array) {
* @return string The language code for the site/user or "en" if not set
*/
function get_current_language() {
- global $CONFIG;
-
$language = get_language();
if (!$language) {
@@ -65,7 +121,7 @@ function get_current_language() {
function get_language() {
global $CONFIG;
- $user = get_loggedin_user();
+ $user = elgg_get_logged_in_user_entity();
$language = false;
if (($user) && ($user->language)) {
@@ -84,90 +140,137 @@ function get_language() {
}
/**
- * Given a message shortcode, returns an appropriately translated full-text string
- *
- * @param string $message_key The short message code
- * @param string $language Optionally, the standard language code (defaults to site/user default, then English)
- * @return string Either the translated string or the original English string
+ * @access private
*/
-function elgg_echo($message_key, $language = "") {
+function _elgg_load_translations() {
global $CONFIG;
- static $CURRENT_LANGUAGE;
- if (!$CURRENT_LANGUAGE) {
- $CURRENT_LANGUAGE = get_language();
- }
- if (!$language) {
- $language = $CURRENT_LANGUAGE;
- }
+ if ($CONFIG->system_cache_enabled) {
+ $loaded = true;
+ $languages = array_unique(array('en', get_current_language()));
+ foreach ($languages as $language) {
+ $data = elgg_load_system_cache("$language.lang");
+ if ($data) {
+ add_translation($language, unserialize($data));
+ } else {
+ $loaded = false;
+ }
+ }
- if (isset($CONFIG->translations[$language][$message_key])) {
- return $CONFIG->translations[$language][$message_key];
- } else if (isset($CONFIG->translations["en"][$message_key])) {
- return $CONFIG->translations["en"][$message_key];
+ if ($loaded) {
+ $CONFIG->i18n_loaded_from_cache = true;
+ // this is here to force
+ $CONFIG->language_paths[dirname(dirname(dirname(__FILE__))) . "/languages/"] = true;
+ return;
+ }
}
- return $message_key;
+ // load core translations from languages directory
+ register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/");
}
+
+
/**
* When given a full path, finds translation files and loads them
*
- * @param string $path Full path
- * @param bool $load_all If true all languages are loaded, if false only the current language + en are loaded
+ * @param string $path Full path
+ * @param bool $load_all If true all languages are loaded, if
+ * false only the current language + en are loaded
+ *
+ * @return bool success
*/
function register_translations($path, $load_all = false) {
global $CONFIG;
+ $path = sanitise_filepath($path);
+
// Make a note of this path just incase we need to register this language later
- if(!isset($CONFIG->language_paths)) $CONFIG->language_paths = array();
+ if (!isset($CONFIG->language_paths)) {
+ $CONFIG->language_paths = array();
+ }
$CONFIG->language_paths[$path] = true;
// Get the current language based on site defaults and user preference
$current_language = get_current_language();
elgg_log("Translations loaded from: $path");
- if ($handle = opendir($path)) {
- while ($language = readdir($handle)) {
- if (
- ((in_array($language, array('en.php', $current_language . '.php'))) /*&& (!is_dir($path . $language))*/) ||
- (($load_all) && (strpos($language, '.php')!==false)/* && (!is_dir($path . $language))*/)
- ) {
- include_once($path . $language);
+ // only load these files unless $load_all is true.
+ $load_language_files = array(
+ 'en.php',
+ "$current_language.php"
+ );
+
+ $load_language_files = array_unique($load_language_files);
+
+ $handle = opendir($path);
+ if (!$handle) {
+ elgg_log("Could not open language path: $path", 'ERROR');
+ return false;
+ }
+
+ $return = true;
+ while (false !== ($language = readdir($handle))) {
+ // ignore bad files
+ if (substr($language, 0, 1) == '.' || substr($language, -4) !== '.php') {
+ continue;
+ }
+
+ if (in_array($language, $load_language_files) || $load_all) {
+ if (!include_once($path . $language)) {
+ $return = false;
+ continue;
}
}
- } else {
- elgg_log("Missing translation path $path", 'ERROR');
}
+
+ return $return;
}
/**
* Reload all translations from all registered paths.
*
- * This is only called by functions which need to know all possible translations, namely the
- * statistic gathering ones.
+ * This is only called by functions which need to know all possible translations.
*
- * TODO: Better on demand loading based on language_paths array
+ * @todo Better on demand loading based on language_paths array
*
- * @return bool
+ * @return void
*/
function reload_all_translations() {
global $CONFIG;
static $LANG_RELOAD_ALL_RUN;
if ($LANG_RELOAD_ALL_RUN) {
- return null;
+ return;
}
- foreach ($CONFIG->language_paths as $path => $dummy) {
- register_translations($path, true);
+ if ($CONFIG->i18n_loaded_from_cache) {
+ $cache = elgg_get_system_cache();
+ $cache_dir = $cache->getVariable("cache_path");
+ $filenames = elgg_get_file_list($cache_dir, array(), array(), array(".lang"));
+ foreach ($filenames as $filename) {
+ if (preg_match('/([a-z]+)\.[^.]+$/', $filename, $matches)) {
+ $language = $matches[1];
+ $data = elgg_load_system_cache("$language.lang");
+ if ($data) {
+ add_translation($language, unserialize($data));
+ }
+ }
+ }
+ } else {
+ foreach ($CONFIG->language_paths as $path => $dummy) {
+ register_translations($path, true);
+ }
}
$LANG_RELOAD_ALL_RUN = true;
}
/**
- * Return an array of installed translations as an associative array "two letter code" => "native language name".
+ * Return an array of installed translations as an associative
+ * array "two letter code" => "native language name".
+ *
+ * @return array
*/
function get_installed_translations() {
global $CONFIG;
@@ -178,8 +281,8 @@ function get_installed_translations() {
$installed = array();
foreach ($CONFIG->translations as $k => $v) {
- $installed[$k] = elgg_echo($k, $k);
- if (isadminloggedin()) {
+ $installed[$k] = elgg_echo($k, array(), $k);
+ if (elgg_is_admin_logged_in()) {
$completeness = get_language_completeness($k);
if (($completeness < 100) && ($k != 'en')) {
$installed[$k] .= " (" . $completeness . "% " . elgg_echo('complete') . ")";
@@ -192,6 +295,10 @@ function get_installed_translations() {
/**
* Return the level of completeness for a given language code (compared to english)
+ *
+ * @param string $language Language
+ *
+ * @return int
*/
function get_language_completeness($language) {
global $CONFIG;
@@ -217,7 +324,12 @@ function get_language_completeness($language) {
}
/**
- * Return the translation keys missing from a given language, or those that are identical to the english version.
+ * Return the translation keys missing from a given language,
+ * or those that are identical to the english version.
+ *
+ * @param string $language The language
+ *
+ * @return mixed
*/
function get_missing_language_keys($language) {
global $CONFIG;
@@ -240,5 +352,3 @@ function get_missing_language_keys($language) {
return false;
}
-
-register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/");
diff --git a/engine/lib/location.php b/engine/lib/location.php
index 21ee7d5fa..1534c7d7b 100644
--- a/engine/lib/location.php
+++ b/engine/lib/location.php
@@ -2,67 +2,31 @@
/**
* Elgg geo-location tagging library.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Location
*/
/**
- * Define an interface for geo-tagging entities.
- *
- */
-interface Locatable {
- /** Set a location text */
- public function setLocation($location);
-
- /**
- * Set latitude and longitude tags for a given entity.
- *
- * @param float $lat
- * @param float $long
- */
- public function setLatLong($lat, $long);
-
- /**
- * Get the contents of the ->geo:lat field.
- *
- */
- public function getLatitude();
-
- /**
- * Get the contents of the ->geo:lat field.
- *
- */
- public function getLongitude();
-
- /**
- * Get the ->location metadata.
- *
- */
- public function getLocation();
-}
-
-/**
* Encode a location into a latitude and longitude, caching the result.
*
- * Works by triggering the 'geocode' 'location' plugin hook, and requires a geocoding module to be installed
- * activated in order to work.
+ * Works by triggering the 'geocode' 'location' plugin
+ * hook, and requires a geocoding plugin to be installed.
*
- * @param String $location The location, e.g. "London", or "24 Foobar Street, Gotham City"
+ * @param string $location The location, e.g. "London", or "24 Foobar Street, Gotham City"
+ * @return string|false
*/
function elgg_geocode_location($location) {
global $CONFIG;
- // Handle cases where we are passed an array (shouldn't be but can happen if location is a tag field)
if (is_array($location)) {
- $location = implode(', ', $location);
+ return false;
}
$location = sanitise_string($location);
// Look for cached version
- $cached_location = get_data_row("SELECT * from {$CONFIG->dbprefix}geocode_cache WHERE location='$location'");
+ $query = "SELECT * from {$CONFIG->dbprefix}geocode_cache WHERE location='$location'";
+ $cached_location = get_data_row($query);
if ($cached_location) {
return array('lat' => $cached_location->lat, 'long' => $cached_location->long);
@@ -70,7 +34,7 @@ function elgg_geocode_location($location) {
// Trigger geocode event if not cached
$return = false;
- $return = trigger_plugin_hook('geocode', 'location', array('location' => $location), $return);
+ $return = elgg_trigger_plugin_hook('geocode', 'location', array('location' => $location), $return);
// If returned, cache and return value
if (($return) && (is_array($return))) {
@@ -78,7 +42,10 @@ function elgg_geocode_location($location) {
$long = (float)$return['long'];
// Put into cache at the end of the page since we don't really care that much
- execute_delayed_write_query("INSERT DELAYED INTO {$CONFIG->dbprefix}geocode_cache (location, lat, `long`) VALUES ('$location', '{$lat}', '{$long}') ON DUPLICATE KEY UPDATE lat='{$lat}', `long`='{$long}'");
+ $query = "INSERT DELAYED INTO {$CONFIG->dbprefix}geocode_cache "
+ . " (location, lat, `long`) VALUES ('$location', '{$lat}', '{$long}')"
+ . " ON DUPLICATE KEY UPDATE lat='{$lat}', `long`='{$long}'";
+ execute_delayed_write_query($query);
}
return $return;
@@ -87,196 +54,104 @@ function elgg_geocode_location($location) {
/**
* Return entities within a given geographic area.
*
- * @param real $lat Latitude
- * @param real $long Longitude
- * @param real $radius The radius
- * @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 rather than the entities themselves (limits and offsets don't apply in this context). Defaults to false.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @param int|array $container_guid The container or containers to get entities from (default: all containers).
- * @return array A list of entities.
+ * Also accepts all options available to elgg_get_entities().
+ *
+ * @see elgg_get_entities
+ *
+ * @param array $options Array in format:
+ *
+ * latitude => FLOAT Latitude of the location
+ *
+ * longitude => FLOAT Longitude of the location
+ *
+ * distance => FLOAT/ARR (
+ * latitude => float,
+ * longitude => float,
+ * )
+ * The distance in degrees that determines the search box. A
+ * single float will result in a square in degrees.
+ * @warning The Earth is round.
+ *
+ * @see ElggEntity::setLatLong()
+ *
+ * @return mixed If count, int. If not count, array. false on errors.
+ * @since 1.8.0
*/
-function get_entities_in_area($lat, $long, $radius, $type = "", $subtype = "", $owner_guid = 0, $order_by = "", $limit = 10, $offset = 0, $count = false, $site_guid = 0, $container_guid) {
- global $CONFIG;
+function elgg_get_entities_from_location(array $options = array()) {
- if ($subtype === false || $subtype === null || $subtype === 0) {
+ global $CONFIG;
+
+ if (!isset($options['latitude']) || !isset($options['longitude']) ||
+ !isset($options['distance'])) {
return false;
}
- $lat = (real)$lat;
- $long = (real)$long;
- $radius = (real)$radius;
-
- $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})";
- }
+ if (!is_array($options['distance'])) {
+ $lat_distance = (float)$options['distance'];
+ $long_distance = (float)$options['distance'];
} 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;
- }
+ $lat_distance = (float)$options['distance']['latitude'];
+ $long_distance = (float)$options['distance']['longitude'];
}
- if ($site_guid > 0) {
- $where[] = "e.site_guid = {$site_guid}";
+ $lat = (float)$options['latitude'];
+ $long = (float)$options['longitude'];
+ $lat_min = $lat - $lat_distance;
+ $lat_max = $lat + $lat_distance;
+ $long_min = $long - $long_distance;
+ $long_max = $long + $long_distance;
+
+ $wheres = array();
+ $wheres[] = "lat_name.string='geo:lat'";
+ $wheres[] = "lat_value.string >= $lat_min";
+ $wheres[] = "lat_value.string <= $lat_max";
+ $wheres[] = "lon_name.string='geo:long'";
+ $wheres[] = "lon_value.string >= $long_min";
+ $wheres[] = "lon_value.string <= $long_max";
+
+ $joins = array();
+ $joins[] = "JOIN {$CONFIG->dbprefix}metadata lat on e.guid=lat.entity_guid";
+ $joins[] = "JOIN {$CONFIG->dbprefix}metastrings lat_name on lat.name_id=lat_name.id";
+ $joins[] = "JOIN {$CONFIG->dbprefix}metastrings lat_value on lat.value_id=lat_value.id";
+ $joins[] = "JOIN {$CONFIG->dbprefix}metadata lon on e.guid=lon.entity_guid";
+ $joins[] = "JOIN {$CONFIG->dbprefix}metastrings lon_name on lon.name_id=lon_name.id";
+ $joins[] = "JOIN {$CONFIG->dbprefix}metastrings lon_value on lon.value_id=lon_value.id";
+
+ // merge wheres to pass to get_entities()
+ if (isset($options['wheres']) && !is_array($options['wheres'])) {
+ $options['wheres'] = array($options['wheres']);
+ } elseif (!isset($options['wheres'])) {
+ $options['wheres'] = array();
}
+ $options['wheres'] = array_merge($options['wheres'], $wheres);
- 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
- $loc_join = "
- JOIN {$CONFIG->dbprefix}metadata loc_start on e.guid=loc_start.entity_guid
- JOIN {$CONFIG->dbprefix}metastrings loc_start_name on loc_start.name_id=loc_start_name.id
- JOIN {$CONFIG->dbprefix}metastrings loc_start_value on loc_start.value_id=loc_start_value.id
-
- JOIN {$CONFIG->dbprefix}metadata loc_end on e.guid=loc_end.entity_guid
- JOIN {$CONFIG->dbprefix}metastrings loc_end_name on loc_end.name_id=loc_end_name.id
- JOIN {$CONFIG->dbprefix}metastrings loc_end_value on loc_end.value_id=loc_end_value.id
- ";
-
- $lat_min = $lat - $radius;
- $lat_max = $lat + $radius;
- $long_min = $long - $radius;
- $long_max = $long + $radius;
-
- $where[] = "loc_start_name.string='geo:lat'";
- $where[] = "loc_start_value.string>=$lat_min";
- $where[] = "loc_start_value.string<=$lat_max";
- $where[] = "loc_end_name.string='geo:long'";
- $where[] = "loc_end_value.string >= $long_min";
- $where[] = "loc_end_value.string <= $long_max";
-
- if (!$count) {
- $query = "SELECT e.* from {$CONFIG->dbprefix}entities e $loc_join where ";
- } else {
- $query = "SELECT count(e.guid) as total from {$CONFIG->dbprefix}entities e $loc_join where ";
- }
- foreach ($where as $w) {
- $query .= " $w and ";
+ // merge joins to pass to get_entities()
+ if (isset($options['joins']) && !is_array($options['joins'])) {
+ $options['joins'] = array($options['joins']);
+ } elseif (!isset($options['joins'])) {
+ $options['joins'] = array();
}
+ $options['joins'] = array_merge($options['joins'], $joins);
- $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 elgg_get_entities_from_relationship($options);
}
/**
- * List entities in a given location
+ * Returns a viewable list of entities from location
*
- * @param string $location Location
- * @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 display per page (default: 10)
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow gallery view
- * @param true|false $pagination Display pagination? Default: true
- * @return string A viewable list of entities
- */
-function list_entities_location($location, $type= "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = false, $navigation = true) {
- return list_entities_from_metadata('location', $location, $type, $subtype, $owner_guid, $limit, $fullview, $viewtypetoggle, $navigation);
-}
-
-/**
- * List items within a given geographic area.
+ * @param array $options Options array
*
- * @param real $lat Latitude
- * @param real $long Longitude
- * @param real $radius The radius
- * @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 display per page (default: 10)
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow gallery view
- * @param true|false $pagination Display pagination? Default: true
- * @return string A viewable list of entities
+ * @see elgg_list_entities()
+ * @see elgg_get_entities_from_location()
+ *
+ * @return string The viewable list of entities
+ * @since 1.8.0
*/
-function list_entities_in_area($lat, $long, $radius, $type= "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = false, $navigation = true) {
-
- $offset = (int) get_input('offset');
- $count = get_entities_in_area($lat, $long, $radius, $type, $subtype, $owner_guid, "", $limit, $offset, true);
- $entities = get_entities_in_area($lat, $long, $radius, $type, $subtype, $owner_guid, "", $limit, $offset);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $navigation);
+function elgg_list_entities_from_location(array $options = array()) {
+ return elgg_list_entities($options, 'elgg_get_entities_from_location');
}
// Some distances in degrees (approximate)
+// @todo huh? see warning on elgg_get_entities_from_location()
define("MILE", 0.01515);
define("KILOMETER", 0.00932);
-
-// TODO: get objects within x miles by entities, metadata and relationship
-
-// TODO: List \ No newline at end of file
diff --git a/engine/lib/mb_wrapper.php b/engine/lib/mb_wrapper.php
index 9aa4aac4c..68fa69005 100644
--- a/engine/lib/mb_wrapper.php
+++ b/engine/lib/mb_wrapper.php
@@ -11,8 +11,10 @@ if (is_callable('mb_internal_encoding')) {
* NOTE: This differs from parse_str() by returning the results
* instead of placing them in the local scope!
*
- * @param str $str
+ * @param string $str The string
+ *
* @return array
+ * @since 1.7.0
*/
function elgg_parse_str($str) {
if (is_callable('mb_parse_str')) {
@@ -24,44 +26,208 @@ function elgg_parse_str($str) {
return $results;
}
-// map string functions to their mb_str_func alternatives
-// and wrap them in elgg_str_fun()
-
-// list of non-mb safe string functions to wrap in elgg_*()
-// only will work with mb_* functions that take the same
-// params in the same order as their non-mb safe counterparts.
-$str_funcs = array(
- // can't wrap parse_str() because of its 2nd parameter.
- //'parse_str',
- 'split',
- 'stristr',
- 'strlen',
- 'strpos',
- 'strrchr',
- 'strripos',
- 'strrpos',
- 'strstr',
- 'strtolower',
- 'strtoupper',
- 'substr_count',
- 'substr'
-);
-
-$eval_statement = '';
-foreach ($str_funcs as $func) {
- // create wrapper function passing in the same args as given
- $mb_func = "mb_$func";
- $eval_statement .= "
- function elgg_$func() {
- \$args = func_get_args();
- if (is_callable('$mb_func')) {
- return call_user_func_array('$mb_func', \$args);
- }
- return call_user_func_array('$func', \$args);
+
+
+/**
+ * Wrapper function for mb_split(). Falls back to split() if
+ * mb_split() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return string
+ * @since 1.7.0
+ */
+function elgg_split() {
+ $args = func_get_args();
+ if (is_callable('mb_split')) {
+ return call_user_func_array('mb_split', $args);
+ }
+ return call_user_func_array('split', $args);
+}
+
+/**
+ * Wrapper function for mb_stristr(). Falls back to stristr() if
+ * mb_stristr() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return string
+ * @since 1.7.0
+ */
+function elgg_stristr() {
+ $args = func_get_args();
+ if (is_callable('mb_stristr')) {
+ return call_user_func_array('mb_stristr', $args);
+ }
+ return call_user_func_array('stristr', $args);
+}
+
+/**
+ * Wrapper function for mb_strlen(). Falls back to strlen() if
+ * mb_strlen() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return string
+ * @since 1.7.0
+ */
+function elgg_strlen() {
+ $args = func_get_args();
+ if (is_callable('mb_strlen')) {
+ return call_user_func_array('mb_strlen', $args);
+ }
+ return call_user_func_array('strlen', $args);
+}
+
+/**
+ * Wrapper function for mb_strpos(). Falls back to strpos() if
+ * mb_strpos() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return string
+ * @since 1.7.0
+ */
+function elgg_strpos() {
+ $args = func_get_args();
+ if (is_callable('mb_strpos')) {
+ return call_user_func_array('mb_strpos', $args);
+ }
+ return call_user_func_array('strpos', $args);
+}
+
+/**
+ * Wrapper function for mb_strrchr(). Falls back to strrchr() if
+ * mb_strrchr() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return string
+ * @since 1.7.0
+ */
+function elgg_strrchr() {
+ $args = func_get_args();
+ if (is_callable('mb_strrchr')) {
+ return call_user_func_array('mb_strrchr', $args);
+ }
+ return call_user_func_array('strrchr', $args);
+}
+
+/**
+ * Wrapper function for mb_strripos(). Falls back to strripos() if
+ * mb_strripos() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return int
+ * @since 1.7.0
+ */
+function elgg_strripos() {
+ $args = func_get_args();
+ if (is_callable('mb_strripos')) {
+ return call_user_func_array('mb_strripos', $args);
+ }
+ return call_user_func_array('strripos', $args);
+}
+
+/**
+ * Wrapper function for mb_strrpos(). Falls back to strrpos() if
+ * mb_strrpos() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return int
+ * @since 1.7.0
+ */
+function elgg_strrpos() {
+ $args = func_get_args();
+ if (is_callable('mb_strrpos')) {
+ return call_user_func_array('mb_strrpos', $args);
+ }
+ return call_user_func_array('strrpos', $args);
+}
+
+/**
+ * Wrapper function for mb_strstr(). Falls back to strstr() if
+ * mb_strstr() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return bool
+ * @since 1.7.0
+ */
+function elgg_strstr() {
+ $args = func_get_args();
+ if (is_callable('mb_strstr')) {
+ return call_user_func_array('mb_strstr', $args);
}
-";
+ return call_user_func_array('strstr', $args);
}
-eval($eval_statement);
+/**
+ * Wrapper function for mb_strtolower(). Falls back to strtolower() if
+ * mb_strtolower() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return string
+ * @since 1.7.0
+ */
+function elgg_strtolower() {
+ $args = func_get_args();
+ if (is_callable('mb_strtolower')) {
+ return call_user_func_array('mb_strtolower', $args);
+ }
+ return call_user_func_array('strtolower', $args);
+}
-// TODO: Other wrapper functions \ No newline at end of file
+/**
+ * Wrapper function for mb_strtoupper(). Falls back to strtoupper() if
+ * mb_strtoupper() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return string
+ * @since 1.7.0
+ */
+function elgg_strtoupper() {
+ $args = func_get_args();
+ if (is_callable('mb_strtoupper')) {
+ return call_user_func_array('mb_strtoupper', $args);
+ }
+ return call_user_func_array('strtoupper', $args);
+}
+
+/**
+ * Wrapper function for mb_substr_count(). Falls back to substr_count() if
+ * mb_substr_count() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return int
+ * @since 1.7.0
+ */
+function elgg_substr_count() {
+ $args = func_get_args();
+ if (is_callable('mb_substr_count')) {
+ return call_user_func_array('mb_substr_count', $args);
+ }
+ return call_user_func_array('substr_count', $args);
+}
+
+/**
+ * Wrapper function for mb_substr(). Falls back to substr() if
+ * mb_substr() isn't available. Parameters are passed to the
+ * wrapped function in the same order they are passed to this
+ * function.
+ *
+ * @return string
+ * @since 1.7.0
+ */
+function elgg_substr() {
+ $args = func_get_args();
+ if (is_callable('mb_substr')) {
+ return call_user_func_array('mb_substr', $args);
+ }
+ return call_user_func_array('substr', $args);
+}
diff --git a/engine/lib/memcache.php b/engine/lib/memcache.php
index ed93cacf0..79b87e850 100644
--- a/engine/lib/memcache.php
+++ b/engine/lib/memcache.php
@@ -4,164 +4,11 @@
*
* Requires php5-memcache to work.
*
- * @package Elgg
- * @subpackage API
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Cache.Memcache
*/
/**
- * Memcache wrapper class.
- * @author Curverider Ltd <info@elgg.com>
- */
-class ElggMemcache extends ElggSharedMemoryCache {
- /**
- * Minimum version of memcached needed to run
- *
- */
- private static $MINSERVERVERSION = '1.1.12';
-
- /**
- * Memcache object
- */
- private $memcache;
-
- /**
- * Expiry of saved items (default timeout after a day to prevent anything getting too stale)
- */
- private $expires = 86400;
-
- /**
- * The version of memcache running
- */
- private $version = 0;
-
- /**
- * Connect to memcache.
- *
- * @param string $cache_id The namespace for this cache to write to - note, namespaces of the same name are shared!
- */
- function __construct($namespace = 'default') {
- global $CONFIG;
-
- $this->setNamespace($namespace);
-
- // Do we have memcache?
- if (!class_exists('Memcache')) {
- throw new ConfigurationException(elgg_echo('memcache:notinstalled'));
- }
-
- // Create memcache object
- $this->memcache = new Memcache;
-
- // Now add servers
- if (!$CONFIG->memcache_servers) {
- throw new ConfigurationException(elgg_echo('memcache:noservers'));
- }
-
- if (is_callable($this->memcache, 'addServer')) {
- foreach ($CONFIG->memcache_servers as $server) {
- if (is_array($server)) {
- $this->memcache->addServer(
- $server[0],
- isset($server[1]) ? $server[1] : 11211,
- isset($server[2]) ? $server[2] : true,
- isset($server[3]) ? $server[3] : null,
- isset($server[4]) ? $server[4] : 1,
- isset($server[5]) ? $server[5] : 15,
- isset($server[6]) ? $server[6] : true
- );
-
- } else {
- $this->memcache->addServer($server, 11211);
- }
- }
- } else {
- elgg_log(elgg_echo('memcache:noaddserver'), 'ERROR');
-
- $server = $CONFIG->memcache_servers[0];
- if (is_array($server)) {
- $this->memcache->connect($server[0], $server[1]);
- } else {
- $this->memcache->addServer($server, 11211);
- }
- }
-
- // Get version
- $this->version = $this->memcache->getversion();
- if (version_compare($this->version, ElggMemcache::$MINSERVERVERSION, '<')) {
- throw new ConfigurationException(sprintf(elgg_echo('memcache:versiontoolow'), ElggMemcache::$MINSERVERVERSION, $this->version));
- }
-
- // Set some defaults
- if (isset($CONFIG->memcache_expires)) {
- $this->expires = $CONFIG->memcache_expires;
- }
- }
-
- /**
- * Set the default expiry.
- *
- * @param int $expires The lifetime as a unix timestamp or time from now. Defaults forever.
- */
- public function setDefaultExpiry($expires = 0) {
- $this->expires = $expires;
- }
-
- /**
- * Combine a key with the namespace.
- * Memcache can only accept <250 char key. If the given key is too long it is shortened.
- *
- * @param string $key The key
- * @return string The new key.
- */
- private function make_memcache_key($key) {
- $prefix = $this->getNamespace() . ":";
-
- if (strlen($prefix.$key)> 250) {
- $key = md5($key);
- }
-
- return $prefix.$key;
- }
-
- public function save($key, $data) {
- $key = $this->make_memcache_key($key);
-
- $result = $this->memcache->set($key, $data, null, $this->expires);
- if (!$result) {
- elgg_log("MEMCACHE: FAILED TO SAVE $key", 'ERROR');
- }
-
- return $result;
- }
-
- public function load($key, $offset = 0, $limit = null) {
- $key = $this->make_memcache_key($key);
-
- $result = $this->memcache->get($key);
- if (!$result) {
- elgg_log("MEMCACHE: FAILED TO LOAD $key", 'ERROR');
- }
-
- return $result;
- }
-
- public function delete($key) {
- $key = $this->make_memcache_key($key);
-
- return $this->memcache->delete($key, 0);
- }
-
- public function clear() {
- // DISABLE clearing for now - you must use delete on a specific key.
- return true;
-
- //TODO: Namespaces as in #532
- }
-}
-
-/**
* Return true if memcache is available and configured.
*
* @return bool
@@ -176,7 +23,7 @@ function is_memcache_available() {
}
// If we haven't set variable to something
- if (($memcache_available!==true) && ($memcache_available!==false)) {
+ if (($memcache_available !== true) && ($memcache_available !== false)) {
try {
$tmp = new ElggMemcache();
// No exception thrown so we have memcache available
@@ -187,4 +34,24 @@ function is_memcache_available() {
}
return $memcache_available;
+}
+
+/**
+ * Invalidate an entity in memcache
+ *
+ * @param int $entity_guid The GUID of the entity to invalidate
+ *
+ * @return void
+ * @access private
+ */
+function _elgg_invalidate_memcache_for_entity($entity_guid) {
+ static $newentity_cache;
+
+ if ((!$newentity_cache) && (is_memcache_available())) {
+ $newentity_cache = new ElggMemcache('new_entity_cache');
+ }
+
+ if ($newentity_cache) {
+ $newentity_cache->delete($entity_guid);
+ }
} \ No newline at end of file
diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php
index d28999a77..fdb1b85f6 100644
--- a/engine/lib/metadata.php
+++ b/engine/lib/metadata.php
@@ -1,132 +1,19 @@
<?php
/**
* Elgg metadata
- * Functions to manage object metadata.
+ * Functions to manage entity metadata.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage DataModel.Metadata
*/
/**
- * ElggMetadata
- * This class describes metadata that can be attached to ElggEntities.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Core
- */
-class ElggMetadata extends ElggExtender {
- /**
- * Construct a new site object, optionally from a given id value or row.
- *
- * @param mixed $id
- */
- function __construct($id = null) {
- $this->attributes = array();
-
- if (!empty($id)) {
- // Create from db row
- if ($id instanceof stdClass) {
- $metadata = $id;
- } else {
- $metadata = get_metadata($id);
- }
-
- if ($metadata) {
- $objarray = (array) $metadata;
- foreach($objarray as $key => $value) {
- $this->attributes[$key] = $value;
- }
- $this->attributes['type'] = "metadata";
- }
- }
- }
-
- /**
- * Class member get overloading
- *
- * @param string $name
- * @return mixed
- */
- function __get($name) {
- return $this->get($name);
- }
-
- /**
- * Class member set overloading
- *
- * @param string $name
- * @param mixed $value
- * @return mixed
- */
- function __set($name, $value) {
- return $this->set($name, $value);
- }
-
- /**
- * Determines whether or not the user can edit this piece of metadata
- *
- * @return true|false Depending on permissions
- */
- function canEdit() {
- if ($entity = get_entity($this->get('entity_guid'))) {
- return $entity->canEditMetadata($this);
- }
- return false;
- }
-
- /**
- * Save matadata object
- *
- * @return int the metadata object id
- */
- function save() {
- if ($this->id > 0) {
- return update_metadata($this->id, $this->name, $this->value, $this->value_type, $this->owner_guid, $this->access_id);
- } else {
- $this->id = create_metadata($this->entity_guid, $this->name, $this->value, $this->value_type, $this->owner_guid, $this->access_id);
- if (!$this->id) {
- throw new IOException(sprintf(elgg_new('IOException:UnableToSaveNew'), get_class()));
- }
- return $this->id;
- }
- }
-
- /**
- * Delete a given metadata.
- */
- function delete() {
- return delete_metadata($this->id);
- }
-
- /**
- * Get a url for this item of metadata.
- *
- * @return string
- */
- public function getURL() {
- return get_metadata_url($this->id);
- }
-
- // SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * For a given ID, return the object associated with it.
- * This is used by the river functionality primarily.
- * This is useful for checking access permissions etc on objects.
- */
- public function getObjectFromID($id) {
- return get_metadata($id);
- }
-}
-
-/**
* Convert a database row to a new ElggMetadata
*
- * @param stdClass $row
- * @return stdClass or ElggMetadata
+ * @param stdClass $row An object from the database
+ *
+ * @return stdClass|ElggMetadata
+ * @access private
*/
function row_to_elggmetadata($row) {
if (!($row instanceof stdClass)) {
@@ -137,64 +24,55 @@ function row_to_elggmetadata($row) {
}
/**
- * Get a specific item of metadata.
+ * Get a specific metadata object by its id.
+ * If you want multiple metadata objects, use
+ * {@link elgg_get_metadata()}.
*
- * @param $id int The item of metadata being retrieved.
+ * @param int $id The id of the metadata object being retrieved.
+ *
+ * @return ElggMetadata|false FALSE if not found
*/
-function get_metadata($id) {
- global $CONFIG;
-
- $id = (int)$id;
- $access = get_access_sql_suffix("e");
- $md_access = get_access_sql_suffix("m");
-
- return row_to_elggmetadata(get_data_row("SELECT m.*, n.string as name, v.string as value from {$CONFIG->dbprefix}metadata m JOIN {$CONFIG->dbprefix}entities e on e.guid = m.entity_guid JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id where m.id=$id and $access and $md_access"));
+function elgg_get_metadata_from_id($id) {
+ return elgg_get_metastring_based_object_from_id($id, 'metadata');
}
/**
- * Removes metadata on an entity with a particular name, optionally with a given value.
+ * Deletes metadata using its ID.
*
- * @param int $entity_guid The entity GUID
- * @param string $name The name of the metadata
- * @param string $value The optional value of the item (useful for removing a single item in a multiple set)
- * @return true|false Depending on success
+ * @param int $id The metadata ID to delete.
+ * @return bool
*/
-function remove_metadata($entity_guid, $name, $value = "") {
- global $CONFIG;
- $entity_guid = (int) $entity_guid;
- $name = sanitise_string($name);
- $value = sanitise_string($value);
-
- $query = "SELECT * from {$CONFIG->dbprefix}metadata WHERE entity_guid = $entity_guid and name_id=" . add_metastring($name);
- if ($value!="") {
- $query .= " and value_id=" . add_metastring($value);
- }
-
- if ($existing = get_data($query)) {
- foreach($existing as $ex) {
- delete_metadata($ex->id);
- }
- return true;
+function elgg_delete_metadata_by_id($id) {
+ $metadata = elgg_get_metadata_from_id($id);
+ if (!$metadata) {
+ return false;
}
-
- return false;
+ return $metadata->delete();
}
/**
* Create a new metadata object, or update an existing one.
*
- * @param int $entity_guid The entity to attach the metadata to
- * @param string $name Name of the metadata
- * @param string $value Value of the metadata (cannot be associative array)
- * @param string $value_type
- * @param int $owner_guid
- * @param int $access_id
- * @param bool $allow_multiple
+ * Metadata can be an array by setting allow_multiple to TRUE, but it is an
+ * indexed array with no control over the indexing.
+ *
+ * @param int $entity_guid The entity to attach the metadata to
+ * @param string $name Name of the metadata
+ * @param string $value Value of the metadata
+ * @param string $value_type 'text', 'integer', or '' for automatic detection
+ * @param int $owner_guid GUID of entity that owns the metadata
+ * @param int $access_id Default is ACCESS_PRIVATE
+ * @param bool $allow_multiple Allow multiple values for one key. Default is FALSE
+ *
+ * @return int|false id of metadata or FALSE if failure
*/
-function create_metadata($entity_guid, $name, $value, $value_type, $owner_guid, $access_id = ACCESS_PRIVATE, $allow_multiple = false) {
+function create_metadata($entity_guid, $name, $value, $value_type = '', $owner_guid = 0,
+ $access_id = ACCESS_PRIVATE, $allow_multiple = false) {
+
global $CONFIG;
$entity_guid = (int)$entity_guid;
+ // name and value are encoded in add_metastring()
//$name = sanitise_string(trim($name));
//$value = sanitise_string(trim($value));
$value_type = detect_extender_valuetype($value, sanitise_string(trim($value_type)));
@@ -202,81 +80,85 @@ function create_metadata($entity_guid, $name, $value, $value_type, $owner_guid,
$owner_guid = (int)$owner_guid;
$allow_multiple = (boolean)$allow_multiple;
- if ($owner_guid==0) {
- $owner_guid = get_loggedin_userid();
+ if (!isset($value)) {
+ return FALSE;
+ }
+
+ if ($owner_guid == 0) {
+ $owner_guid = elgg_get_logged_in_user_guid();
}
$access_id = (int)$access_id;
- $id = false;
+ $query = "SELECT * from {$CONFIG->dbprefix}metadata"
+ . " WHERE entity_guid = $entity_guid and name_id=" . add_metastring($name) . " limit 1";
- $existing = get_data_row("SELECT * from {$CONFIG->dbprefix}metadata WHERE entity_guid = $entity_guid and name_id=" . add_metastring($name) . " limit 1");
- if (($existing) && (!$allow_multiple) && (isset($value))) {
- $id = $existing->id;
+ $existing = get_data_row($query);
+ if ($existing && !$allow_multiple) {
+ $id = (int)$existing->id;
$result = update_metadata($id, $name, $value, $value_type, $owner_guid, $access_id);
if (!$result) {
return false;
}
- }
- else if (isset($value)) {
+ } else {
// Support boolean types
if (is_bool($value)) {
- if ($value) {
- $value = 1;
- } else {
- $value = 0;
- }
+ $value = (int) $value;
}
// Add the metastrings
- $value = add_metastring($value);
- if (!$value) {
+ $value_id = add_metastring($value);
+ if (!$value_id) {
return false;
}
- $name = add_metastring($name);
- if (!$name) {
+ $name_id = add_metastring($name);
+ if (!$name_id) {
return false;
}
// If ok then add it
- $id = insert_data("INSERT into {$CONFIG->dbprefix}metadata (entity_guid, name_id, value_id, value_type, owner_guid, time_created, access_id) VALUES ($entity_guid, '$name','$value','$value_type', $owner_guid, $time, $access_id)");
+ $query = "INSERT into {$CONFIG->dbprefix}metadata"
+ . " (entity_guid, name_id, value_id, value_type, owner_guid, time_created, access_id)"
+ . " VALUES ($entity_guid, '$name_id','$value_id','$value_type', $owner_guid, $time, $access_id)";
+
+ $id = insert_data($query);
+
+ if ($id !== false) {
+ $obj = elgg_get_metadata_from_id($id);
+ if (elgg_trigger_event('create', 'metadata', $obj)) {
+
+ elgg_get_metadata_cache()->save($entity_guid, $name, $value, $allow_multiple);
- if ($id!==false) {
- $obj = get_metadata($id);
- if (trigger_elgg_event('create', 'metadata', $obj)) {
- return true;
+ return $id;
} else {
- delete_metadata($id);
+ elgg_delete_metadata_by_id($id);
}
}
-
- } else if ($existing) {
- // TODO: Check... are you sure you meant to do this Ben? :)
- $id = $existing->id;
- delete_metadata($id);
}
return $id;
}
/**
- * Update an item of metadata.
- *
- * @param int $id
- * @param string $name
- * @param string $value
- * @param string $value_type
- * @param int $owner_guid
- * @param int $access_id
+ * Update a specific piece of metadata.
+ *
+ * @param int $id ID of the metadata to update
+ * @param string $name Metadata name
+ * @param string $value Metadata value
+ * @param string $value_type Value type
+ * @param int $owner_guid Owner guid
+ * @param int $access_id Access ID
+ *
+ * @return bool
*/
function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_id) {
global $CONFIG;
$id = (int)$id;
- if (!$md = get_metadata($id)) {
+ if (!$md = elgg_get_metadata_from_id($id)) {
return false;
}
if (!$md->canEdit()) {
@@ -290,51 +172,50 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i
}
if ($metabyname_memcache) {
+ // @todo fix memcache (name_id is not a property of ElggMetadata)
$metabyname_memcache->delete("{$md->entity_guid}:{$md->name_id}");
}
- //$name = sanitise_string(trim($name));
- //$value = sanitise_string(trim($value));
$value_type = detect_extender_valuetype($value, sanitise_string(trim($value_type)));
$owner_guid = (int)$owner_guid;
- if ($owner_guid==0) {
- $owner_guid = get_loggedin_userid();
+ if ($owner_guid == 0) {
+ $owner_guid = elgg_get_logged_in_user_guid();
}
$access_id = (int)$access_id;
- $access = get_access_sql_suffix();
-
// Support boolean types (as integers)
if (is_bool($value)) {
- if ($value) {
- $value = 1;
- } else {
- $value = 0;
- }
+ $value = (int) $value;
}
// Add the metastring
- $value = add_metastring($value);
- if (!$value) {
+ $value_id = add_metastring($value);
+ if (!$value_id) {
return false;
}
- $name = add_metastring($name);
- if (!$name) {
+ $name_id = add_metastring($name);
+ if (!$name_id) {
return false;
}
// If ok then add it
- $result = update_data("UPDATE {$CONFIG->dbprefix}metadata set value_id='$value', value_type='$value_type', access_id=$access_id, owner_guid=$owner_guid where id=$id and name_id='$name'");
- if ($result!==false) {
- $obj = get_metadata($id);
- if (trigger_elgg_event('update', 'metadata', $obj)) {
- return true;
- } else {
- delete_metadata($id);
- }
+ $query = "UPDATE {$CONFIG->dbprefix}metadata"
+ . " set name_id='$name_id', value_id='$value_id', value_type='$value_type', access_id=$access_id,"
+ . " owner_guid=$owner_guid where id=$id";
+
+ $result = update_data($query);
+ if ($result !== false) {
+
+ elgg_get_metadata_cache()->save($md->entity_guid, $name, $value);
+
+ // @todo this event tells you the metadata has been updated, but does not
+ // let you do anything about it. What is needed is a plugin hook before
+ // the update that passes old and new values.
+ $obj = elgg_get_metadata_from_id($id);
+ elgg_trigger_event('update', 'metadata', $obj);
}
return $result;
@@ -343,16 +224,26 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i
/**
* This function creates metadata from an associative array of "key => value" pairs.
*
- * @param int $entity_guid
- * @param string $name_and_values
- * @param string $value_type
- * @param int $owner_guid
- * @param int $access_id
- * @param bool $allow_multiple
+ * To achieve an array for a single key, pass in the same key multiple times with
+ * allow_multiple set to TRUE. This creates an indexed array. It does not support
+ * associative arrays and there is no guarantee on the ordering in the array.
+ *
+ * @param int $entity_guid The entity to attach the metadata to
+ * @param array $name_and_values Associative array - a value can be a string, number, bool
+ * @param string $value_type 'text', 'integer', or '' for automatic detection
+ * @param int $owner_guid GUID of entity that owns the metadata
+ * @param int $access_id Default is ACCESS_PRIVATE
+ * @param bool $allow_multiple Allow multiple values for one key. Default is FALSE
+ *
+ * @return bool
*/
-function create_metadata_from_array($entity_guid, array $name_and_values, $value_type, $owner_guid, $access_id = ACCESS_PRIVATE, $allow_multiple = false) {
+function create_metadata_from_array($entity_guid, array $name_and_values, $value_type, $owner_guid,
+$access_id = ACCESS_PRIVATE, $allow_multiple = false) {
+
foreach ($name_and_values as $k => $v) {
- if (!create_metadata($entity_guid, $k, $v, $value_type, $owner_guid, $access_id, $allow_multiple)) {
+ $result = create_metadata($entity_guid, $k, $v, $value_type, $owner_guid,
+ $access_id, $allow_multiple);
+ if (!$result) {
return false;
}
}
@@ -360,228 +251,202 @@ function create_metadata_from_array($entity_guid, array $name_and_values, $value
}
/**
- * Delete an item of metadata, where the current user has access.
+ * Returns metadata. Accepts all elgg_get_entities() options for entity
+ * restraints.
+ *
+ * @see elgg_get_entities
+ *
+ * @warning 1.7's find_metadata() didn't support limits and returned all metadata.
+ * This function defaults to a limit of 25. There is probably not a reason
+ * for you to return all metadata unless you're exporting an entity,
+ * have other restraints in place, or are doing something horribly
+ * wrong in your code.
*
- * @param $id int The item of metadata to delete.
+ * @param array $options Array in format:
+ *
+ * metadata_names => NULL|ARR metadata names
+ * metadata_values => NULL|ARR metadata values
+ * metadata_ids => NULL|ARR metadata ids
+ * metadata_case_sensitive => BOOL Overall Case sensitive
+ * metadata_owner_guids => NULL|ARR guids for metadata owners
+ * metadata_created_time_lower => INT Lower limit for created time.
+ * metadata_created_time_upper => INT Upper limit for created time.
+ * metadata_calculation => STR Perform the MySQL function on the metadata values returned.
+ * The "metadata_calculation" option causes this function to
+ * return the result of performing a mathematical calculation on
+ * all metadata that match the query instead of returning
+ * ElggMetadata objects.
+ *
+ * @return ElggMetadata[]|mixed
+ * @since 1.8.0
*/
-function delete_metadata($id) {
- global $CONFIG;
-
- $id = (int)$id;
- $metadata = get_metadata($id);
-
- if ($metadata) {
- // Tidy up if memcache is enabled.
- static $metabyname_memcache;
- if ((!$metabyname_memcache) && (is_memcache_available())) {
- $metabyname_memcache = new ElggMemcache('metabyname_memcache');
- }
-
- if ($metabyname_memcache) {
- $metabyname_memcache->delete("{$metadata->entity_guid}:{$metadata->name_id}");
- }
+function elgg_get_metadata(array $options = array()) {
- if (($metadata->canEdit()) && (trigger_elgg_event('delete', 'metadata', $metadata))) {
- return delete_data("DELETE from {$CONFIG->dbprefix}metadata where id=$id");
- }
+ // @todo remove support for count shortcut - see #4393
+ // support shortcut of 'count' => true for 'metadata_calculation' => 'count'
+ if (isset($options['count']) && $options['count']) {
+ $options['metadata_calculation'] = 'count';
+ unset($options['count']);
}
- return false;
+ $options['metastring_type'] = 'metadata';
+ return elgg_get_metastring_based_objects($options);
}
/**
- * Return the metadata values that match your query.
+ * Deletes metadata based on $options.
*
- * @param string $meta_name
- * @return mixed either a value, an array of ElggMetadata or false.
+ * @warning Unlike elgg_get_metadata() this will not accept an empty options array!
+ * 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 bool|null true on success, false on failure, null if no metadata to delete.
+ * @since 1.8.0
*/
-function get_metadata_byname($entity_guid, $meta_name) {
- global $CONFIG;
-
- $meta_name = get_metastring_id($meta_name);
-
- if (empty($meta_name)) {
+function elgg_delete_metadata(array $options) {
+ if (!elgg_is_valid_options_for_batch_operation($options, 'metadata')) {
return false;
}
+ $options['metastring_type'] = 'metadata';
+ $result = elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false);
- $entity_guid = (int)$entity_guid;
- $access = get_access_sql_suffix("e");
- $md_access = get_access_sql_suffix("m");
-
- // If memcache is available then cache this (cache only by name for now since this is the most common query)
- $meta = null;
- static $metabyname_memcache;
- if ((!$metabyname_memcache) && (is_memcache_available())) {
- $metabyname_memcache = new ElggMemcache('metabyname_memcache');
- }
- if ($metabyname_memcache) {
- $meta = $metabyname_memcache->load("{$entity_guid}:{$meta_name}");
- }
- if ($meta) {
- return $meta;
- }
-
- $result = get_data("SELECT m.*, n.string as name, v.string as value from {$CONFIG->dbprefix}metadata m JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.entity_guid JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id where m.entity_guid=$entity_guid and m.name_id='$meta_name' and $access and $md_access ORDER BY m.id ASC", "row_to_elggmetadata");
- if (!$result) {
- return false;
- }
-
- // Cache if memcache available
- if ($metabyname_memcache) {
- if (count($result) == 1) {
- $r = $result[0];
- } else {
- $r = $result;
- }
- // This is a bit of a hack - we shorten the expiry on object
- // metadata so that it'll be gone in an hour. This means that
- // deletions and more importantly updates will filter through eventually.
- $metabyname_memcache->setDefaultExpiry(3600);
- $metabyname_memcache->save("{$entity_guid}:{$meta_name}", $r);
- }
- if (count($result) == 1) {
- return $result[0];
- }
+ // This moved last in case an object's constructor sets metadata. Currently the batch
+ // delete process has to create the entity to delete its metadata. See #5214
+ elgg_get_metadata_cache()->invalidateByOptions('delete', $options);
return $result;
}
/**
- * Return all the metadata for a given GUID.
+ * Disables metadata based on $options.
*
- * @param int $entity_guid
+ * @warning Unlike elgg_get_metadata() this will not accept an empty options array!
+ *
+ * @param array $options An options array. {@See elgg_get_metadata()}
+ * @return bool|null true on success, false on failure, null if no metadata disabled.
+ * @since 1.8.0
*/
-function get_metadata_for_entity($entity_guid) {
- global $CONFIG;
+function elgg_disable_metadata(array $options) {
+ if (!elgg_is_valid_options_for_batch_operation($options, 'metadata')) {
+ return false;
+ }
- $entity_guid = (int)$entity_guid;
- $access = get_access_sql_suffix("e");
- $md_access = get_access_sql_suffix("m");
+ elgg_get_metadata_cache()->invalidateByOptions('disable', $options);
+
+ // if we can see hidden (disabled) we need to use the offset
+ // otherwise we risk an infinite loop if there are more than 50
+ $inc_offset = access_get_show_hidden_status();
- return get_data("SELECT m.*, n.string as name, v.string as value from {$CONFIG->dbprefix}metadata m JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.entity_guid JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id where m.entity_guid=$entity_guid and $access and $md_access", "row_to_elggmetadata");
+ $options['metastring_type'] = 'metadata';
+ return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset);
}
/**
- * Get the metadata where the entities they are referring to match a given criteria.
- *
- * @param mixed $meta_name
- * @param mixed $meta_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 $limit
- * @param int $offset
- * @param string $order_by Optional ordering.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
+ * Enables metadata based on $options.
+ *
+ * @warning Unlike elgg_get_metadata() this will not accept an empty options array!
+ *
+ * @warning In order to enable metadata, you must first use
+ * {@link access_show_hidden_entities()}.
+ *
+ * @param array $options An options array. {@See elgg_get_metadata()}
+ * @return bool|null true on success, false on failure, null if no metadata enabled.
+ * @since 1.8.0
*/
-function find_metadata($meta_name = "", $meta_value = "", $entity_type = "", $entity_subtype = "", $limit = 10, $offset = 0, $order_by = "", $site_guid = 0) {
- global $CONFIG;
-
- $meta_n = get_metastring_id($meta_name);
- $meta_v = get_metastring_id($meta_value);
-
- $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 ($site_guid == 0) {
- $site_guid = $CONFIG->site_guid;
- }
-
- $where = array();
-
- if ($entity_type!="") {
- $where[] = "e.type='$entity_type'";
- }
-
- if ($entity_subtype) {
- $where[] = "e.subtype=$entity_subtype";
- }
-
- if ($meta_name!="") {
- if (!$meta_v) {
- // The value is set, but we didn't get a value... so something went wrong.
- return false;
- }
- $where[] = "m.name_id='$meta_n'";
- }
- if ($meta_value!="") {
- // The value is set, but we didn't get a value... so something went wrong.
- if (!$meta_v) {
- return false;
- }
- $where[] = "m.value_id='$meta_v'";
- }
- if ($site_guid > 0) {
- $where[] = "e.site_guid = {$site_guid}";
+function elgg_enable_metadata(array $options) {
+ if (!$options || !is_array($options)) {
+ return false;
}
- $query = "SELECT m.*, n.string as name, v.string as value from {$CONFIG->dbprefix}entities e JOIN {$CONFIG->dbprefix}metadata m on e.guid = m.entity_guid JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id where";
- foreach ($where as $w) {
- $query .= " $w and ";
- }
- $query .= get_access_sql_suffix("e"); // Add access controls
- $query .= ' and ' . get_access_sql_suffix("m"); // Add access controls
- $query .= " order by $order_by limit $offset, $limit"; // Add order and limit
+ elgg_get_metadata_cache()->invalidateByOptions('enable', $options);
- return get_data($query, "row_to_elggmetadata");
+ $options['metastring_type'] = 'metadata';
+ return elgg_batch_metastring_based_objects($options, 'elgg_batch_enable_callback');
}
/**
+ * ElggEntities interfaces
+ */
+
+/**
* Returns entities based upon metadata. Also accepts all
* options available to elgg_get_entities(). Supports
* the singular option shortcut.
*
- * NB: Using metadata_names and metadata_values results in a
+ * @note Using metadata_names and metadata_values results in a
* "names IN (...) AND values IN (...)" clause. This is subtly
* differently than default multiple metadata_name_value_pairs, which use
* "(name = value) AND (name = value)" clauses.
*
* When in doubt, use name_value_pairs.
*
+ * To ask for entities that do not have a metadata value, use a custom
+ * where clause like this:
+ *
+ * $options['wheres'][] = "NOT EXISTS (
+ * SELECT 1 FROM {$dbprefix}metadata md
+ * WHERE md.entity_guid = e.guid
+ * AND md.name_id = $name_metastring_id
+ * AND md.value_id = $value_metastring_id)";
+ *
+ * Note the metadata name and value has been denormalized in the above example.
+ *
* @see elgg_get_entities
- * @see elgg_get_entities_from_annotations
+ *
* @param array $options Array in format:
*
* metadata_names => NULL|ARR metadata names
*
* metadata_values => NULL|ARR metadata values
*
- * metadata_name_value_pairs => NULL|ARR (name => 'name', value => 'value', 'operand' => '=', 'case_sensitive' => TRUE) entries.
- * Currently if multiple values are sent via an array (value => array('value1', 'value2') the pair's operand will be forced to "IN".
- *
- * metadata_name_value_pairs_operator => NULL|STR The operator to use for combining (name = value) OPERATOR (name = value); default AND
+ * metadata_name_value_pairs => NULL|ARR (
+ * name => 'name',
+ * value => 'value',
+ * 'operand' => '=',
+ * 'case_sensitive' => TRUE
+ * )
+ * Currently if multiple values are sent via
+ * an array (value => array('value1', 'value2')
+ * the pair's operand will be forced to "IN".
+ * If passing "IN" as the operand and a string as the value,
+ * the value must be a properly quoted and escaped string.
+ *
+ * metadata_name_value_pairs_operator => NULL|STR The operator to use for combining
+ * (name = value) OPERATOR (name = value); default AND
*
* metadata_case_sensitive => BOOL Overall Case sensitive
*
- * order_by_metadata => NULL|ARR (array('name' => 'metadata_text1', 'direction' => ASC|DESC, 'as' => text|integer),
- * Also supports array('name' => 'metadata_text1')
+ * order_by_metadata => NULL|ARR array(
+ * 'name' => 'metadata_text1',
+ * 'direction' => ASC|DESC,
+ * 'as' => text|integer
+ * )
+ * Also supports array('name' => 'metadata_text1')
*
* metadata_owner_guids => NULL|ARR guids for metadata owners
*
- * @return array
+ * @return ElggEntity[]|mixed If count, int. If not count, array. false on errors.
+ * @since 1.7.0
*/
function elgg_get_entities_from_metadata(array $options = array()) {
$defaults = array(
- 'metadata_names' => ELGG_ENTITIES_ANY_VALUE,
- 'metadata_values' => ELGG_ENTITIES_ANY_VALUE,
- 'metadata_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE,
+ 'metadata_names' => ELGG_ENTITIES_ANY_VALUE,
+ 'metadata_values' => ELGG_ENTITIES_ANY_VALUE,
+ 'metadata_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE,
- 'metadata_name_value_pairs_operator'=> 'AND',
- 'metadata_case_sensitive' => TRUE,
- 'order_by_metadata' => array(),
+ 'metadata_name_value_pairs_operator' => 'AND',
+ 'metadata_case_sensitive' => TRUE,
+ 'order_by_metadata' => array(),
- 'metadata_owner_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'metadata_owner_guids' => ELGG_ENTITIES_ANY_VALUE,
);
$options = array_merge($defaults, $options);
- $singulars = array('metadata_name', 'metadata_value', 'metadata_name_value_pair', 'metadata_owner_guid');
+ $singulars = array('metadata_name', 'metadata_value',
+ 'metadata_name_value_pair', 'metadata_owner_guid');
+
$options = elgg_normalise_plural_options_array($options, $singulars);
if (!$options = elgg_entities_get_metastrings_options('metadata', $options)) {
@@ -592,63 +457,6 @@ function elgg_get_entities_from_metadata(array $options = array()) {
}
/**
- * Returns options to pass to elgg_get_entities() for metastrings operations.
- *
- * @param string $type Metastring type: annotations or metadata
- * @param array $options Options
- *
- * @return array
- */
-function elgg_entities_get_metastrings_options($type, $options) {
- $valid_types = array('metadata', 'annotation');
- if (!in_array($type, $valid_types)) {
- return FALSE;
- }
-
- // the options for annotations are singular (annotation_name) but the table
- // is plural (elgg_annotations) so rewrite for the table name.
- $n_table = ($type == 'annotation') ? 'annotations' : $type;
-
- $singulars = array("{$type}_name", "{$type}_value", "{$type}_name_value_pair", "{$type}_owner_guid");
- $options = elgg_normalise_plural_options_array($options, $singulars);
-
- $clauses = elgg_get_entity_metadata_where_sql('e', $n_table, $options["{$type}_names"], $options["{$type}_values"],
- $options["{$type}_name_value_pairs"], $options["{$type}_name_value_pairs_operator"], $options["{$type}_case_sensitive"],
- $options["order_by_{$type}"], $options["{$type}_owner_guids"]);
-
- if ($clauses) {
- // merge wheres to pass to get_entities()
- if (isset($options['wheres']) && !is_array($options['wheres'])) {
- $options['wheres'] = array($options['wheres']);
- } elseif (!isset($options['wheres'])) {
- $options['wheres'] = array();
- }
-
- $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']);
-
- // merge joins to pass to get_entities()
- if (isset($options['joins']) && !is_array($options['joins'])) {
- $options['joins'] = array($options['joins']);
- } elseif (!isset($options['joins'])) {
- $options['joins'] = array();
- }
-
- $options['joins'] = array_merge($options['joins'], $clauses['joins']);
-
- if ($clauses['orders']) {
- $order_by_metadata = implode(", ",$clauses['orders']);
- if (isset($options['order_by']) && $options['order_by']) {
- $options['order_by'] = "$order_by_metadata, {$options['order_by']}";
- } else {
- $options['order_by'] = "$order_by_metadata, e.time_created DESC";
- }
- }
- }
-
- return $options;
-}
-
-/**
* Returns metadata name and value SQL where for entities.
* NB: $names and $values are not paired. Use $pairs for this.
* Pairs default to '=' operand.
@@ -656,17 +464,25 @@ function elgg_entities_get_metastrings_options($type, $options) {
* This function is reused for annotations because the tables are
* exactly the same.
*
- * @param string $e_table Entities table name
- * @param string $n_table Normalized metastrings table name (Where entities, values, and names are joined. annotations / metadata)
- * @param ARR|NULL $names
- * @param ARR|NULL $values
- * @param ARR|NULL $pairs array of names / values / operands
- * @param AND|OR $pair_operator Operator to use to join the where clauses for pairs
- * @param BOOL $case_sensitive
- * @param ARR|NULL $order_by_metadata array of names / direction
- * @return FALSE|array False on fail, array('joins', 'wheres')
+ * @param string $e_table Entities table name
+ * @param string $n_table Normalized metastrings table name (Where entities,
+ * values, and names are joined. annotations / metadata)
+ * @param array|null $names Array of names
+ * @param array|null $values Array of values
+ * @param array|null $pairs Array of names / values / operands
+ * @param string $pair_operator ("AND" or "OR") Operator to use to join the where clauses for pairs
+ * @param bool $case_sensitive Case sensitive metadata names?
+ * @param array|null $order_by_metadata Array of names / direction
+ * @param array|null $owner_guids Array of owner GUIDs
+ *
+ * @return false|array False on fail, array('joins', 'wheres')
+ * @since 1.7.0
+ * @access private
*/
-function elgg_get_entity_metadata_where_sql($e_table, $n_table, $names = NULL, $values = NULL, $pairs = NULL, $pair_operator = 'AND', $case_sensitive = TRUE, $order_by_metadata = NULL, $owner_guids = NULL) {
+function elgg_get_entity_metadata_where_sql($e_table, $n_table, $names = NULL, $values = NULL,
+$pairs = NULL, $pair_operator = 'AND', $case_sensitive = TRUE, $order_by_metadata = NULL,
+$owner_guids = NULL) {
+
global $CONFIG;
// short circuit if nothing requested
@@ -677,7 +493,7 @@ function elgg_get_entity_metadata_where_sql($e_table, $n_table, $names = NULL, $
&& (!$values && $values !== 0)
&& (!$pairs && $pairs !== 0)
&& (!$owner_guids && $owner_guids !== 0)
- && !isset($order_by_metadata)) {
+ && !$order_by_metadata) {
return '';
}
@@ -698,7 +514,8 @@ function elgg_get_entity_metadata_where_sql($e_table, $n_table, $names = NULL, $
);
// will always want to join these tables if pulling metastrings.
- $return['joins'][] = "JOIN {$CONFIG->dbprefix}{$n_table} n_table on {$e_table}.guid = n_table.entity_guid";
+ $return['joins'][] = "JOIN {$CONFIG->dbprefix}{$n_table} n_table on
+ {$e_table}.guid = n_table.entity_guid";
$wheres = array();
@@ -805,6 +622,8 @@ function elgg_get_entity_metadata_where_sql($e_table, $n_table, $names = NULL, $
// if the operand is IN don't quote it because quoting should be done already.
if (is_numeric($pair['value'])) {
$value = sanitise_string($pair['value']);
+ } else if (is_bool($pair['value'])) {
+ $value = (int) $pair['value'];
} else if (is_array($pair['value'])) {
$values_array = array();
@@ -832,16 +651,20 @@ function elgg_get_entity_metadata_where_sql($e_table, $n_table, $names = NULL, $
$name = sanitise_string($pair['name']);
// @todo The multiple joins are only needed when the operator is AND
- $return['joins'][] = "JOIN {$CONFIG->dbprefix}{$n_table} n_table{$i} on {$e_table}.guid = n_table{$i}.entity_guid";
- $return['joins'][] = "JOIN {$CONFIG->dbprefix}metastrings msn{$i} on n_table{$i}.name_id = msn{$i}.id";
- $return['joins'][] = "JOIN {$CONFIG->dbprefix}metastrings msv{$i} on n_table{$i}.value_id = msv{$i}.id";
+ $return['joins'][] = "JOIN {$CONFIG->dbprefix}{$n_table} n_table{$i}
+ on {$e_table}.guid = n_table{$i}.entity_guid";
+ $return['joins'][] = "JOIN {$CONFIG->dbprefix}metastrings msn{$i}
+ on n_table{$i}.name_id = msn{$i}.id";
+ $return['joins'][] = "JOIN {$CONFIG->dbprefix}metastrings msv{$i}
+ on n_table{$i}.value_id = msv{$i}.id";
- $pair_wheres[] = "(msn{$i}.string = '$name' AND {$pair_binary}msv{$i}.string $operand $value AND $access)";
+ $pair_wheres[] = "(msn{$i}.string = '$name' AND {$pair_binary}msv{$i}.string
+ $operand $value AND $access)";
$i++;
}
- if ($where = implode (" $pair_operator ", $pair_wheres)) {
+ if ($where = implode(" $pair_operator ", $pair_wheres)) {
$wheres[] = "($where)";
}
}
@@ -863,7 +686,7 @@ function elgg_get_entity_metadata_where_sql($e_table, $n_table, $names = NULL, $
}
if (is_array($order_by_metadata)) {
- if ((count($order_by_metadata) > 0) && !is_array($order_by_metadata[0])) {
+ if ((count($order_by_metadata) > 0) && !isset($order_by_metadata[0])) {
// singleton, so fix
$order_by_metadata = array($order_by_metadata);
}
@@ -875,9 +698,12 @@ function elgg_get_entity_metadata_where_sql($e_table, $n_table, $names = NULL, $
} else {
$direction = 'ASC';
}
- $return['joins'][] = "JOIN {$CONFIG->dbprefix}{$n_table} n_table{$i} on {$e_table}.guid = n_table{$i}.entity_guid";
- $return['joins'][] = "JOIN {$CONFIG->dbprefix}metastrings msn{$i} on n_table{$i}.name_id = msn{$i}.id";
- $return['joins'][] = "JOIN {$CONFIG->dbprefix}metastrings msv{$i} on n_table{$i}.value_id = msv{$i}.id";
+ $return['joins'][] = "JOIN {$CONFIG->dbprefix}{$n_table} n_table{$i}
+ on {$e_table}.guid = n_table{$i}.entity_guid";
+ $return['joins'][] = "JOIN {$CONFIG->dbprefix}metastrings msn{$i}
+ on n_table{$i}.name_id = msn{$i}.id";
+ $return['joins'][] = "JOIN {$CONFIG->dbprefix}metastrings msv{$i}
+ on n_table{$i}.value_id = msv{$i}.id";
$access = get_access_sql_suffix("n_table{$i}");
@@ -896,272 +722,35 @@ function elgg_get_entity_metadata_where_sql($e_table, $n_table, $names = NULL, $
}
/**
- * Return a list of entities based on the given search criteria.
- *
- * @deprecated 1.7 use elgg_get_entities_from_metadata().
- * @param mixed $meta_name
- * @param mixed $meta_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 $limit
- * @param int $offset
- * @param string $order_by Optional ordering.
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @param true|false $count If set to true, returns the total number of entities rather than a list. (Default: false)
- * @param true|false $case_sensitive If set to false this searches for the meta data without case sensitivity. (Default: true)
- *
- * @return int|array A list of entities, or a count if $count is set to true
- */
-function get_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, $case_sensitive = TRUE) {
-
- elgg_deprecated_notice('get_entities_from_metadata() was deprecated by elgg_get_entities_from_metadata()!', 1.7);
-
- $options = array();
-
- $options['metadata_names'] = $meta_name;
-
- if ($meta_value) {
- $options['metadata_values'] = $meta_value;
- }
-
- if ($entity_type) {
- $options['types'] = $entity_type;
- }
-
- if ($entity_subtype) {
- $options['subtypes'] = $entity_subtype;
- }
-
- if ($owner_guid) {
- $options['owner_guid'] = $owner_guid;
- }
-
- if ($limit) {
- $options['limit'] = $limit;
- }
-
- if ($offset) {
- $options['offset'] = $offset;
- }
-
- if ($order_by) {
- $options['order_by'];
- }
-
- if ($site_guid) {
- $options['site_guid'];
- }
-
- if ($count) {
- $options['count'] = $count;
- }
-
- // need to be able to pass false
- $options['metadata_case_sensitive'] = $case_sensitive;
-
- return elgg_get_entities_from_metadata($options);
-}
-
-/**
- * Return a list of entities suitable for display based on the given search criteria.
- *
- * @see elgg_view_entity_list
- *
- * @deprecated 1.8 Use elgg_list_entities_from_metadata
- * @param mixed $meta_name Metadata name to search on
- * @param mixed $meta_value The value to match, optionally
- * @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 $limit Number of entities to display per page
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow users to toggle to the gallery view. Default: true
- * @param true|false $pagination Display pagination? Default: true
- *
- * @return string A list of entities suitable for display
- */
-function list_entities_from_metadata($meta_name, $meta_value = "", $entity_type = ELGG_ENTITIES_ANY_VALUE, $entity_subtype = ELGG_ENTITIES_ANY_VALUE, $owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = true, $pagination = true, $case_sensitive = true ) {
- elgg_deprecated_notice('get_entities_from_metadata_multi() was deprecated by elgg_get_entities_from_metadata()!', 1.8);
-
- $offset = (int) get_input('offset');
- $limit = (int) $limit;
- $options = array(
- 'metadata_name' => $meta_name,
- 'metadata_value' => $meta_value,
- 'types' => $entity_type,
- 'subtypes' => $entity_subtype,
- 'owner_guid' => $owner_guid,
- 'limit' => $limit,
- 'offset' => $offset,
- 'count' => TRUE,
- 'metadata_case_sensitive' => $case_sensitive
- );
- $count = elgg_get_entities_from_metadata($options);
-
- $options['count'] = FALSE;
- $entities = elgg_get_entities_from_metadata($options);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
-}
-
-/**
* Returns a list of entities filtered by provided metadata.
*
* @see elgg_get_entities_from_metadata
*
- * @param array $options
+ * @param array $options Options array
+ *
+ * @return array
+ * @since 1.7.0
*/
function elgg_list_entities_from_metadata($options) {
- $defaults = array(
- 'offset' => (int) max(get_input('offset', 0), 0),
- 'limit' => (int) max(get_input('limit', 10), 0),
- 'full_view' => TRUE,
- 'view_type_toggle' => FALSE,
- 'pagination' => TRUE
- );
-
- $options = array_merge($defaults, $options);
-
- $count = elgg_get_entities_from_metadata(array_merge(array('count' => TRUE), $options));
- $entities = elgg_get_entities_from_metadata($options);
-
- return elgg_view_entity_list($entities, $count, $options['offset'], $options['limit'], $options['full_view'], $options['view_type_toggle'], $options['pagination']);
-}
-
-/**
- * @deprecated 1.7. Use elgg_get_entities_from_metadata().
- * @param $meta_array
- * @param $entity_type
- * @param $entity_subtype
- * @param $owner_guid
- * @param $limit
- * @param $offset
- * @param $order_by
- * @param $site_guid
- * @param $count
- * @param $meta_array_operator
- * @return unknown_type
- */
-function get_entities_from_metadata_multi($meta_array, $entity_type = "", $entity_subtype = "",
-$owner_guid = 0, $limit = 10, $offset = 0, $order_by = "", $site_guid = 0,
-$count = false, $meta_array_operator = 'and') {
- elgg_deprecated_notice('get_entities_from_metadata_multi() was deprecated by elgg_get_entities_from_metadata()!', 1.7);
-
- if (!is_array($meta_array) || sizeof($meta_array) == 0) {
- return false;
- }
-
- $options = array();
-
- $options['metadata_name_value_pairs'] = $meta_array;
-
- if ($entity_type) {
- $options['types'] = $entity_type;
- }
-
- if ($entity_subtype) {
- $options['subtypes'] = $entity_subtype;
- }
-
- if ($owner_guid) {
- $options['owner_guid'] = $owner_guid;
- }
-
- if ($limit) {
- $options['limit'] = $limit;
- }
-
- if ($offset) {
- $options['offset'] = $offset;
- }
-
- if ($order_by) {
- $options['order_by'];
- }
-
- if ($site_guid) {
- $options['site_guid'];
- }
-
- if ($count) {
- $options['count'] = $count;
- }
-
- $options['metadata_name_value_pairs_operator'] = $meta_array_operator;
-
- return elgg_get_entities_from_metadata($options);
+ return elgg_list_entities($options, 'elgg_get_entities_from_metadata');
}
/**
- * Returns a viewable list of entities based on the given search criteria.
- *
- * @see elgg_view_entity_list
- *
- * @param array $meta_array Array of 'name' => 'value' pairs
- * @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 $limit
- * @param int $offset
- * @param string $order_by Optional ordering.
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow users to toggle to the gallery view. Default: true
- * @param true|false $pagination Display pagination? Default: true
- * @return string List of ElggEntities suitable for display
+ * Other functions
*/
-function list_entities_from_metadata_multi($meta_array, $entity_type = "", $entity_subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = true, $pagination = true) {
- $offset = (int) get_input('offset');
- $limit = (int) $limit;
- $count = get_entities_from_metadata_multi($meta_array, $entity_type, $entity_subtype, $owner_guid, $limit, $offset, "", $site_guid, true);
- $entities = get_entities_from_metadata_multi($meta_array, $entity_type, $entity_subtype, $owner_guid, $limit, $offset, "", $site_guid, false);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
-}
/**
- * Clear all the metadata for a given entity, assuming you have access to that metadata.
+ * Handler called by trigger_plugin_hook on the "export" event.
*
- * @param int $guid
- */
-function clear_metadata($entity_guid) {
- global $CONFIG;
-
- $entity_guid = (int)$entity_guid;
- if ($entity = get_entity($entity_guid)) {
- if ($entity->canEdit()) {
- return delete_data("DELETE from {$CONFIG->dbprefix}metadata where entity_guid={$entity_guid}");
- }
- }
- return false;
-}
-
-/**
- * Clear all annotations belonging to a given owner_guid
+ * @param string $hook export
+ * @param string $entity_type all
+ * @param mixed $returnvalue Value returned from previous hook
+ * @param mixed $params Params
*
- * @param int $owner_guid The owner
- */
-function clear_metadata_by_owner($owner_guid) {
- global $CONFIG;
-
- $owner_guid = (int)$owner_guid;
-
- $metas = get_data("SELECT id from {$CONFIG->dbprefix}metadata WHERE owner_guid=$owner_guid");
- $deleted = 0;
-
- if (is_array($metas)) {
- foreach ($metas as $id) {
- // Is this the best way?
- if (delete_metadata($id->id)) {
- $deleted++;
- }
- }
- }
-
- return $deleted;
-}
-
-/**
- * Handler called by trigger_plugin_hook on the "export" event.
+ * @return array
+ * @access private
+ *
+ * @throws InvalidParameterException
*/
function export_metadata_plugin_hook($hook, $entity_type, $returnvalue, $params) {
// Sanity check values
@@ -1173,12 +762,13 @@ function export_metadata_plugin_hook($hook, $entity_type, $returnvalue, $params)
throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonArrayReturnValue'));
}
- $guid = (int)$params['guid'];
- $name = $params['name'];
-
- $result = get_metadata_for_entity($guid);
+ $result = elgg_get_metadata(array(
+ 'guid' => (int)$params['guid'],
+ 'limit' => 0,
+ ));
if ($result) {
+ /* @var ElggMetadata[] $result */
foreach ($result as $r) {
$returnvalue[] = $r->export();
}
@@ -1188,34 +778,37 @@ 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
+ * Takes in a comma-separated string and returns an array of tags
+ * which have been trimmed
*
* @param string $string Comma-separated tag string
+ *
* @return array|false An array of strings, or false on failure
*/
function string_to_tag_array($string) {
if (is_string($string)) {
- $ar = explode(",",$string);
- $ar = array_map('trim', $ar); // trim blank spaces
- $ar = array_map('elgg_strtolower', $ar); // make lower case : [Marcus Povey 20090605 - Using mb wrapper function using UTF8 safe function where available]
- $ar = array_filter($ar, 'is_not_null'); // Remove null values
+ $ar = explode(",", $string);
+ $ar = array_map('trim', $ar);
+ $ar = array_filter($ar, 'is_not_null');
+ $ar = array_map('strip_tags', $ar);
return $ar;
}
return false;
-
}
/**
- * Takes a metadata array (which has all kinds of properties) and turns it into a simple array of strings
+ * Takes a metadata array (which has all kinds of properties)
+ * and turns it into a simple array of strings
*
* @param array $array Metadata array
+ *
* @return array Array of strings
*/
function metadata_array_to_values($array) {
$valuearray = array();
if (is_array($array)) {
- foreach($array as $element) {
+ foreach ($array as $element) {
$valuearray[] = $element->value;
}
}
@@ -1224,14 +817,18 @@ function metadata_array_to_values($array) {
}
/**
- * Get the URL for this item of metadata, by default this links to the export handler in the current view.
+ * Get the URL for this metadata
+ *
+ * By default this links to the export handler in the current view.
*
- * @param int $id
+ * @param int $id Metadata ID
+ *
+ * @return mixed
*/
function get_metadata_url($id) {
$id = (int)$id;
- if ($extender = get_metadata($id)) {
+ if ($extender = elgg_get_metadata_from_id($id)) {
return get_extender_url($extender);
}
return false;
@@ -1241,8 +838,10 @@ function get_metadata_url($id) {
* Mark entities with a particular type and subtype as having access permissions
* that can be changed independently from their parent entity
*
- * @param string $type The type - object, user, etc
+ * @param string $type The type - object, user, etc
* @param string $subtype The subtype; all subtypes by default
+ *
+ * @return void
*/
function register_metadata_as_independent($type, $subtype = '*') {
global $CONFIG;
@@ -1256,9 +855,10 @@ function register_metadata_as_independent($type, $subtype = '*') {
* Determines whether entities of a given type and subtype should not change
* their metadata in line with their parent entity
*
- * @param string $type The type - object, user, etc
+ * @param string $type The type - object, user, etc
* @param string $subtype The entity subtype
- * @return true|false
+ *
+ * @return bool
*/
function is_metadata_independent($type, $subtype) {
global $CONFIG;
@@ -1275,17 +875,20 @@ function is_metadata_independent($type, $subtype) {
/**
* When an entity is updated, resets the access ID on all of its child metadata
*
- * @param string $event The name of the event
- * @param string $object_type The type of object
- * @param ElggEntity $object The entity itself
+ * @param string $event The name of the event
+ * @param string $object_type The type of object
+ * @param ElggEntity $object The entity itself
+ *
+ * @return true
*/
function metadata_update($event, $object_type, $object) {
if ($object instanceof ElggEntity) {
if (!is_metadata_independent($object->getType(), $object->getSubtype())) {
- global $CONFIG;
+ $db_prefix = elgg_get_config('dbprefix');
$access_id = (int) $object->access_id;
$guid = (int) $object->getGUID();
- update_data("update {$CONFIG->dbprefix}metadata set access_id = {$access_id} where entity_guid = {$guid}");
+ $query = "update {$db_prefix}metadata set access_id = {$access_id} where entity_guid = {$guid}";
+ update_data($query);
}
}
return true;
@@ -1294,22 +897,82 @@ function metadata_update($event, $object_type, $object) {
/**
* Register a metadata url handler.
*
- * @param string $function_name The function.
* @param string $extender_name The name, default 'all'.
+ * @param string $function The function name.
+ *
+ * @return bool
*/
-function register_metadata_url_handler($function_name, $extender_name = "all") {
- return register_extender_url_handler($function_name, 'metadata', $extender_name);
+function elgg_register_metadata_url_handler($extender_name, $function) {
+ return elgg_register_extender_url_handler('metadata', $extender_name, $function);
+}
+
+/**
+ * Get the global metadata cache instance
+ *
+ * @return ElggVolatileMetadataCache
+ *
+ * @access private
+ */
+function elgg_get_metadata_cache() {
+ global $CONFIG;
+ if (empty($CONFIG->local_metadata_cache)) {
+ $CONFIG->local_metadata_cache = new ElggVolatileMetadataCache();
+ }
+ return $CONFIG->local_metadata_cache;
+}
+
+/**
+ * Invalidate the metadata cache based on options passed to various *_metadata functions
+ *
+ * @param string $action Action performed on metadata. "delete", "disable", or "enable"
+ * @param array $options Options passed to elgg_(delete|disable|enable)_metadata
+ * @return void
+ */
+function elgg_invalidate_metadata_cache($action, array $options) {
+ // remove as little as possible, optimizing for common cases
+ $cache = elgg_get_metadata_cache();
+ if (empty($options['guid'])) {
+ // safest to clear everything unless we want to make this even more complex :(
+ $cache->flush();
+ } else {
+ if (empty($options['metadata_name'])) {
+ // safest to clear the whole entity
+ $cache->clear($options['guid']);
+ } else {
+ switch ($action) {
+ case 'delete':
+ $cache->markEmpty($options['guid'], $options['metadata_name']);
+ break;
+ default:
+ $cache->markUnknown($options['guid'], $options['metadata_name']);
+ }
+ }
+ }
}
/** Register the hook */
-register_plugin_hook("export", "all", "export_metadata_plugin_hook", 2);
+elgg_register_plugin_hook_handler("export", "all", "export_metadata_plugin_hook", 2);
+
/** Call a function whenever an entity is updated **/
-register_elgg_event_handler('update','all','metadata_update');
+elgg_register_event_handler('update', 'all', 'metadata_update');
// unit testing
-register_plugin_hook('unit_test', 'system', 'metadata_test');
+elgg_register_plugin_hook_handler('unit_test', 'system', 'metadata_test');
+
+/**
+ * Metadata unit test
+ *
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of other tests
+ * @param mixed $params Params
+ *
+ * @return array
+ * @access private
+ */
function metadata_test($hook, $type, $value, $params) {
global $CONFIG;
- $value[] = $CONFIG->path . 'engine/tests/objects/metadata.php';
+ $value[] = $CONFIG->path . 'engine/tests/api/metadata.php';
+ $value[] = $CONFIG->path . 'engine/tests/api/metadata_cache.php';
return $value;
}
diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php
index 71f32a786..57d876c06 100644
--- a/engine/lib/metastrings.php
+++ b/engine/lib/metastrings.php
@@ -3,35 +3,39 @@
* Elgg metastrngs
* Functions to manage object metastrings.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage DataModel.MetaStrings
*/
/** Cache metastrings for a page */
+global $METASTRINGS_CACHE;
$METASTRINGS_CACHE = array();
/** Keep a record of strings we know don't exist */
+global $METASTRINGS_DEADNAME_CACHE;
$METASTRINGS_DEADNAME_CACHE = array();
+
+
/**
* Return the meta string id for a given tag, or false.
*
- * @param string $string The value (whatever that is) to be stored
- * @param bool $case_sensitive Do we want to make the query case sensitive? If not there may be more than one result
- * @return int|array|false meta string id, array of ids or false if none found
+ * @param string $string The value to store
+ * @param bool $case_sensitive Do we want to make the query case sensitive?
+ * If not there may be more than one result
+ *
+ * @return int|array|false meta string id, array of ids or false if none found
*/
function get_metastring_id($string, $case_sensitive = TRUE) {
global $CONFIG, $METASTRINGS_CACHE, $METASTRINGS_DEADNAME_CACHE;
$string = sanitise_string($string);
-
- // caching doesn't work for case insensitive searches
- if ($case_sensitive) {
+
+ // caching doesn't work for case insensitive searches
+ if ($case_sensitive) {
$result = array_search($string, $METASTRINGS_CACHE, true);
-
- if ($result!==false) {
+
+ if ($result !== false) {
elgg_log("** Returning id for string:$string from cache.");
return $result;
}
@@ -40,7 +44,7 @@ function get_metastring_id($string, $case_sensitive = TRUE) {
if (in_array($string, $METASTRINGS_DEADNAME_CACHE, true)) {
return false;
}
-
+
// Experimental memcache
$msfc = null;
static $metastrings_memcache;
@@ -63,19 +67,19 @@ function get_metastring_id($string, $case_sensitive = TRUE) {
}
$row = FALSE;
- $metaStrings = get_data($query, "entity_row_to_elggstar");
+ $metaStrings = get_data($query);
if (is_array($metaStrings)) {
if (sizeof($metaStrings) > 1) {
$ids = array();
- foreach($metaStrings as $metaString) {
+ foreach ($metaStrings as $metaString) {
$ids[] = $metaString->id;
}
return $ids;
- } else {
+ } else if (isset($metaStrings[0])) {
$row = $metaStrings[0];
}
}
-
+
if ($row) {
$METASTRINGS_CACHE[$row->id] = $row->string; // Cache it
@@ -98,6 +102,7 @@ function get_metastring_id($string, $case_sensitive = TRUE) {
* When given an ID, returns the corresponding metastring
*
* @param int $id Metastring ID
+ *
* @return string Metastring
*/
function get_metastring($id) {
@@ -126,8 +131,9 @@ function get_metastring($id) {
* Add a metastring.
* It returns the id of the tag, whether by creating it or updating it.
*
- * @param string $string The value (whatever that is) to be stored
- * @param bool $case_sensitive Do we want to make the query case sensitive?
+ * @param string $string The value (whatever that is) to be stored
+ * @param bool $case_sensitive Do we want to make the query case sensitive?
+ *
* @return mixed Integer tag or false.
*/
function add_metastring($string, $case_sensitive = true) {
@@ -154,6 +160,8 @@ function add_metastring($string, $case_sensitive = true) {
/**
* Delete any orphaned entries in metastrings. This is run by the garbage collector.
*
+ * @return bool
+ * @access private
*/
function delete_orphaned_metastrings() {
global $CONFIG;
@@ -194,4 +202,702 @@ function delete_orphaned_metastrings() {
)";
return delete_data($query);
-} \ No newline at end of file
+}
+
+/**
+ * Returns an array of either ElggAnnotation or ElggMetadata objects.
+ * Accepts all elgg_get_entities() options for entity restraints.
+ *
+ * @see elgg_get_entities
+ *
+ * @param array $options Array in format:
+ *
+ * metastring_names => NULL|ARR metastring names
+ *
+ * metastring_values => NULL|ARR metastring values
+ *
+ * metastring_ids => NULL|ARR metastring ids
+ *
+ * metastring_case_sensitive => BOOL Overall Case sensitive
+ *
+ * metastring_owner_guids => NULL|ARR Guids for metadata owners
+ *
+ * metastring_created_time_lower => INT Lower limit for created time.
+ *
+ * metastring_created_time_upper => INT Upper limit for created time.
+ *
+ * metastring_calculation => STR Perform the MySQL function on the metastring values
+ * returned.
+ * This differs from egef_annotation_calculation in that
+ * it returns only the calculation of all annotation values.
+ * You can sum, avg, count, etc. egef_annotation_calculation()
+ * returns ElggEntities ordered by a calculation on their
+ * annotation values.
+ *
+ * metastring_type => STR metadata or annotation(s)
+ *
+ * @return mixed
+ * @access private
+ */
+function elgg_get_metastring_based_objects($options) {
+ $options = elgg_normalize_metastrings_options($options);
+
+ switch ($options['metastring_type']) {
+ case 'metadata':
+ $type = 'metadata';
+ $callback = 'row_to_elggmetadata';
+ break;
+
+ case 'annotations':
+ case 'annotation':
+ $type = 'annotations';
+ $callback = 'row_to_elggannotation';
+ break;
+
+ default:
+ return false;
+ }
+
+ $defaults = array(
+ // entities
+ 'types' => ELGG_ENTITIES_ANY_VALUE,
+ 'subtypes' => ELGG_ENTITIES_ANY_VALUE,
+ 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
+
+ 'guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'owner_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'container_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'site_guids' => get_config('site_guid'),
+
+ 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE,
+ 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE,
+ 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
+ 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
+
+ // options are normalized to the plural in case we ever add support for them.
+ 'metastring_names' => ELGG_ENTITIES_ANY_VALUE,
+ 'metastring_values' => ELGG_ENTITIES_ANY_VALUE,
+ //'metastring_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE,
+ //'metastring_name_value_pairs_operator' => 'AND',
+
+ 'metastring_case_sensitive' => TRUE,
+ //'order_by_metastring' => array(),
+ 'metastring_calculation' => ELGG_ENTITIES_NO_VALUE,
+
+ 'metastring_created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
+ 'metastring_created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
+
+ 'metastring_owner_guids' => ELGG_ENTITIES_ANY_VALUE,
+
+ 'metastring_ids' => ELGG_ENTITIES_ANY_VALUE,
+
+ // sql
+ 'order_by' => 'n_table.time_created asc',
+ 'limit' => 10,
+ 'offset' => 0,
+ 'count' => FALSE,
+ 'selects' => array(),
+ 'wheres' => array(),
+ 'joins' => array(),
+
+ 'callback' => $callback
+ );
+
+ // @todo Ignore site_guid right now because of #2910
+ $options['site_guid'] = ELGG_ENTITIES_ANY_VALUE;
+
+ $options = array_merge($defaults, $options);
+
+ // can't use helper function with type_subtype_pair because
+ // it's already an array...just need to merge it
+ if (isset($options['type_subtype_pair'])) {
+ if (isset($options['type_subtype_pairs'])) {
+ $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'],
+ $options['type_subtype_pair']);
+ } else {
+ $options['type_subtype_pairs'] = $options['type_subtype_pair'];
+ }
+ }
+
+ $singulars = array(
+ 'type', 'subtype', 'type_subtype_pair',
+ 'guid', 'owner_guid', 'container_guid', 'site_guid',
+ 'metastring_name', 'metastring_value',
+ 'metastring_owner_guid', 'metastring_id',
+ 'select', 'where', 'join'
+ );
+
+ $options = elgg_normalise_plural_options_array($options, $singulars);
+
+ if (!$options) {
+ return false;
+ }
+
+ $db_prefix = elgg_get_config('dbprefix');
+
+ // evaluate where clauses
+ if (!is_array($options['wheres'])) {
+ $options['wheres'] = array($options['wheres']);
+ }
+
+ $wheres = $options['wheres'];
+
+ // entities
+ $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'],
+ $options['subtypes'], $options['type_subtype_pairs']);
+
+ $wheres[] = elgg_get_guid_based_where_sql('e.guid', $options['guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']);
+
+ $wheres[] = elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
+ $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
+
+
+ $wheres[] = elgg_get_entity_time_where_sql('n_table', $options['metastring_created_time_upper'],
+ $options['metastring_created_time_lower'], null, null);
+
+ $wheres[] = elgg_get_guid_based_where_sql('n_table.owner_guid',
+ $options['metastring_owner_guids']);
+
+ // see if any functions failed
+ // remove empty strings on successful functions
+ foreach ($wheres as $i => $where) {
+ if ($where === FALSE) {
+ return FALSE;
+ } elseif (empty($where)) {
+ unset($wheres[$i]);
+ }
+ }
+
+ // remove identical where clauses
+ $wheres = array_unique($wheres);
+
+ // evaluate join clauses
+ if (!is_array($options['joins'])) {
+ $options['joins'] = array($options['joins']);
+ }
+
+ $joins = $options['joins'];
+ $joins[] = "JOIN {$db_prefix}entities e ON n_table.entity_guid = e.guid";
+
+ // evaluate selects
+ if (!is_array($options['selects'])) {
+ $options['selects'] = array($options['selects']);
+ }
+
+ $selects = $options['selects'];
+
+ // For performance reasons we don't want the joins required for metadata / annotations
+ // unless we're going through one of their callbacks.
+ // this means we expect the functions passing different callbacks to pass their required joins.
+ // If we're doing a calculation
+ $custom_callback = ($options['callback'] == 'row_to_elggmetadata'
+ || $options['callback'] == 'row_to_elggannotation');
+ $is_calculation = $options['metastring_calculation'] ? true : false;
+
+ if ($custom_callback || $is_calculation) {
+ $joins[] = "JOIN {$db_prefix}metastrings n on n_table.name_id = n.id";
+ $joins[] = "JOIN {$db_prefix}metastrings v on n_table.value_id = v.id";
+
+ $selects[] = 'n.string as name';
+ $selects[] = 'v.string as value';
+ }
+
+ foreach ($joins as $i => $join) {
+ if ($join === FALSE) {
+ return FALSE;
+ } elseif (empty($join)) {
+ unset($joins[$i]);
+ }
+ }
+
+ // metastrings
+ $metastring_clauses = elgg_get_metastring_sql('n_table', $options['metastring_names'],
+ $options['metastring_values'], null, $options['metastring_ids'],
+ $options['metastring_case_sensitive']);
+
+ if ($metastring_clauses) {
+ $wheres = array_merge($wheres, $metastring_clauses['wheres']);
+ $joins = array_merge($joins, $metastring_clauses['joins']);
+ } else {
+ $wheres[] = get_access_sql_suffix('n_table');
+ }
+
+ if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
+ $selects = array_unique($selects);
+ // evalutate selects
+ $select_str = '';
+ if ($selects) {
+ foreach ($selects as $select) {
+ $select_str .= ", $select";
+ }
+ }
+
+ $query = "SELECT DISTINCT n_table.*{$select_str} FROM {$db_prefix}$type n_table";
+ } elseif ($options['count']) {
+ // count is over the entities
+ $query = "SELECT count(DISTINCT e.guid) as calculation FROM {$db_prefix}$type n_table";
+ } else {
+ $query = "SELECT {$options['metastring_calculation']}(v.string) as calculation FROM {$db_prefix}$type n_table";
+ }
+
+ // remove identical join clauses
+ $joins = array_unique($joins);
+
+ // add joins
+ foreach ($joins as $j) {
+ $query .= " $j ";
+ }
+
+ // add wheres
+ $query .= ' WHERE ';
+
+ foreach ($wheres as $w) {
+ $query .= " $w AND ";
+ }
+
+ // Add access controls
+ $query .= get_access_sql_suffix('e');
+
+ // reverse order by
+ if (isset($options['reverse_order_by']) && $options['reverse_order_by']) {
+ $options['order_by'] = elgg_sql_reverse_order_by_clause($options['order_by'],
+ $defaults['order_by']);
+ }
+
+ if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
+ if (isset($options['group_by'])) {
+ $options['group_by'] = sanitise_string($options['group_by']);
+ $query .= " GROUP BY {$options['group_by']}";
+ }
+
+ if (isset($options['order_by']) && $options['order_by']) {
+ $options['order_by'] = sanitise_string($options['order_by']);
+ $query .= " ORDER BY {$options['order_by']}, n_table.id";
+ }
+
+ if ($options['limit']) {
+ $limit = sanitise_int($options['limit']);
+ $offset = sanitise_int($options['offset'], false);
+ $query .= " LIMIT $offset, $limit";
+ }
+
+ $dt = get_data($query, $options['callback']);
+ return $dt;
+ } else {
+ $result = get_data_row($query);
+ return $result->calculation;
+ }
+}
+
+/**
+ * Returns an array of joins and wheres for use in metastrings.
+ *
+ * @note The $pairs is reserved for name/value pairs if we want to implement those.
+ *
+ * @param string $table The annotation or metadata table name or alias
+ * @param array $names An array of names
+ * @param array $values An array of values
+ * @param array $pairs Name / value pairs. Not currently used.
+ * @param array $ids Metastring IDs
+ * @param bool $case_sensitive Should name and values be case sensitive?
+ *
+ * @return array
+ * @access private
+ */
+function elgg_get_metastring_sql($table, $names = null, $values = null,
+ $pairs = null, $ids = null, $case_sensitive = false) {
+
+ if ((!$names && $names !== 0)
+ && (!$values && $values !== 0)
+ && !$ids
+ && (!$pairs && $pairs !== 0)) {
+
+ return array();
+ }
+
+ $db_prefix = elgg_get_config('dbprefix');
+
+ // binary forces byte-to-byte comparision of strings, making
+ // it case- and diacritical-mark- sensitive.
+ // only supported on values.
+ $binary = ($case_sensitive) ? ' BINARY ' : '';
+
+ $return = array (
+ 'joins' => array (),
+ 'wheres' => array()
+ );
+
+ $wheres = array();
+
+ // get names wheres and joins
+ $names_where = '';
+ if ($names !== NULL) {
+ if (!is_array($names)) {
+ $names = array($names);
+ }
+
+ $sanitised_names = array();
+ foreach ($names as $name) {
+ // normalise to 0.
+ if (!$name) {
+ $name = '0';
+ }
+ $sanitised_names[] = '\'' . sanitise_string($name) . '\'';
+ }
+
+ if ($names_str = implode(',', $sanitised_names)) {
+ $return['joins'][] = "JOIN {$db_prefix}metastrings msn on $table.name_id = msn.id";
+ $names_where = "(msn.string IN ($names_str))";
+ }
+ }
+
+ // get values wheres and joins
+ $values_where = '';
+ if ($values !== NULL) {
+ if (!is_array($values)) {
+ $values = array($values);
+ }
+
+ $sanitised_values = array();
+ foreach ($values as $value) {
+ // normalize to 0
+ if (!$value) {
+ $value = 0;
+ }
+ $sanitised_values[] = '\'' . sanitise_string($value) . '\'';
+ }
+
+ if ($values_str = implode(',', $sanitised_values)) {
+ $return['joins'][] = "JOIN {$db_prefix}metastrings msv on $table.value_id = msv.id";
+ $values_where = "({$binary}msv.string IN ($values_str))";
+ }
+ }
+
+ if ($ids !== NULL) {
+ if (!is_array($ids)) {
+ $ids = array($ids);
+ }
+
+ $ids_str = implode(',', $ids);
+
+ if ($ids_str) {
+ $wheres[] = "n_table.id IN ($ids_str)";
+ }
+ }
+
+ if ($names_where && $values_where) {
+ $wheres[] = "($names_where AND $values_where)";
+ } elseif ($names_where) {
+ $wheres[] = $names_where;
+ } elseif ($values_where) {
+ $wheres[] = $values_where;
+ }
+
+ $wheres[] = get_access_sql_suffix($table);
+
+ if ($where = implode(' AND ', $wheres)) {
+ $return['wheres'][] = "($where)";
+ }
+
+ return $return;
+}
+
+/**
+ * Normalizes metadata / annotation option names to their corresponding metastrings name.
+ *
+ * @param array $options An options array
+ * @since 1.8.0
+ * @return array
+ * @access private
+ */
+function elgg_normalize_metastrings_options(array $options = array()) {
+
+ // support either metastrings_type or metastring_type
+ // because I've made this mistake many times and hunting it down is a pain...
+ $type = elgg_extract('metastring_type', $options, null);
+ $type = elgg_extract('metastrings_type', $options, $type);
+
+ $options['metastring_type'] = $type;
+
+ // support annotation_ and annotations_ because they're way too easy to confuse
+ $prefixes = array('metadata_', 'annotation_', 'annotations_');
+
+ // map the metadata_* options to metastring_* options
+ $map = array(
+ 'names' => 'metastring_names',
+ 'values' => 'metastring_values',
+ 'case_sensitive' => 'metastring_case_sensitive',
+ 'owner_guids' => 'metastring_owner_guids',
+ 'created_time_lower' => 'metastring_created_time_lower',
+ 'created_time_upper' => 'metastring_created_time_upper',
+ 'calculation' => 'metastring_calculation',
+ 'ids' => 'metastring_ids'
+ );
+
+ foreach ($prefixes as $prefix) {
+ $singulars = array("{$prefix}name", "{$prefix}value", "{$prefix}owner_guid", "{$prefix}id");
+ $options = elgg_normalise_plural_options_array($options, $singulars);
+
+ foreach ($map as $specific => $normalized) {
+ $key = $prefix . $specific;
+ if (isset($options[$key])) {
+ $options[$normalized] = $options[$key];
+ }
+ }
+ }
+
+ return $options;
+}
+
+/**
+ * Enables or disables a metastrings-based object by its id.
+ *
+ * @warning To enable disabled metastrings you must first use
+ * {@link access_show_hidden_entities()}.
+ *
+ * @param int $id The object's ID
+ * @param string $enabled Value to set to: yes or no
+ * @param string $type The type of table to use: metadata or annotations
+ *
+ * @return bool
+ * @throws InvalidParameterException
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_set_metastring_based_object_enabled_by_id($id, $enabled, $type) {
+ $id = (int)$id;
+ $db_prefix = elgg_get_config('dbprefix');
+
+ $object = elgg_get_metastring_based_object_from_id($id, $type);
+
+ switch($type) {
+ case 'annotation':
+ case 'annotations':
+ $table = "{$db_prefix}annotations";
+ break;
+
+ case 'metadata':
+ $table = "{$db_prefix}metadata";
+ break;
+ }
+
+ if ($enabled === 'yes' || $enabled === 1 || $enabled === true) {
+ $enabled = 'yes';
+ $event = 'enable';
+ } elseif ($enabled === 'no' || $enabled === 0 || $enabled === false) {
+ $enabled = 'no';
+ $event = 'disable';
+ } else {
+ return false;
+ }
+
+ $return = false;
+
+ if ($object) {
+ // don't set it if it's already set.
+ if ($object->enabled == $enabled) {
+ $return = false;
+ } elseif ($object->canEdit() && (elgg_trigger_event($event, $type, $object))) {
+ $return = update_data("UPDATE $table SET enabled = '$enabled' where id = $id");
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Runs metastrings-based objects found using $options through $callback
+ *
+ * @warning Unlike elgg_get_metastring_based_objects() this will not accept an
+ * empty options array!
+ *
+ * @warning This returns null on no ops.
+ *
+ * @param array $options An options array. {@See elgg_get_metastring_based_objects()}
+ * @param string $callback The callback to pass each result through
+ * @param bool $inc_offset Increment the offset? Pass false for callbacks that delete / disable
+ *
+ * @return bool|null true on success, false on failure, null if no objects are found.
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_batch_metastring_based_objects(array $options, $callback, $inc_offset = true) {
+ if (!$options || !is_array($options)) {
+ return false;
+ }
+
+ $batch = new ElggBatch('elgg_get_metastring_based_objects', $options, $callback, 50, $inc_offset);
+ return $batch->callbackResult;
+}
+
+/**
+ * Returns a singular metastring-based object by its ID.
+ *
+ * @param int $id The metastring-based object's ID
+ * @param string $type The type: annotation or metadata
+ * @return ElggMetadata|ElggAnnotation
+ *
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_get_metastring_based_object_from_id($id, $type) {
+ $id = (int)$id;
+ if (!$id) {
+ return false;
+ }
+
+ $options = array(
+ 'metastring_type' => $type,
+ 'metastring_id' => $id
+ );
+
+ $obj = elgg_get_metastring_based_objects($options);
+
+ if ($obj && count($obj) == 1) {
+ return $obj[0];
+ }
+
+ return false;
+}
+
+/**
+ * Deletes a metastring-based object by its id
+ *
+ * @param int $id The object's ID
+ * @param string $type The object's metastring type: annotation or metadata
+ * @return bool
+ *
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_delete_metastring_based_object_by_id($id, $type) {
+ $id = (int)$id;
+ $db_prefix = elgg_get_config('dbprefix');
+
+ switch ($type) {
+ case 'annotation':
+ case 'annotations':
+ $type = 'annotations';
+ break;
+
+ case 'metadata':
+ $type = 'metadata';
+ break;
+
+ default:
+ return false;
+ }
+
+ $obj = elgg_get_metastring_based_object_from_id($id, $type);
+ $table = $db_prefix . $type;
+
+ if ($obj) {
+ // Tidy up if memcache is enabled.
+ // @todo only metadata is supported
+ if ($type == 'metadata') {
+ static $metabyname_memcache;
+ if ((!$metabyname_memcache) && (is_memcache_available())) {
+ $metabyname_memcache = new ElggMemcache('metabyname_memcache');
+ }
+
+ if ($metabyname_memcache) {
+ // @todo why name_id? is that even populated?
+ $metabyname_memcache->delete("{$obj->entity_guid}:{$obj->name_id}");
+ }
+ }
+
+ if (($obj->canEdit()) && (elgg_trigger_event('delete', $type, $obj))) {
+ return (bool)delete_data("DELETE from $table where id=$id");
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Entities interface helpers
+ */
+
+/**
+ * Returns options to pass to elgg_get_entities() for metastrings operations.
+ *
+ * @param string $type Metastring type: annotations or metadata
+ * @param array $options Options
+ *
+ * @return array
+ * @since 1.7.0
+ * @access private
+ */
+function elgg_entities_get_metastrings_options($type, $options) {
+ $valid_types = array('metadata', 'annotation');
+ if (!in_array($type, $valid_types)) {
+ return FALSE;
+ }
+
+ // the options for annotations are singular (annotation_name) but the table
+ // is plural (elgg_annotations) so rewrite for the table name.
+ $n_table = ($type == 'annotation') ? 'annotations' : $type;
+
+ $singulars = array("{$type}_name", "{$type}_value",
+ "{$type}_name_value_pair", "{$type}_owner_guid");
+ $options = elgg_normalise_plural_options_array($options, $singulars);
+
+ $clauses = elgg_get_entity_metadata_where_sql('e', $n_table, $options["{$type}_names"],
+ $options["{$type}_values"], $options["{$type}_name_value_pairs"],
+ $options["{$type}_name_value_pairs_operator"], $options["{$type}_case_sensitive"],
+ $options["order_by_{$type}"], $options["{$type}_owner_guids"]);
+
+ if ($clauses) {
+ // merge wheres to pass to get_entities()
+ if (isset($options['wheres']) && !is_array($options['wheres'])) {
+ $options['wheres'] = array($options['wheres']);
+ } elseif (!isset($options['wheres'])) {
+ $options['wheres'] = array();
+ }
+
+ $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']);
+
+ // merge joins to pass to get_entities()
+ if (isset($options['joins']) && !is_array($options['joins'])) {
+ $options['joins'] = array($options['joins']);
+ } elseif (!isset($options['joins'])) {
+ $options['joins'] = array();
+ }
+
+ $options['joins'] = array_merge($options['joins'], $clauses['joins']);
+
+ if ($clauses['orders']) {
+ $order_by_metadata = implode(", ", $clauses['orders']);
+ if (isset($options['order_by']) && $options['order_by']) {
+ $options['order_by'] = "$order_by_metadata, {$options['order_by']}";
+ } else {
+ $options['order_by'] = "$order_by_metadata, e.time_created DESC";
+ }
+ }
+ }
+
+ return $options;
+}
+
+// unit testing
+elgg_register_plugin_hook_handler('unit_test', 'system', 'metastrings_test');
+
+/**
+ * Metadata unit test
+ *
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of other tests
+ * @param mixed $params Params
+ *
+ * @return array
+ * @access private
+ */
+function metastrings_test($hook, $type, $value, $params) {
+ global $CONFIG;
+ $value[] = $CONFIG->path . 'engine/tests/api/metastrings.php';
+ return $value;
+}
diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php
new file mode 100644
index 000000000..ab9cc05e8
--- /dev/null
+++ b/engine/lib/navigation.php
@@ -0,0 +1,527 @@
+<?php
+/**
+ * Elgg navigation library
+ * Functions for managing menus and other navigational elements
+ *
+ * Breadcrumbs
+ * Elgg uses a breadcrumb stack. The page handlers (controllers in MVC terms)
+ * push the breadcrumb links onto the stack. @see elgg_push_breadcrumb()
+ *
+ *
+ * Pagination
+ * Automatically handled by Elgg when using elgg_list_entities* functions.
+ * @see elgg_list_entities()
+ *
+ *
+ * Tabs
+ * @see navigation/tabs view
+ *
+ *
+ * Menus
+ * Elgg uses a single interface to manage its menus. Menu items are added with
+ * {@link elgg_register_menu_item()}. This is generally used for menus that
+ * appear only once per page. For dynamic menus (such as the hover
+ * menu for user's avatar), a plugin hook is emitted when the menu is being
+ * created. The hook is 'register', 'menu:<menu_name>'. For more details on this,
+ * @see elgg_view_menu().
+ *
+ * Menus supported by the Elgg core
+ * Standard menus:
+ * site Site navigation shown on every page.
+ * page Page menu usually shown in a sidebar. Uses Elgg's context.
+ * topbar Topbar menu shown on every page. The default has two sections.
+ * footer Like the topbar but in the footer.
+ * extras Links about content on the page. The RSS link is added to this.
+ *
+ * Dynamic menus (also called just-in-time menus):
+ * user_hover Avatar hover menu. The user entity is passed as a parameter.
+ * entity The set of links shown in the summary of an entity.
+ * river Links shown on river items.
+ * owner_block Links shown for a user or group in their owner block.
+ * filter The tab filter for content (all, mine, friends)
+ * title The buttons shown next to a content title.
+ * long-text The links shown above the input/longtext view.
+ *
+ * @package Elgg.Core
+ * @subpackage Navigation
+ */
+
+/**
+ * Register an item for an Elgg menu
+ *
+ * @warning Generally you should not use this in response to the plugin hook:
+ * 'register', 'menu:<menu_name>'. If you do, you may end up with many incorrect
+ * links on a dynamic menu.
+ *
+ * @warning A menu item's name must be unique per menu. If more than one menu
+ * item with the same name are registered, the last menu item takes priority.
+ *
+ * @see elgg_view_menu() for the plugin hooks available for modifying a menu as
+ * it is being rendered.
+ *
+ * @param string $menu_name The name of the menu: site, page, userhover,
+ * userprofile, groupprofile, or any custom menu
+ * @param mixed $menu_item A ElggMenuItem object or an array of options in format:
+ * name => STR Menu item identifier (required)
+ * text => STR Menu item display text (required)
+ * href => STR Menu item URL (required) (false for non-links.
+ * @warning If you disable the href the <a> tag will
+ * not appear, so the link_class will not apply. If you
+ * put <a> tags in manually through the 'text' option
+ * the default CSS selector .elgg-menu-$menu > li > a
+ * may affect formatting. Wrap in a <span> if it does.)
+ * contexts => ARR Page context strings
+ * section => STR Menu section identifier
+ * title => STR Menu item tooltip
+ * selected => BOOL Is this menu item currently selected
+ * parent_name => STR Identifier of the parent menu item
+ * link_class => STR A class or classes for the <a> tag
+ * item_class => STR A class or classes for the <li> tag
+ *
+ * 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
+ */
+function elgg_register_menu_item($menu_name, $menu_item) {
+ global $CONFIG;
+
+ if (!isset($CONFIG->menus[$menu_name])) {
+ $CONFIG->menus[$menu_name] = array();
+ }
+
+ if (is_array($menu_item)) {
+ $item = ElggMenuItem::factory($menu_item);
+ if (!$item) {
+ elgg_log("Unable to add menu item '{$menu_item['name']}' to '$menu_name' menu", 'WARNING');
+ elgg_log(print_r($menu_item, true), 'DEBUG');
+ return false;
+ }
+ } else {
+ $item = $menu_item;
+ }
+
+ $CONFIG->menus[$menu_name][] = $item;
+ return true;
+}
+
+/**
+ * Remove an item from a menu
+ *
+ * @param string $menu_name The name of the menu
+ * @param string $item_name The unique identifier for this menu item
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+function elgg_unregister_menu_item($menu_name, $item_name) {
+ global $CONFIG;
+
+ if (!isset($CONFIG->menus[$menu_name])) {
+ return false;
+ }
+
+ foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) {
+ /* @var ElggMenuItem $menu_object */
+ if ($menu_object->getName() == $item_name) {
+ unset($CONFIG->menus[$menu_name][$index]);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Check if a menu item has been registered
+ *
+ * @param string $menu_name The name of the menu
+ * @param string $item_name The unique identifier for this menu item
+ *
+ * @return bool
+ * @since 1.8.0
+ */
+function elgg_is_menu_item_registered($menu_name, $item_name) {
+ global $CONFIG;
+
+ if (!isset($CONFIG->menus[$menu_name])) {
+ return false;
+ }
+
+ foreach ($CONFIG->menus[$menu_name] as $menu_object) {
+ /* @var ElggMenuItem $menu_object */
+ if ($menu_object->getName() == $item_name) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Convenience function for registering a button to title menu
+ *
+ * The URL must be $handler/$name/$guid where $guid is the guid of the page owner.
+ * The label of the button is "$handler:$name" so that must be defined in a
+ * language file.
+ *
+ * This is used primarily to support adding an add content button
+ *
+ * @param string $handler The handler to use or null to autodetect from context
+ * @param string $name Name of the button
+ * @return void
+ * @since 1.8.0
+ */
+function elgg_register_title_button($handler = null, $name = 'add') {
+ if (elgg_is_logged_in()) {
+
+ if (!$handler) {
+ $handler = elgg_get_context();
+ }
+
+ $owner = elgg_get_page_owner_entity();
+ if (!$owner) {
+ // no owns the page so this is probably an all site list page
+ $owner = elgg_get_logged_in_user_entity();
+ }
+ if ($owner && $owner->canWriteToContainer()) {
+ $guid = $owner->getGUID();
+ elgg_register_menu_item('title', array(
+ 'name' => $name,
+ 'href' => "$handler/$name/$guid",
+ 'text' => elgg_echo("$handler:$name"),
+ 'link_class' => 'elgg-button elgg-button-action',
+ ));
+ }
+ }
+}
+
+/**
+ * Adds a breadcrumb to the breadcrumbs stack.
+ *
+ * @param string $title The title to display
+ * @param string $link Optional. The link for the title.
+ *
+ * @return void
+ * @since 1.8.0
+ *
+ * @link http://docs.elgg.org/Tutorials/UI/Breadcrumbs
+ */
+function elgg_push_breadcrumb($title, $link = NULL) {
+ global $CONFIG;
+ if (!isset($CONFIG->breadcrumbs)) {
+ $CONFIG->breadcrumbs = array();
+ }
+
+ // avoid key collisions.
+ $CONFIG->breadcrumbs[] = array('title' => elgg_get_excerpt($title, 100), 'link' => $link);
+}
+
+/**
+ * Removes last breadcrumb entry.
+ *
+ * @return array popped item.
+ * @since 1.8.0
+ * @link http://docs.elgg.org/Tutorials/UI/Breadcrumbs
+ */
+function elgg_pop_breadcrumb() {
+ global $CONFIG;
+
+ if (is_array($CONFIG->breadcrumbs)) {
+ return array_pop($CONFIG->breadcrumbs);
+ }
+
+ return FALSE;
+}
+
+/**
+ * Returns all breadcrumbs as an array of array('title' => 'Readable Title', 'link' => 'URL')
+ *
+ * @return array Breadcrumbs
+ * @since 1.8.0
+ * @link http://docs.elgg.org/Tutorials/UI/Breadcrumbs
+ */
+function elgg_get_breadcrumbs() {
+ global $CONFIG;
+
+ if (isset($CONFIG->breadcrumbs) && is_array($CONFIG->breadcrumbs)) {
+ return $CONFIG->breadcrumbs;
+ }
+
+ return array();
+}
+
+/**
+ * Set up the site menu
+ *
+ * Handles default, featured, and custom menu items
+ *
+ * @param string $hook
+ * @param string $type
+ * @param array $return Menu array
+ * @param array $params
+ * @return array
+ * @access private
+ */
+function elgg_site_menu_setup($hook, $type, $return, $params) {
+
+ $featured_menu_names = elgg_get_config('site_featured_menu_names');
+ $custom_menu_items = elgg_get_config('site_custom_menu_items');
+ if ($featured_menu_names || $custom_menu_items) {
+ // we have featured or custom menu items
+
+ $registered = $return['default'];
+
+ // set up featured menu items
+ $featured = array();
+ foreach ($featured_menu_names as $name) {
+ foreach ($registered as $index => $item) {
+ if ($item->getName() == $name) {
+ $featured[] = $item;
+ unset($registered[$index]);
+ }
+ }
+ }
+
+ // add custom menu items
+ $n = 1;
+ foreach ($custom_menu_items as $title => $url) {
+ $item = new ElggMenuItem("custom$n", $title, $url);
+ $featured[] = $item;
+ $n++;
+ }
+
+ $return['default'] = $featured;
+ if (count($registered) > 0) {
+ $return['more'] = $registered;
+ }
+ } else {
+ // no featured menu items set
+ $max_display_items = 5;
+
+ // the first n are shown, rest added to more list
+ // if only one item on more menu, stick it with the rest
+ $num_menu_items = count($return['default']);
+ if ($num_menu_items > ($max_display_items + 1)) {
+ $return['more'] = array_splice($return['default'], $max_display_items);
+ }
+ }
+
+ // check if we have anything selected
+ $selected = false;
+ foreach ($return as $section) {
+ foreach ($section as $item) {
+ if ($item->getSelected()) {
+ $selected = true;
+ break 2;
+ }
+ }
+ }
+
+ if (!$selected) {
+ // nothing selected, match name to context or match url
+ $current_url = current_page_url();
+ foreach ($return as $section_name => $section) {
+ foreach ($section as $key => $item) {
+ // only highlight internal links
+ if (strpos($item->getHref(), elgg_get_site_url()) === 0) {
+ if ($item->getName() == elgg_get_context()) {
+ $return[$section_name][$key]->setSelected(true);
+ break 2;
+ }
+ if ($item->getHref() == $current_url) {
+ $return[$section_name][$key]->setSelected(true);
+ break 2;
+ }
+ }
+ }
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Add the comment and like links to river actions menu
+ * @access private
+ */
+function elgg_river_menu_setup($hook, $type, $return, $params) {
+ if (elgg_is_logged_in()) {
+ $item = $params['item'];
+ /* @var ElggRiverItem $item */
+ $object = $item->getObjectEntity();
+ // comments and non-objects cannot be commented on or liked
+ if (!elgg_in_context('widgets') && $item->annotation_id == 0) {
+ // comments
+ if ($object->canComment()) {
+ $options = array(
+ 'name' => 'comment',
+ 'href' => "#comments-add-$object->guid",
+ 'text' => elgg_view_icon('speech-bubble'),
+ 'title' => elgg_echo('comment:this'),
+ 'rel' => 'toggle',
+ 'priority' => 50,
+ );
+ $return[] = ElggMenuItem::factory($options);
+ }
+ }
+
+ if (elgg_is_admin_logged_in()) {
+ $options = array(
+ 'name' => 'delete',
+ 'href' => elgg_add_action_tokens_to_url("action/river/delete?id=$item->id"),
+ 'text' => elgg_view_icon('delete'),
+ 'title' => elgg_echo('delete'),
+ 'confirm' => elgg_echo('deleteconfirm'),
+ 'priority' => 200,
+ );
+ $return[] = ElggMenuItem::factory($options);
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Entity menu is list of links and info on any entity
+ * @access private
+ */
+function elgg_entity_menu_setup($hook, $type, $return, $params) {
+ if (elgg_in_context('widgets')) {
+ return $return;
+ }
+
+ $entity = $params['entity'];
+ /* @var ElggEntity $entity */
+ $handler = elgg_extract('handler', $params, false);
+
+ // access
+ $access = elgg_view('output/access', array('entity' => $entity));
+ $options = array(
+ 'name' => 'access',
+ 'text' => $access,
+ 'href' => false,
+ 'priority' => 100,
+ );
+ $return[] = ElggMenuItem::factory($options);
+
+ if ($entity->canEdit() && $handler) {
+ // edit link
+ $options = array(
+ 'name' => 'edit',
+ 'text' => elgg_echo('edit'),
+ 'title' => elgg_echo('edit:this'),
+ 'href' => "$handler/edit/{$entity->getGUID()}",
+ 'priority' => 200,
+ );
+ $return[] = ElggMenuItem::factory($options);
+
+ // delete link
+ $options = array(
+ 'name' => 'delete',
+ 'text' => elgg_view_icon('delete'),
+ 'title' => elgg_echo('delete:this'),
+ 'href' => "action/$handler/delete?guid={$entity->getGUID()}",
+ 'confirm' => elgg_echo('deleteconfirm'),
+ 'priority' => 300,
+ );
+ $return[] = ElggMenuItem::factory($options);
+ }
+
+ return $return;
+}
+
+/**
+ * Widget menu is a set of widget controls
+ * @access private
+ */
+function elgg_widget_menu_setup($hook, $type, $return, $params) {
+
+ $widget = $params['entity'];
+ /* @var ElggWidget $widget */
+ $show_edit = elgg_extract('show_edit', $params, true);
+
+ $collapse = array(
+ 'name' => 'collapse',
+ 'text' => ' ',
+ 'href' => "#elgg-widget-content-$widget->guid",
+ 'class' => 'elgg-widget-collapse-button',
+ 'rel' => 'toggle',
+ 'priority' => 1
+ );
+ $return[] = ElggMenuItem::factory($collapse);
+
+ if ($widget->canEdit()) {
+ $delete = array(
+ 'name' => 'delete',
+ 'text' => elgg_view_icon('delete-alt'),
+ 'title' => elgg_echo('widget:delete', array($widget->getTitle())),
+ 'href' => "action/widgets/delete?widget_guid=$widget->guid",
+ 'is_action' => true,
+ 'class' => 'elgg-widget-delete-button',
+ 'id' => "elgg-widget-delete-button-$widget->guid",
+ 'priority' => 900
+ );
+ $return[] = ElggMenuItem::factory($delete);
+
+ if ($show_edit) {
+ $edit = array(
+ 'name' => 'settings',
+ 'text' => elgg_view_icon('settings-alt'),
+ 'title' => elgg_echo('widget:edit'),
+ 'href' => "#widget-edit-$widget->guid",
+ 'class' => "elgg-widget-edit-button",
+ 'rel' => 'toggle',
+ 'priority' => 800,
+ );
+ $return[] = ElggMenuItem::factory($edit);
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Adds a delete link to "generic_comment" annotations
+ * @access private
+ */
+function elgg_annotation_menu_setup($hook, $type, $return, $params) {
+ $annotation = $params['annotation'];
+ /* @var ElggAnnotation $annotation */
+
+ if ($annotation->name == 'generic_comment' && $annotation->canEdit()) {
+ $url = elgg_http_add_url_query_elements('action/comments/delete', array(
+ 'annotation_id' => $annotation->id,
+ ));
+
+ $options = array(
+ 'name' => 'delete',
+ 'href' => $url,
+ 'text' => "<span class=\"elgg-icon elgg-icon-delete\"></span>",
+ 'confirm' => elgg_echo('deleteconfirm'),
+ 'encode_text' => false
+ );
+ $return[] = ElggMenuItem::factory($options);
+ }
+
+ return $return;
+}
+
+
+/**
+ * Navigation initialization
+ * @access private
+ */
+function elgg_nav_init() {
+ elgg_register_plugin_hook_handler('prepare', 'menu:site', 'elgg_site_menu_setup');
+ elgg_register_plugin_hook_handler('register', 'menu:river', 'elgg_river_menu_setup');
+ elgg_register_plugin_hook_handler('register', 'menu:entity', 'elgg_entity_menu_setup');
+ elgg_register_plugin_hook_handler('register', 'menu:widget', 'elgg_widget_menu_setup');
+ elgg_register_plugin_hook_handler('register', 'menu:annotation', 'elgg_annotation_menu_setup');
+}
+
+elgg_register_event_handler('init', 'system', 'elgg_nav_init');
diff --git a/engine/lib/notification.php b/engine/lib/notification.php
index 7f949c32f..be0c359d4 100644
--- a/engine/lib/notification.php
+++ b/engine/lib/notification.php
@@ -3,38 +3,42 @@
* Notifications
* This file contains classes and functions which allow plugins to register and send notifications.
*
- * There are notification methods which are provided out of the box (see notification_init() ). Each method
- * is identified by a string, e.g. "email".
+ * There are notification methods which are provided out of the box
+ * (see notification_init() ). Each method is identified by a string, e.g. "email".
*
- * To register an event use register_notification_handler() and pass the method name and a handler function.
+ * To register an event use register_notification_handler() and pass the method name and a
+ * handler function.
*
- * To send a notification call notify() passing it the method you wish to use combined with a number of method
- * specific addressing parameters.
+ * To send a notification call notify() passing it the method you wish to use combined with a
+ * number of method specific addressing parameters.
*
* Catch NotificationException to trap errors.
*
- * @package Elgg
- * @subpackage API
-
- * @author Curverider Ltd
-
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Notifications
*/
/** Notification handlers */
+global $NOTIFICATION_HANDLERS;
$NOTIFICATION_HANDLERS = array();
/**
* This function registers a handler for a given notification type (eg "email")
*
- * @param string $method The method
- * @param string $handler The handler function, in the format "handler(ElggEntity $from, ElggUser $to, $subject, $message, array $params = NULL)". This function should return false on failure, and true/a tracking message ID on success.
- * @param array $params A associated array of other parameters for this handler defining some properties eg. supported message length or rich text support.
+ * @param string $method The method
+ * @param string $handler The handler function, in the format
+ * "handler(ElggEntity $from, ElggUser $to, $subject,
+ * $message, array $params = NULL)". This function should
+ * return false on failure, and true/a tracking message ID on success.
+ * @param array $params An associated array of other parameters for this handler
+ * defining some properties eg. supported msg length or rich text support.
+ *
+ * @return bool
*/
function register_notification_handler($method, $handler, $params = NULL) {
global $NOTIFICATION_HANDLERS;
- if (is_callable($handler)) {
+ if (is_callable($handler, true)) {
$NOTIFICATION_HANDLERS[$method] = new stdClass;
$NOTIFICATION_HANDLERS[$method]->handler = $handler;
@@ -54,6 +58,9 @@ function register_notification_handler($method, $handler, $params = NULL) {
* This function unregisters a handler for a given notification type (eg "email")
*
* @param string $method The method
+ *
+ * @return void
+ * @since 1.7.1
*/
function unregister_notification_handler($method) {
global $NOTIFICATION_HANDLERS;
@@ -66,18 +73,20 @@ function unregister_notification_handler($method) {
/**
* Notify a user via their preferences.
*
- * @param mixed $to Either a guid or an array of guid's to notify.
- * @param int $from GUID of the sender, which may be a user, site or object.
- * @param string $subject Message subject.
- * @param string $message Message body.
- * @param array $params Misc additional parameters specific to various methods.
- * @param mixed $methods_override A string, or an array of strings specifying the delivery methods to use - or leave blank
- * for delivery using the user's chosen delivery methods.
+ * @param mixed $to Either a guid or an array of guid's to notify.
+ * @param int $from GUID of the sender, which may be a user, site or object.
+ * @param string $subject Message subject.
+ * @param string $message Message body.
+ * @param array $params Misc additional parameters specific to various methods.
+ * @param mixed $methods_override A string, or an array of strings specifying the delivery
+ * methods to use - or leave blank for delivery using the
+ * user's chosen delivery methods.
+ *
* @return array Compound array of each delivery user/delivery method's success or failure.
* @throws NotificationException
*/
function notify_user($to, $from, $subject, $message, array $params = NULL, $methods_override = "") {
- global $NOTIFICATION_HANDLERS, $CONFIG;
+ global $NOTIFICATION_HANDLERS;
// Sanitise
if (!is_array($to)) {
@@ -101,12 +110,15 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth
// Are we overriding delivery?
$methods = $methods_override;
if (!$methods) {
- $tmp = (array)get_user_notification_settings($guid);
+ $tmp = get_user_notification_settings($guid);
$methods = array();
- foreach($tmp as $k => $v) {
- // Add method if method is turned on for user!
- if ($v) {
- $methods[] = $k;
+ // $tmp may be false. don't cast
+ if (is_object($tmp)) {
+ foreach ($tmp as $k => $v) {
+ // Add method if method is turned on for user!
+ if ($v) {
+ $methods[] = $k;
+ }
}
}
}
@@ -122,16 +134,17 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth
// Extract method details from list
$details = $NOTIFICATION_HANDLERS[$method];
$handler = $details->handler;
+ /* @var callable $handler */
- if ((!$NOTIFICATION_HANDLERS[$method]) || (!$handler)) {
- error_log(sprintf(elgg_echo('NotificationException:NoHandlerFound'), $method));
+ if ((!$NOTIFICATION_HANDLERS[$method]) || (!$handler) || (!is_callable($handler))) {
+ error_log(elgg_echo('NotificationException:NoHandlerFound', array($method)));
}
elgg_log("Sending message to $guid using $method");
// Trigger handler and retrieve result.
try {
- $result[$guid][$method] = $handler(
+ $result[$guid][$method] = call_user_func($handler,
$from ? get_entity($from) : NULL, // From entity
get_entity($guid), // To entity
$subject, // The subject
@@ -154,16 +167,22 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth
* Get the notification settings for a given user.
*
* @param int $user_guid The user id
- * @return stdClass
+ *
+ * @return stdClass|false
*/
function get_user_notification_settings($user_guid = 0) {
$user_guid = (int)$user_guid;
if ($user_guid == 0) {
- $user_guid = get_loggedin_userid();
+ $user_guid = elgg_get_logged_in_user_guid();
}
- $all_metadata = get_metadata_for_entity($user_guid);
+ // @todo: there should be a better way now that metadata is cached. E.g. just query for MD names, then
+ // query user object directly
+ $all_metadata = elgg_get_metadata(array(
+ 'guid' => $user_guid,
+ 'limit' => 0
+ ));
if ($all_metadata) {
$prefix = "notification:method:";
$return = new stdClass;
@@ -186,9 +205,10 @@ function get_user_notification_settings($user_guid = 0) {
/**
* Set a user notification pref.
*
- * @param int $user_guid The user id.
- * @param string $method The delivery method (eg. email)
- * @param bool $value On(true) or off(false).
+ * @param int $user_guid The user id.
+ * @param string $method The delivery method (eg. email)
+ * @param bool $value On(true) or off(false).
+ *
* @return bool
*/
function set_user_notification_setting($user_guid, $method, $value) {
@@ -197,7 +217,7 @@ function set_user_notification_setting($user_guid, $method, $value) {
$user = get_entity($user_guid);
if (!$user) {
- $user = get_loggedin_user();
+ $user = elgg_get_logged_in_user_entity();
}
if (($user) && ($user instanceof ElggUser)) {
@@ -212,42 +232,43 @@ function set_user_notification_setting($user_guid, $method, $value) {
}
/**
- * Notification exception.
- * @author Curverider Ltd
- */
-class NotificationException extends Exception {}
-
-
-/**
* Send a notification via email.
*
- * @param ElggEntity $from The from user/site/object
- * @param ElggUser $to To which user?
- * @param string $subject The subject of the message.
- * @param string $message The message body
- * @param array $params Optional parameters (none taken in this instance)
+ * @param ElggEntity $from The from user/site/object
+ * @param ElggUser $to To which user?
+ * @param string $subject The subject of the message.
+ * @param string $message The message body
+ * @param array $params Optional parameters (none taken in this instance)
+ *
* @return bool
+ * @throws NotificationException
+ * @access private
*/
-function email_notify_handler(ElggEntity $from, ElggUser $to, $subject, $message, array $params = NULL) {
+function email_notify_handler(ElggEntity $from, ElggUser $to, $subject, $message,
+array $params = NULL) {
+
global $CONFIG;
if (!$from) {
- throw new NotificationException(sprintf(elgg_echo('NotificationException:MissingParameter'), 'from'));
+ $msg = elgg_echo('NotificationException:MissingParameter', array('from'));
+ throw new NotificationException($msg);
}
if (!$to) {
- throw new NotificationException(sprintf(elgg_echo('NotificationException:MissingParameter'), 'to'));
+ $msg = elgg_echo('NotificationException:MissingParameter', array('to'));
+ throw new NotificationException($msg);
}
- if ($to->email=="") {
- throw new NotificationException(sprintf(elgg_echo('NotificationException:NoEmailAddress'), $to->guid));
+ if ($to->email == "") {
+ $msg = elgg_echo('NotificationException:NoEmailAddress', array($to->guid));
+ throw new NotificationException($msg);
}
// To
$to = $to->email;
// From
- $site = get_entity($CONFIG->site_guid);
+ $site = elgg_get_site_entity();
// If there's an email address, use it - but only if its not from a user.
if (!($from instanceof ElggUser) && $from->email) {
$from = $from->email;
@@ -265,31 +286,39 @@ function email_notify_handler(ElggEntity $from, ElggUser $to, $subject, $message
/**
* Send an email to any email address
*
- * @param string $from Email address or string: "name <email>"
- * @param string $to Email address or string: "name <email>"
+ * @param string $from Email address or string: "name <email>"
+ * @param string $to Email address or string: "name <email>"
* @param string $subject The subject of the message
- * @param string $body The message body
- * @param array $params Optional parameters (none used in this function)
+ * @param string $body The message body
+ * @param array $params Optional parameters (none used in this function)
+ *
* @return bool
+ * @throws NotificationException
+ * @since 1.7.2
*/
function elgg_send_email($from, $to, $subject, $body, array $params = NULL) {
global $CONFIG;
if (!$from) {
- throw new NotificationException(sprintf(elgg_echo('NotificationException:NoEmailAddress'), 'from'));
+ $msg = elgg_echo('NotificationException:MissingParameter', array('from'));
+ throw new NotificationException($msg);
}
if (!$to) {
- throw new NotificationException(sprintf(elgg_echo('NotificationException:NoEmailAddress'), 'to'));
+ $msg = elgg_echo('NotificationException:MissingParameter', array('to'));
+ throw new NotificationException($msg);
}
// return TRUE/FALSE to stop elgg_send_email() from sending
- $mail_params = array( 'to' => $to,
+ $mail_params = array(
+ 'to' => $to,
'from' => $from,
'subject' => $subject,
'body' => $body,
- 'params' => $params);
- $result = trigger_plugin_hook('email', 'system', $mail_params, NULL);
+ 'params' => $params
+ );
+
+ $result = elgg_trigger_plugin_hook('email', 'system', $mail_params, NULL);
if ($result !== NULL) {
return $result;
}
@@ -301,7 +330,7 @@ function elgg_send_email($from, $to, $subject, $body, array $params = NULL) {
}
// Windows is somewhat broken, so we use just address for to and from
- if (strtolower(substr(PHP_OS, 0 , 3)) == 'win') {
+ if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {
// strip name from to and from
if (strpos($to, '<')) {
preg_match('/<(.*)>/', $to, $matches);
@@ -321,13 +350,15 @@ function elgg_send_email($from, $to, $subject, $body, array $params = NULL) {
// Sanitise subject by stripping line endings
$subject = preg_replace("/(\r\n|\r|\n)/", " ", $subject);
+ // this is because Elgg encodes everything and matches what is done with body
+ $subject = html_entity_decode($subject, ENT_COMPAT, 'UTF-8'); // Decode any html entities
if (is_callable('mb_encode_mimeheader')) {
- $subject = mb_encode_mimeheader($subject,"UTF-8", "B");
+ $subject = mb_encode_mimeheader($subject, "UTF-8", "B");
}
// Format message
$body = html_entity_decode($body, ENT_COMPAT, 'UTF-8'); // Decode any html entities
- $body = strip_tags($body); // Strip tags from message
+ $body = elgg_strip_tags($body); // Strip tags from message
$body = preg_replace("/(\r\n|\r)/", "\n", $body); // Convert to unix line endings in body
$body = preg_replace("/^From/", ">From", $body); // Change lines starting with From to >From
@@ -337,32 +368,42 @@ function elgg_send_email($from, $to, $subject, $body, array $params = NULL) {
/**
* Correctly initialise notifications and register the email handler.
*
+ * @return void
+ * @access private
*/
function notification_init() {
// Register a notification handler for the default email method
register_notification_handler("email", "email_notify_handler");
// Add settings view to user settings & register action
- extend_elgg_settings_page('notifications/settings/usersettings', 'usersettings/user');
-
- register_plugin_hook('usersettings:save','user','notification_user_settings_save');
+ elgg_extend_view('forms/account/settings', 'core/settings/account/notifications');
- //register_action("notifications/settings/usersettings/save");
+ elgg_register_plugin_hook_handler('usersettings:save', 'user', 'notification_user_settings_save');
}
+/**
+ * Includes the action to save user notifications
+ *
+ * @return void
+ * @todo why can't this call action(...)?
+ * @access private
+ */
function notification_user_settings_save() {
global $CONFIG;
+ //@todo Wha??
include($CONFIG->path . "actions/notifications/settings/usersettings/save.php");
}
/**
* Register an entity type and subtype to be eligible for notifications
*
- * @param string $entity_type The type of entity
+ * @param string $entity_type The type of entity
* @param string $object_subtype Its subtype
- * @param string $english_name It's English notification string (eg "New blog post")
+ * @param string $language_name Its localized notification string (eg "New blog post")
+ *
+ * @return void
*/
-function register_notification_object($entity_type, $object_subtype, $english_name) {
+function register_notification_object($entity_type, $object_subtype, $language_name) {
global $CONFIG;
if ($entity_type == '') {
@@ -380,15 +421,16 @@ function register_notification_object($entity_type, $object_subtype, $english_na
$CONFIG->register_objects[$entity_type] = array();
}
- $CONFIG->register_objects[$entity_type][$object_subtype] = $english_name;
+ $CONFIG->register_objects[$entity_type][$object_subtype] = $language_name;
}
/**
* Establish a 'notify' relationship between the user and a content author
*
- * @param int $user_guid The GUID of the user who wants to follow a user's content
+ * @param int $user_guid The GUID of the user who wants to follow a user's content
* @param int $author_guid The GUID of the user whose content the user wants to follow
- * @return true|false Depending on success
+ *
+ * @return bool Depending on success
*/
function register_notification_interest($user_guid, $author_guid) {
return add_entity_relationship($user_guid, 'notify', $author_guid);
@@ -397,9 +439,10 @@ function register_notification_interest($user_guid, $author_guid) {
/**
* Remove a 'notify' relationship between the user and a content author
*
- * @param int $user_guid The GUID of the user who is following a user's content
+ * @param int $user_guid The GUID of the user who is following a user's content
* @param int $author_guid The GUID of the user whose content the user wants to unfollow
- * @return true|false Depending on success
+ *
+ * @return bool Depending on success
*/
function remove_notification_interest($user_guid, $author_guid) {
return remove_entity_relationship($user_guid, 'notify', $author_guid);
@@ -410,15 +453,23 @@ function remove_notification_interest($user_guid, $author_guid) {
* objects and attempts to send notifications to anybody who's interested
*
* @see register_notification_object
+ *
+ * @param string $event create
+ * @param string $object_type mixed
+ * @param mixed $object The object created
+ *
+ * @return bool
+ * @access private
*/
function object_notifications($event, $object_type, $object) {
// We only want to trigger notification events for ElggEntities
if ($object instanceof ElggEntity) {
+ /* @var ElggEntity $object */
// Get config data
global $CONFIG, $SESSION, $NOTIFICATION_HANDLERS;
- $hookresult = trigger_plugin_hook('object:notifications',$object_type,array(
+ $hookresult = elgg_trigger_plugin_hook('object:notifications', $object_type, array(
'event' => $event,
'object_type' => $object_type,
'object' => $object,
@@ -439,34 +490,37 @@ function object_notifications($event, $object_type, $object) {
}
if (isset($CONFIG->register_objects[$object_type][$object_subtype])) {
- $descr = $CONFIG->register_objects[$object_type][$object_subtype];
- $string = $descr . ": " . $object->getURL();
+ $subject = $CONFIG->register_objects[$object_type][$object_subtype];
+ $string = $subject . ": " . $object->getURL();
// Get users interested in content from this person and notify them
// (Person defined by container_guid so we can also subscribe to groups if we want)
- foreach($NOTIFICATION_HANDLERS as $method => $foo) {
+ foreach ($NOTIFICATION_HANDLERS as $method => $foo) {
$interested_users = elgg_get_entities_from_relationship(array(
+ 'site_guids' => ELGG_ENTITIES_ANY_VALUE,
'relationship' => 'notify' . $method,
- 'relationship_guid' => $object->container_guid,
+ 'relationship_guid' => $object->container_guid,
'inverse_relationship' => TRUE,
- 'types' => 'user',
- 'limit' => 99999
+ 'type' => 'user',
+ 'limit' => false
));
+ /* @var ElggUser[] $interested_users */
if ($interested_users && is_array($interested_users)) {
- foreach($interested_users as $user) {
+ foreach ($interested_users as $user) {
if ($user instanceof ElggUser && !$user->isBanned()) {
- if (($user->guid != $SESSION['user']->guid) && has_access_to_entity($object,$user)
+ if (($user->guid != $SESSION['user']->guid) && has_access_to_entity($object, $user)
&& $object->access_id != ACCESS_PRIVATE) {
- $methodstring = trigger_plugin_hook('notify:entity:message',$object->getType(),array(
+ $body = elgg_trigger_plugin_hook('notify:entity:message', $object->getType(), array(
'entity' => $object,
'to_entity' => $user,
- 'method' => $method),$string);
- if (empty($methodstring) && $methodstring !== false) {
- $methodstring = $string;
+ 'method' => $method), $string);
+ if (empty($body) && $body !== false) {
+ $body = $string;
}
- if ($methodstring !== false) {
- notify_user($user->guid,$object->container_guid,$descr,$methodstring,NULL,array($method));
+ if ($body !== false) {
+ notify_user($user->guid, $object->container_guid, $subject, $body,
+ null, array($method));
}
}
}
@@ -478,5 +532,5 @@ function object_notifications($event, $object_type, $object) {
}
// Register a startup event
-register_elgg_event_handler('init','system','notification_init',0);
-register_elgg_event_handler('create','object','object_notifications');
+elgg_register_event_handler('init', 'system', 'notification_init', 0);
+elgg_register_event_handler('create', 'object', 'object_notifications');
diff --git a/engine/lib/objects.php b/engine/lib/objects.php
index 6023a4805..ff3cc733f 100644
--- a/engine/lib/objects.php
+++ b/engine/lib/objects.php
@@ -1,217 +1,19 @@
<?php
-
/**
* Elgg objects
* Functions to manage multiple or single objects in an Elgg install
*
* @package Elgg
* @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-
-/**
- * ElggObject
- * Representation of an "object" in the system.
- *
- * @package Elgg
- * @subpackage Core
*/
-class ElggObject extends ElggEntity {
- /**
- * Initialise the attributes array.
- * This is vital to distinguish between metadata and base parameters.
- *
- * Place your base parameters here.
- */
- protected function initialise_attributes() {
- parent::initialise_attributes();
-
- $this->attributes['type'] = "object";
- $this->attributes['title'] = "";
- $this->attributes['description'] = "";
- $this->attributes['tables_split'] = 2;
- }
-
- /**
- * Construct a new object entity, optionally from a given id value.
- *
- * @param mixed $guid If an int, load that GUID.
- * If a db row then will attempt to load the rest of the data.
- * @throws Exception if there was a problem creating the object.
- */
- function __construct($guid = null) {
- $this->initialise_attributes();
-
- if (!empty($guid)) {
- // Is $guid is a DB row - either a entity row, or a object table row.
- if ($guid instanceof stdClass) {
- // Load the rest
- if (!$this->load($guid->guid)) {
- throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid->guid));
- }
- }
-
- // Is $guid is an ElggObject? Use a copy constructor
- else if ($guid instanceof ElggObject) {
- elgg_deprecated_notice('This type of usage of the ElggObject constructor was deprecated. Please use the clone method.', 1.7);
-
- foreach ($guid->attributes as $key => $value) {
- $this->attributes[$key] = $value;
- }
- }
-
- // Is this is an ElggEntity but not an ElggObject = ERROR!
- else if ($guid instanceof ElggEntity) {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggObject'));
- }
-
- // We assume if we have got this far, $guid is an int
- else if (is_numeric($guid)) {
- if (!$this->load($guid)) {
- throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid));
- }
- }
-
- else {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
- }
- }
- }
-
- /**
- * Override the load function.
- * This function will ensure that all data is loaded (were possible), so
- * if only part of the ElggObject is loaded, it'll load the rest.
- *
- * @param int $guid
- * @return true|false
- */
- protected function load($guid) {
- // Test to see if we have the generic stuff
- if (!parent::load($guid)) {
- return false;
- }
-
- // Check the type
- if ($this->attributes['type']!='object') {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, get_class()));
- }
-
- // Load missing data
- $row = get_object_entity_as_row($guid);
- if (($row) && (!$this->isFullyLoaded())) {
- // If $row isn't a cached copy then increment the counter
- $this->attributes['tables_loaded'] ++;
- }
-
- // Now put these into the attributes array as core values
- $objarray = (array) $row;
- foreach($objarray as $key => $value) {
- $this->attributes[$key] = $value;
- }
-
- return true;
- }
-
- /**
- * Override the save function.
- * @return true|false
- */
- public function save() {
- // Save generic stuff
- if (!parent::save()) {
- return false;
- }
-
- // Now save specific stuff
- return create_object_entity($this->get('guid'), $this->get('title'), $this->get('description'), $this->get('container_guid'));
- }
-
- /**
- * Get sites that this object is a member of
- *
- * @param string $subtype Optionally, the subtype of result we want to limit to
- * @param int $limit The number of results to return
- * @param int $offset Any indexing offset
- */
- function getSites($subtype="", $limit = 10, $offset = 0) {
- return get_site_objects($this->getGUID(), $subtype, $limit, $offset);
- }
-
- /**
- * Add this object to a particular site
- *
- * @param int $site_guid The guid of the site to add it to
- * @return true|false
- */
- function addToSite($site_guid) {
- return add_site_object($this->getGUID(), $site_guid);
- }
-
- /**
- * Set the container for this object.
- *
- * @param int $container_guid The ID of the container.
- * @return bool
- */
- function setContainer($container_guid) {
- $container_guid = (int)$container_guid;
-
- return $this->set('container_guid', $container_guid);
- }
-
- /**
- * Return the container GUID of this object.
- *
- * @return int
- */
- function getContainer() {
- return $this->get('container_guid');
- }
-
- /**
- * As getContainer(), but returns the whole entity.
- *
- * @return mixed ElggGroup object or false.
- */
- function getContainerEntity() {
- $result = get_entity($this->getContainer());
-
- if (($result) && ($result instanceof ElggGroup)) {
- return $result;
- }
-
- return false;
- }
-
- /**
- * Get the collections associated with a object.
- *
- * @param string $subtype Optionally, the subtype of result we want to limit to
- * @param int $limit The number of results to return
- * @param int $offset Any indexing offset
- * @return unknown
- */
- //public function getCollections($subtype="", $limit = 10, $offset = 0) { get_object_collections($this->getGUID(), $subtype, $limit, $offset); }
-
- // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an array of fields which can be exported.
- */
- public function getExportableValues() {
- return array_merge(parent::getExportableValues(), array(
- 'title',
- 'description',
- ));
- }
-}
/**
* Return the object specific details of a object by a row.
*
- * @param int $guid
+ * @param int $guid The guid to retreive
+ *
+ * @return bool
+ * @access private
*/
function get_object_entity_as_row($guid) {
global $CONFIG;
@@ -224,9 +26,12 @@ function get_object_entity_as_row($guid) {
* Create or update the extras table for a given object.
* Call create_entity first.
*
- * @param int $guid The guid of the entity you're creating (as obtained by create_entity)
- * @param string $title The title of the object
+ * @param int $guid The guid of the entity you're creating (as obtained by create_entity)
+ * @param string $title The title of the object
* @param string $description The object's description
+ *
+ * @return bool
+ * @access private
*/
function create_object_entity($guid, $title, $description) {
global $CONFIG;
@@ -239,27 +44,30 @@ function create_object_entity($guid, $title, $description) {
if ($row) {
// Core entities row exists and we have access to it
- if ($exists = get_data_row("SELECT guid from {$CONFIG->dbprefix}objects_entity where guid = {$guid}")) {
- $result = update_data("UPDATE {$CONFIG->dbprefix}objects_entity set title='$title', description='$description' where guid=$guid");
- if ($result!=false) {
+ $query = "SELECT guid from {$CONFIG->dbprefix}objects_entity where guid = {$guid}";
+ if ($exists = get_data_row($query)) {
+ $query = "UPDATE {$CONFIG->dbprefix}objects_entity
+ set title='$title', description='$description' where guid=$guid";
+
+ $result = update_data($query);
+ if ($result != false) {
// Update succeeded, continue
$entity = get_entity($guid);
- if (trigger_elgg_event('update',$entity->type,$entity)) {
- return $guid;
- } else {
- $entity->delete();
- }
+ elgg_trigger_event('update', $entity->type, $entity);
+ return $guid;
}
} else {
// Update failed, attempt an insert.
- $result = insert_data("INSERT into {$CONFIG->dbprefix}objects_entity (guid, title, description) values ($guid, '$title','$description')");
- if ($result!==false) {
+ $query = "INSERT into {$CONFIG->dbprefix}objects_entity
+ (guid, title, description) values ($guid, '$title','$description')";
+
+ $result = insert_data($query);
+ if ($result !== false) {
$entity = get_entity($guid);
- if (trigger_elgg_event('create',$entity->type,$entity)) {
+ if (elgg_trigger_event('create', $entity->type, $entity)) {
return $guid;
} else {
$entity->delete();
- //delete_entity($guid);
}
}
}
@@ -269,72 +77,12 @@ function create_object_entity($guid, $title, $description) {
}
/**
- * THIS FUNCTION IS DEPRECATED.
- *
- * Delete a object's extra data.
- *
- * @param int $guid
- */
-function delete_object_entity($guid) {
- system_message(sprintf(elgg_echo('deprecatedfunction'), 'delete_user_entity'));
-
- return 1; // Always return that we have deleted one row in order to not break existing code.
-}
-
-/**
- * Searches for an object based on a complete or partial title or description using full text searching.
- *
- * IMPORTANT NOTE: With MySQL's default setup:
- * 1) $criteria must be 4 or more characters long
- * 2) If $criteria matches greater than 50% of results NO RESULTS ARE RETURNED!
- *
- * @param string $criteria The partial or full name or username.
- * @param int $limit Limit of the search.
- * @param int $offset Offset.
- * @param string $order_by The order.
- * @param boolean $count Whether to return the count of results or just the results.
- * @deprecated 1.7
- */
-function search_for_object($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) {
- elgg_deprecated_notice('search_for_object() was deprecated by new search plugin.', 1.7);
- global $CONFIG;
-
- $criteria = sanitise_string($criteria);
- $limit = (int)$limit;
- $offset = (int)$offset;
- $order_by = sanitise_string($order_by);
- $container_guid = (int)$container_guid;
-
- $access = get_access_sql_suffix("e");
-
- if ($order_by == "") {
- $order_by = "e.time_created desc";
- }
-
- if ($count) {
- $query = "SELECT count(e.guid) as total ";
- } else {
- $query = "SELECT e.* ";
- }
- $query .= "from {$CONFIG->dbprefix}entities e join {$CONFIG->dbprefix}objects_entity o on e.guid=o.guid where match(o.title,o.description) against ('$criteria') and $access";
-
- 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 the sites this object is part of
*
* @param int $object_guid The object's GUID
- * @param int $limit Number of results to return
- * @param int $offset Any indexing offset
+ * @param int $limit Number of results to return
+ * @param int $offset Any indexing offset
+ *
* @return false|array On success, an array of ElggSites
*/
function get_object_sites($object_guid, $limit = 10, $offset = 0) {
@@ -345,14 +93,22 @@ function get_object_sites($object_guid, $limit = 10, $offset = 0) {
return elgg_get_entities_from_relationship(array(
'relationship' => 'member_of_site',
'relationship_guid' => $object_guid,
- 'types' => 'site',
+ 'type' => 'site',
'limit' => $limit,
- 'offset' => $offset
+ 'offset' => $offset,
));
}
/**
* Runs unit tests for ElggObject
+ *
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of tests
+ * @param mixed $params Params
+ *
+ * @return array
+ * @access private
*/
function objects_test($hook, $type, $value, $params) {
global $CONFIG;
@@ -360,34 +116,5 @@ function objects_test($hook, $type, $value, $params) {
return $value;
}
-
-/**
- * Returns a formatted list of objects suitable for injecting into search.
- * @deprecated 1.7
- *
- */
-function search_list_objects_by_name($hook, $user, $returnvalue, $tag) {
- elgg_deprecated_notice('search_list_objects_by_name was deprecated by new search plugin.', 1.7);
-
- // Change this to set the number of users that display on the search page
- $threshold = 4;
-
- $object = get_input('object');
-
- if (!get_input('offset') && (empty($object) || $object == 'user')) {
- if ($users = search_for_user($tag,$threshold)) {
- $countusers = search_for_user($tag,0,0,"",true);
-
- $return = elgg_view('user/search/startblurb',array('count' => $countusers, 'tag' => $tag));
- foreach($users as $user) {
- $return .= elgg_view_entity($user);
- }
- $return .= elgg_view('user/search/finishblurb',array('count' => $countusers, 'threshold' => $threshold, 'tag' => $tag));
- return $return;
-
- }
- }
-}
-
-register_elgg_event_handler('init', 'system', 'objects_init', 0);
-register_plugin_hook('unit_test', 'system', 'objects_test');
+elgg_register_event_handler('init', 'system', 'objects_init', 0);
+elgg_register_plugin_hook_handler('unit_test', 'system', 'objects_test');
diff --git a/engine/lib/opendd.php b/engine/lib/opendd.php
index c582e6f77..7d635a295 100644
--- a/engine/lib/opendd.php
+++ b/engine/lib/opendd.php
@@ -2,316 +2,34 @@
/**
* OpenDD PHP Library.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
+ * @package Elgg.Core
+ * @subpackage ODD
* @version 0.4
- * @link http://elgg.org/
*/
-include_once("xml.php");
-
-/**
- * @class ODDDocument ODD Document container.
- * This class is used during import and export to construct.
- * @author Curverider Ltd
- */
-class ODDDocument implements Iterator {
- /**
- * ODD Version
- *
- * @var string
- */
- private $ODDSupportedVersion = "1.0";
-
- /**
- * Elements of the document.
- */
- private $elements;
-
- /**
- * Optional wrapper factory.
- */
- private $wrapperfactory;
-
- public function __construct(array $elements = NULL) {
- if ($elements) {
- if (is_array($elements)) {
- $this->elements = $elements;
- } else {
- $this->addElement($elements);
- }
- } else {
- $this->elements = array();
- }
- }
-
- /**
- * Return the version of ODD being used.
- *
- * @return string
- */
- public function getVersion() {
- return $this->ODDSupportedVersion;
- }
-
- public function getNumElements() {
- return count($this->elements);
- }
-
- public function addElement(ODD $element) {
- if (!is_array($this->elements)) {
- $this->elements = array();
- $this->elements[] = $element;
- }
- }
-
- public function addElements(array $elements) {
- foreach ($elements as $element) {
- $this->addElement($element);
- }
- }
-
- public function getElements() {
- return $this->elements;
- }
-
- /**
- * Set an optional wrapper factory to optionally embed the ODD document in another format.
- */
- public function setWrapperFactory(ODDWrapperFactory $factory) {
- $this->wrapperfactory = $factory;
- }
-
- /**
- * Magic function to generate valid ODD XML for this item.
- */
- public function __toString() {
- $xml = "";
-
- if ($this->wrapperfactory) {
- // A wrapper has been provided
- $wrapper = $this->wrapperfactory->getElementWrapper($this); // Get the wrapper for this element
-
- $xml = $wrapper->wrap($this); // Wrap this element (and subelements)
- } else {
- // Output begin tag
- $generated = date("r");
- $xml .= "<odd version=\"{$this->ODDSupportedVersion}\" generated=\"$generated\">\n";
-
- // Get XML for elements
- foreach ($this->elements as $element) {
- $xml .= "$element";
- }
-
- // Output end tag
- $xml .= "</odd>\n";
- }
-
- return $xml;
- }
-
- // ITERATOR INTERFACE //////////////////////////////////////////////////////////////
- /*
- * This lets an entity's attributes be displayed using foreach as a normal array.
- * Example: http://www.sitepoint.com/print/php5-standard-library
- */
-
- private $valid = FALSE;
-
- function rewind() {
- $this->valid = (FALSE !== reset($this->elements));
- }
-
- function current() {
- return current($this->elements);
- }
-
- function key() {
- return key($this->elements);
- }
-
- function next() {
- $this->valid = (FALSE !== next($this->elements));
- }
-
- function valid() {
- return $this->valid;
- }
-}
-
-/**
- * Open Data Definition (ODD) superclass.
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- */
-abstract class ODD {
- /**
- * Attributes.
- */
- private $attributes = array();
-
- /**
- * Optional body.
- */
- private $body;
-
- /**
- * Construct an ODD document with initial values.
- */
- public function __construct() {
- $this->body = "";
- }
-
- public function getAttributes() {
- return $this->attributes;
- }
-
- public function setAttribute($key, $value) {
- $this->attributes[$key] = $value;
- }
-
- public function getAttribute($key) {
- if (isset($this->attributes[$key])) {
- return $this->attributes[$key];
- }
-
- return NULL;
- }
-
- public function setBody($value) {
- $this->body = $value;
- }
-
- public function getBody() {
- return $this->body;
- }
-
- /**
- * Set the published time.
- *
- * @param int $time Unix timestamp
- */
- public function setPublished($time) {
- $this->attributes['published'] = date("r", $time);
- }
-
- /**
- * Return the published time as a unix timestamp.
- *
- * @return int or false on failure.
- */
- public function getPublishedAsTime() {
- return strtotime($this->attributes['published']);
- }
-
- /**
- * For serialisation, implement to return a string name of the tag eg "header" or "metadata".
- * @return string
- */
- abstract protected function getTagName();
-
- /**
- * Magic function to generate valid ODD XML for this item.
- */
- public function __toString() {
- // Construct attributes
- $attr = "";
- foreach ($this->attributes as $k => $v) {
- $attr .= ($v!="") ? "$k=\"$v\" " : "";
- }
-
- $body = $this->getBody();
- $tag = $this->getTagName();
-
- $end = "/>";
- if ($body!="") {
- $end = "><![CDATA[$body]]></{$tag}>";
- }
-
- return "<{$tag} $attr" . $end . "\n";
- }
-}
-
-/**
- * ODD Entity class.
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- */
-class ODDEntity extends ODD {
- function __construct($uuid, $class, $subclass = "") {
- parent::__construct();
-
- $this->setAttribute('uuid', $uuid);
- $this->setAttribute('class', $class);
- $this->setAttribute('subclass', $subclass);
- }
-
- protected function getTagName() { return "entity"; }
-}
-
-/**
- * ODD Metadata class.
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- */
-class ODDMetaData extends ODD {
- function __construct($uuid, $entity_uuid, $name, $value, $type = "", $owner_uuid = "") {
- parent::__construct();
-
- $this->setAttribute('uuid', $uuid);
- $this->setAttribute('entity_uuid', $entity_uuid);
- $this->setAttribute('name', $name);
- $this->setAttribute('type', $type);
- $this->setAttribute('owner_uuid', $owner_uuid);
- $this->setBody($value);
- }
-
- protected function getTagName() {
- return "metadata";
- }
-}
-
-/**
- * ODD Relationship class.
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- */
-class ODDRelationship extends ODD {
- function __construct($uuid1, $type, $uuid2) {
- parent::__construct();
-
- $this->setAttribute('uuid1', $uuid1);
- $this->setAttribute('type', $type);
- $this->setAttribute('uuid2', $uuid2);
- }
-
- protected function getTagName() { return "relationship"; }
-}
+// @codingStandardsIgnoreStart
/**
* Attempt to construct an ODD object out of a XmlElement or sub-elements.
*
* @param XmlElement $element The element(s)
+ *
* @return mixed An ODD object if the element can be handled, or false.
+ * @access private
*/
-function ODD_factory(XmlElement $element) {
+function ODD_factory (XmlElement $element) {
$name = $element->name;
$odd = false;
switch ($name) {
case 'entity' :
- $odd = new ODDEntity("","","");
+ $odd = new ODDEntity("", "", "");
break;
case 'metadata' :
- $odd = new ODDMetaData("","","","");
+ $odd = new ODDMetaData("", "", "", "");
break;
case 'relationship' :
- $odd = new ODDRelationship("","","");
+ $odd = new ODDRelationship("", "", "");
break;
}
@@ -319,15 +37,15 @@ function ODD_factory(XmlElement $element) {
if ($odd) {
// Attributes
foreach ($element->attributes as $k => $v) {
- $odd->setAttribute($k,$v);
+ $odd->setAttribute($k, $v);
}
// Body
$body = $element->content;
$a = stripos($body, "<![CDATA");
$b = strripos($body, "]]>");
- if (($body) && ($a!==false) && ($b!==false)) {
- $body = substr($body, $a+8, $b-($a+8));
+ if (($body) && ($a !== false) && ($b !== false)) {
+ $body = substr($body, $a + 8, $b - ($a + 8));
}
$odd->setBody($body);
@@ -340,7 +58,9 @@ function ODD_factory(XmlElement $element) {
* Import an ODD document.
*
* @param string $xml The XML ODD.
+ *
* @return ODDDocument
+ * @access private
*/
function ODD_Import($xml) {
// Parse XML to an array
@@ -378,8 +98,12 @@ function ODD_Import($xml) {
* Export an ODD Document.
*
* @param ODDDocument $document The Document.
- * @param ODDWrapperFactory $wrapper Optional wrapper permitting the export process to embed ODD in other document formats.
+ *
+ * @return string
+ * @access private
*/
function ODD_Export(ODDDocument $document) {
return "$document";
-} \ No newline at end of file
+}
+
+// @codingStandardsIgnoreEnd
diff --git a/engine/lib/output.php b/engine/lib/output.php
index 6e861e4ac..de4f911fb 100644
--- a/engine/lib/output.php
+++ b/engine/lib/output.php
@@ -1,41 +1,45 @@
<?php
/**
* Output functions
- * Processing text for output, formatting HTML,
+ * Processing text for output such as pulling out URLs and extracting excerpts
*
* @package Elgg
* @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
*/
/**
* Takes a string and turns any URLs into formatted links
*
* @param string $text The input string
- * @return string The output stirng with formatted links
- **/
+ *
+ * @return string The output string with formatted links
+ */
function parse_urls($text) {
+
+ // URI specification: http://www.ietf.org/rfc/rfc3986.txt
+ // This varies from the specification in the following ways:
+ // * Supports non-ascii characters
+ // * Does not allow parentheses and single quotes
+ // * Cuts off commas, exclamation points, and periods off as last character
+
// @todo this causes problems with <attr = "val">
- // must be ing <attr="val"> format (no space).
+ // must be in <attr="val"> format (no space).
// By default htmlawed rewrites tags to this format.
// if PHP supported conditional negative lookbehinds we could use this:
// $r = preg_replace_callback('/(?<!=)(?<![ ])?(?<!["\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\'\!\(\),]+)/i',
- //
- // we can put , in the list of excluded char but need to keep . because of domain names.
- // it is removed in the callback.
- $r = preg_replace_callback('/(?<!=)(?<!["\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\'\!\(\),]+)/i',
+ $r = preg_replace_callback('/(?<![=\/"\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\']+)/i',
create_function(
'$matches',
'
$url = $matches[1];
- $period = \'\';
- if (substr($url, -1, 1) == \'.\') {
- $period = \'.\';
- $url = trim($url, \'.\');
+ $punc = "";
+ $last = substr($url, -1, 1);
+ if (in_array($last, array(".", "!", ",", "(", ")"))) {
+ $punc = $last;
+ $url = rtrim($url, ".!,()");
}
$urltext = str_replace("/", "/<wbr />", $url);
- return "<a href=\"$url\" style=\"text-decoration:underline;\">$urltext</a>$period";
+ return "<a href=\"$url\" rel=\"nofollow\">$urltext</a>$punc";
'
), $text);
@@ -44,46 +48,26 @@ function parse_urls($text) {
/**
* Create paragraphs from text with line spacing
- * Borrowed from Wordpress.
*
+ * @param string $pee The string
+ * @deprecated Use elgg_autop instead
+ * @todo Add deprecation warning in 1.9
+ *
+ * @return string
**/
-function autop($pee, $br = 1) {
- $pee = $pee . "\n"; // just to make things a little easier, pad the end
- $pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee);
- // Space things out a little
- $allblocks = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)';
- $pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee);
- $pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
- $pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines
- if ( strpos($pee, '<object') !== false ) {
- $pee = preg_replace('|\s*<param([^>]*)>\s*|', "<param$1>", $pee); // no pee inside object/embed
- $pee = preg_replace('|\s*</embed>\s*|', '</embed>', $pee);
- }
- $pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates
- $pee = preg_replace('/\n?(.+?)(?:\n\s*\n|\z)/s', "<p>$1</p>\n", $pee); // make paragraphs, including one at the end
- $pee = preg_replace('|<p>\s*?</p>|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace
- $pee = preg_replace('!<p>([^<]+)\s*?(</(?:div|address|form)[^>]*>)!', "<p>$1</p>$2", $pee);
- $pee = preg_replace( '|<p>|', "$1<p>", $pee );
- $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); // don't pee all over a tag
- $pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problem with nested lists
- $pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee);
- $pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
- $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee);
- $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
- if ($br) {
- $pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', create_function('$matches', 'return str_replace("\n", "<WPPreserveNewline />", $matches[0]);'), $pee);
- $pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee); // optionally make line breaks
- $pee = str_replace('<WPPreserveNewline />', "\n", $pee);
- }
- $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee);
- $pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
-// if (strpos($pee, '<pre') !== false) {
-// mind the space between the ? and >. Only there because of the comment.
-// $pee = preg_replace_callback('!(<pre.*? >)(.*?)</pre>!is', 'clean_pre', $pee );
-// }
- $pee = preg_replace( "|\n</p>$|", '</p>', $pee );
-
- return $pee;
+function autop($pee) {
+ return elgg_autop($pee);
+}
+
+/**
+ * Create paragraphs from text with line spacing
+ *
+ * @param string $string The string
+ *
+ * @return string
+ **/
+function elgg_autop($string) {
+ return ElggAutoP::getInstance()->process($string);
}
/**
@@ -92,15 +76,16 @@ function autop($pee, $br = 1) {
* If no spaces are found (like in Japanese) will crop off at the
* n char mark. Adds ... if any text was chopped.
*
- * @param string $text
- * @param int $num_chars Return a string up to $num_chars long
+ * @param string $text The full text to excerpt
+ * @param int $num_chars Return a string up to $num_chars long
+ *
* @return string
* @since 1.7.2
*/
-function elgg_make_excerpt($text, $num_chars = 250) {
- $text = trim(strip_tags($text));
+function elgg_get_excerpt($text, $num_chars = 250) {
+ $text = trim(elgg_strip_tags($text));
$string_length = elgg_strlen($text);
-
+
if ($string_length <= $num_chars) {
return $text;
}
@@ -116,7 +101,7 @@ function elgg_make_excerpt($text, $num_chars = 250) {
$excerpt = trim(elgg_substr($excerpt, 0, $space));
if ($string_length != elgg_strlen($excerpt)) {
- $excerpt .= '&#8230';
+ $excerpt .= '...';
}
return $excerpt;
@@ -125,7 +110,8 @@ function elgg_make_excerpt($text, $num_chars = 250) {
/**
* Handles formatting of ampersands in urls
*
- * @param string $url
+ * @param string $url The URL
+ *
* @return string
* @since 1.7.1
*/
@@ -134,21 +120,350 @@ function elgg_format_url($url) {
}
/**
+ * Converts an associative array into a string of well-formed attributes
+ *
+ * @note usually for HTML, but could be useful for XML too...
+ *
+ * @param array $attrs An associative array of attr => val pairs
+ *
+ * @return string HTML attributes to be inserted into a tag (e.g., <tag $attrs>)
+ */
+function elgg_format_attributes(array $attrs) {
+ $attrs = elgg_clean_vars($attrs);
+ $attributes = array();
+
+ if (isset($attrs['js'])) {
+ //@todo deprecated notice?
+
+ if (!empty($attrs['js'])) {
+ $attributes[] = $attrs['js'];
+ }
+
+ unset($attrs['js']);
+ }
+
+ foreach ($attrs as $attr => $val) {
+ $attr = strtolower($attr);
+
+ if ($val === TRUE) {
+ $val = $attr; //e.g. checked => TRUE ==> checked="checked"
+ }
+
+ // ignore $vars['entity'] => ElggEntity stuff
+ if ($val !== NULL && $val !== false && (is_array($val) || !is_object($val))) {
+
+ // allow $vars['class'] => array('one', 'two');
+ // @todo what about $vars['style']? Needs to be semi-colon separated...
+ if (is_array($val)) {
+ $val = implode(' ', $val);
+ }
+
+ $val = htmlspecialchars($val, ENT_QUOTES, 'UTF-8', false);
+ $attributes[] = "$attr=\"$val\"";
+ }
+ }
+
+ return implode(' ', $attributes);
+}
+
+/**
+ * Preps an associative array for use in {@link elgg_format_attributes()}.
+ *
+ * Removes all the junk that {@link elgg_view()} puts into $vars.
+ * Maintains backward compatibility with attributes like 'internalname' and 'internalid'
+ *
+ * @note This function is called automatically by elgg_format_attributes(). No need to
+ * call it yourself before using elgg_format_attributes().
+ *
+ * @param array $vars The raw $vars array with all it's dirtiness (config, url, etc.)
+ *
+ * @return array The array, ready to be used in elgg_format_attributes().
+ * @access private
+ */
+function elgg_clean_vars(array $vars = array()) {
+ unset($vars['config']);
+ unset($vars['url']);
+ unset($vars['user']);
+
+ // backwards compatibility code
+ if (isset($vars['internalname'])) {
+ $vars['name'] = $vars['internalname'];
+ unset($vars['internalname']);
+ }
+
+ if (isset($vars['internalid'])) {
+ $vars['id'] = $vars['internalid'];
+ unset($vars['internalid']);
+ }
+
+ if (isset($vars['__ignoreInternalid'])) {
+ unset($vars['__ignoreInternalid']);
+ }
+
+ if (isset($vars['__ignoreInternalname'])) {
+ unset($vars['__ignoreInternalname']);
+ }
+
+ return $vars;
+}
+
+/**
+ * Converts shorthand urls to absolute urls.
+ *
+ * If the url is already absolute or protocol-relative, no change is made.
+ *
+ * @example
+ * elgg_normalize_url(''); // 'http://my.site.com/'
+ * elgg_normalize_url('dashboard'); // 'http://my.site.com/dashboard'
+ * elgg_normalize_url('http://google.com/'); // no change
+ * elgg_normalize_url('//google.com/'); // no change
+ *
+ * @param string $url The URL to normalize
+ *
+ * @return string The absolute url
+ */
+function elgg_normalize_url($url) {
+ // see https://bugs.php.net/bug.php?id=51192
+ // from the bookmarks save action.
+ $php_5_2_13_and_below = version_compare(PHP_VERSION, '5.2.14', '<');
+ $php_5_3_0_to_5_3_2 = version_compare(PHP_VERSION, '5.3.0', '>=') &&
+ version_compare(PHP_VERSION, '5.3.3', '<');
+
+ if ($php_5_2_13_and_below || $php_5_3_0_to_5_3_2) {
+ $tmp_address = str_replace("-", "", $url);
+ $validated = filter_var($tmp_address, FILTER_VALIDATE_URL);
+ } else {
+ $validated = filter_var($url, FILTER_VALIDATE_URL);
+ }
+
+ // work around for handling absoluate IRIs (RFC 3987) - see #4190
+ if (!$validated && (strpos($url, 'http:') === 0) || (strpos($url, 'https:') === 0)) {
+ $validated = true;
+ }
+
+ if ($validated) {
+ // all normal URLs including mailto:
+ return $url;
+
+ } elseif (preg_match("#^(\#|\?|//)#i", $url)) {
+ // '//example.com' (Shortcut for protocol.)
+ // '?query=test', #target
+ return $url;
+
+ } elseif (stripos($url, 'javascript:') === 0 || stripos($url, 'mailto:') === 0) {
+ // 'javascript:' and 'mailto:'
+ // Not covered in FILTER_VALIDATE_URL
+ return $url;
+
+ } elseif (preg_match("#^[^/]*\.php(\?.*)?$#i", $url)) {
+ // 'install.php', 'install.php?step=step'
+ return elgg_get_site_url() . $url;
+
+ } elseif (preg_match("#^[^/]*\.#i", $url)) {
+ // 'example.com', 'example.com/subpage'
+ return "http://$url";
+
+ } else {
+ // 'page/handler', 'mod/plugin/file.php'
+
+ // trim off any leading / because the site URL is stored
+ // with a trailing /
+ return elgg_get_site_url() . ltrim($url, '/');
+ }
+}
+
+/**
* When given a title, returns a version suitable for inclusion in a URL
*
* @param string $title The title
+ *
* @return string The optimised title
+ * @since 1.7.2
*/
-function friendly_title($title) {
- return elgg_view('output/friendlytitle', array('title' => $title));
+function elgg_get_friendly_title($title) {
+
+ // return a URL friendly title to short circuit normal title formatting
+ $params = array('title' => $title);
+ $result = elgg_trigger_plugin_hook('format', 'friendly:title', $params, NULL);
+ if ($result) {
+ return $result;
+ }
+
+ // titles are often stored HTML encoded
+ $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
+
+ $title = ElggTranslit::urlize($title);
+
+ return $title;
}
/**
- * Displays a UNIX timestamp in a friendly way (eg "less than a minute ago")
+ * Formats a UNIX timestamp in a friendly way (eg "less than a minute ago")
+ *
+ * @see elgg_view_friendly_time()
*
* @param int $time A UNIX epoch timestamp
- * @return string The friendly time
+ *
+ * @return string The friendly time string
+ * @since 1.7.2
+ */
+function elgg_get_friendly_time($time) {
+
+ // return a time string to short circuit normal time formatting
+ $params = array('time' => $time);
+ $result = elgg_trigger_plugin_hook('format', 'friendly:time', $params, NULL);
+ if ($result) {
+ return $result;
+ }
+
+ $diff = time() - (int)$time;
+
+ $minute = 60;
+ $hour = $minute * 60;
+ $day = $hour * 24;
+
+ if ($diff < $minute) {
+ return elgg_echo("friendlytime:justnow");
+ } else if ($diff < $hour) {
+ $diff = round($diff / $minute);
+ if ($diff == 0) {
+ $diff = 1;
+ }
+
+ if ($diff > 1) {
+ return elgg_echo("friendlytime:minutes", array($diff));
+ } else {
+ return elgg_echo("friendlytime:minutes:singular", array($diff));
+ }
+ } else if ($diff < $day) {
+ $diff = round($diff / $hour);
+ if ($diff == 0) {
+ $diff = 1;
+ }
+
+ if ($diff > 1) {
+ return elgg_echo("friendlytime:hours", array($diff));
+ } else {
+ return elgg_echo("friendlytime:hours:singular", array($diff));
+ }
+ } else {
+ $diff = round($diff / $day);
+ if ($diff == 0) {
+ $diff = 1;
+ }
+
+ if ($diff > 1) {
+ return elgg_echo("friendlytime:days", array($diff));
+ } else {
+ return elgg_echo("friendlytime:days:singular", array($diff));
+ }
+ }
+}
+
+/**
+ * Strip tags and offer plugins the chance.
+ * Plugins register for output:strip_tags plugin hook.
+ * Original string included in $params['original_string']
+ *
+ * @param string $string Formatted string
+ *
+ * @return string String run through strip_tags() and any plugin hooks.
*/
-function friendly_time($time) {
- return elgg_view('output/friendlytime', array('time' => $time));
+function elgg_strip_tags($string) {
+ $params['original_string'] = $string;
+
+ $string = strip_tags($string);
+ $string = elgg_trigger_plugin_hook('format', 'strip_tags', $params, $string);
+
+ return $string;
}
+
+/**
+ * Apply html_entity_decode() to a string while re-entitising HTML
+ * special char entities to prevent them from being decoded back to their
+ * unsafe original forms.
+ *
+ * This relies on html_entity_decode() not translating entities when
+ * doing so leaves behind another entity, e.g. &amp;gt; if decoded would
+ * create &gt; which is another entity itself. This seems to escape the
+ * usual behaviour where any two paired entities creating a HTML tag are
+ * usually decoded, i.e. a lone &gt; is not decoded, but &lt;foo&gt; would
+ * be decoded to <foo> since it creates a full tag.
+ *
+ * Note: This function is poorly explained in the manual - which is really
+ * bad given its potential for misuse on user input already escaped elsewhere.
+ * Stackoverflow is littered with advice to use this function in the precise
+ * way that would lead to user input being capable of injecting arbitrary HTML.
+ *
+ * @param string $string
+ *
+ * @return string
+ *
+ * @author Pádraic Brady
+ * @copyright Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com)
+ * @license Released under dual-license GPL2/MIT by explicit permission of Pádraic Brady
+ *
+ * @access private
+ */
+function _elgg_html_decode($string) {
+ $string = str_replace(
+ array('&gt;', '&lt;', '&amp;', '&quot;', '&#039;'),
+ array('&amp;gt;', '&amp;lt;', '&amp;amp;', '&amp;quot;', '&amp;#039;'),
+ $string
+ );
+ $string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8');
+ $string = str_replace(
+ array('&amp;gt;', '&amp;lt;', '&amp;amp;', '&amp;quot;', '&amp;#039;'),
+ array('&gt;', '&lt;', '&amp;', '&quot;', '&#039;'),
+ $string
+ );
+ return $string;
+}
+
+/**
+ * Prepares query string for output to prevent CSRF attacks.
+ *
+ * @param string $string
+ * @return string
+ *
+ * @access private
+ */
+function _elgg_get_display_query($string) {
+ //encode <,>,&, quotes and characters above 127
+ if (function_exists('mb_convert_encoding')) {
+ $display_query = mb_convert_encoding($string, 'HTML-ENTITIES', 'UTF-8');
+ } else {
+ // if no mbstring extension, we just strip characters
+ $display_query = preg_replace("/[^\x01-\x7F]/", "", $string);
+ }
+ return htmlspecialchars($display_query, ENT_QUOTES, 'UTF-8', false);
+}
+
+/**
+ * Unit tests for Output
+ *
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of tests
+ * @param mixed $params Params
+ *
+ * @return array
+ * @access private
+ */
+function output_unit_test($hook, $type, $value, $params) {
+ global $CONFIG;
+ $value[] = $CONFIG->path . 'engine/tests/api/output.php';
+ return $value;
+}
+
+/**
+ * Initialise the Output subsystem.
+ *
+ * @return void
+ * @access private
+ */
+function output_init() {
+ elgg_register_plugin_hook_handler('unit_test', 'system', 'output_unit_test');
+}
+
+elgg_register_event_handler('init', 'system', 'output_init');
diff --git a/engine/lib/pagehandler.php b/engine/lib/pagehandler.php
index 5f60eefae..0cf99b6fe 100644
--- a/engine/lib/pagehandler.php
+++ b/engine/lib/pagehandler.php
@@ -2,82 +2,92 @@
/**
* Elgg page handler functions
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Routing
*/
/**
- * Turns the current page over to the page handler, allowing registered handlers to take over.
+ * Routes the request to a registered page handler
*
- * If a page handler returns FALSE, the request is handed over to the default_page_handler.
+ * This function sets the context based on the handler name (first segment of the
+ * URL). It also triggers a plugin hook 'route', $handler so that plugins can
+ * modify the routing or handle a request.
*
* @param string $handler The name of the handler type (eg 'blog')
- * @param array $page The parameters to the page, as an array (exploded by '/' slashes)
- * @return true|false Depending on whether a registered page handler was found
+ * @param array $page The parameters to the page, as an array (exploded by '/' slashes)
+ *
+ * @return bool
+ * @access private
*/
function page_handler($handler, $page) {
global $CONFIG;
- set_context($handler);
+ elgg_set_context($handler);
- $page = explode('/',$page);
+ $page = explode('/', $page);
// remove empty array element when page url ends in a / (see #1480)
if ($page[count($page) - 1] === '') {
array_pop($page);
}
- if (!isset($CONFIG->pagehandler) || empty($handler)) {
- $result = false;
- } else if (isset($CONFIG->pagehandler[$handler]) && is_callable($CONFIG->pagehandler[$handler])) {
- $function = $CONFIG->pagehandler[$handler];
- $result = $function($page, $handler);
- if ($result !== false) {
- $result = true;
- }
- } else {
- $result = false;
+ // return false to stop processing the request (because you handled it)
+ // return a new $request array if you want to route the request differently
+ $request = array(
+ 'handler' => $handler,
+ 'segments' => $page,
+ );
+ $request = elgg_trigger_plugin_hook('route', $handler, null, $request);
+ if ($request === false) {
+ return true;
}
- if (!$result) {
- $result = default_page_handler($page, $handler);
- }
- if ($result !== false) {
- $result = true;
+ $handler = $request['handler'];
+ $page = $request['segments'];
+
+ $result = false;
+ if (isset($CONFIG->pagehandler)
+ && !empty($handler)
+ && isset($CONFIG->pagehandler[$handler])
+ && is_callable($CONFIG->pagehandler[$handler])) {
+ $function = $CONFIG->pagehandler[$handler];
+ $result = call_user_func($function, $page, $handler);
}
- return $result;
+ return $result || headers_sent();
}
/**
* Registers a page handler for a particular identifier
*
* For example, you can register a function called 'blog_page_handler' for handler type 'blog'
- * Now for all URLs of type http://yoururl/pg/blog/*, the blog_page_handler() function will be called.
+ * For all URLs http://yoururl/blog/*, the blog_page_handler() function will be called.
* The part of the URL marked with * above will be exploded on '/' characters and passed as an
* array to that function.
* For example, the URL http://yoururl/blog/username/friends/ would result in the call:
* blog_page_handler(array('username','friends'), blog);
*
- * Page handler functions should return true or the default page handler will be called.
- *
* A request to register a page handler with the same identifier as previously registered
* handler will replace the previous one.
*
* The context is set to the page handler identifier before the registered
* page handler function is called. For the above example, the context is set to 'blog'.
*
- * @param string $handler The page type to handle
+ * Page handlers should return true to indicate that they handled the request.
+ * Requests not handled are forwarded to the front page with a reason of 404.
+ * Plugins can register for the 'forward', '404' plugin hook. @see forward()
+ *
+ * @param string $handler The page type to handle
* @param string $function Your function name
- * @return true|false Depending on success
+ *
+ * @return bool Depending on success
*/
-function register_page_handler($handler, $function) {
+function elgg_register_page_handler($handler, $function) {
global $CONFIG;
+
if (!isset($CONFIG->pagehandler)) {
$CONFIG->pagehandler = array();
}
- if (is_callable($function)) {
+ if (is_callable($function, true)) {
$CONFIG->pagehandler[$handler] = $function;
return true;
}
@@ -88,14 +98,16 @@ function register_page_handler($handler, $function) {
/**
* Unregister a page handler for an identifier
*
- * Note: to replace a page handler, call register_page_handler()
- *
+ * Note: to replace a page handler, call elgg_register_page_handler()
+ *
* @param string $handler The page type identifier
+ *
* @since 1.7.2
+ * @return void
*/
-function unregister_page_handler($handler) {
+function elgg_unregister_page_handler($handler) {
global $CONFIG;
-
+
if (!isset($CONFIG->pagehandler)) {
return;
}
@@ -104,34 +116,35 @@ function unregister_page_handler($handler) {
}
/**
- * A default page handler
- * Tries to locate a suitable file to include. Only works for core pages, not plugins.
+ * Serve an error page
+ *
+ * @todo not sending status codes yet
*
- * @param array $page The page URL elements
- * @param string $handler The base handler
- * @return true|false Depending on success
+ * @param string $hook The name of the hook
+ * @param string $type The type of the hook
+ * @param bool $result The current value of the hook
+ * @param array $params Parameters related to the hook
+ * @return void
*/
-function default_page_handler($page, $handler) {
- global $CONFIG;
-
- $page = implode('/', $page);
-
- // protect against including arbitary files
- $page = str_replace("..", "", $page);
-
- $callpath = $CONFIG->path . $handler . "/" . $page;
- if (is_dir($callpath)) {
- $callpath = sanitise_filepath($callpath);
- $callpath .= "index.php";
- if (file_exists($callpath)) {
- if (include($callpath)) {
- return TRUE;
- }
- }
- } else if (file_exists($callpath)) {
- include($callpath);
- return TRUE;
+function elgg_error_page_handler($hook, $type, $result, $params) {
+ if (elgg_view_exists("errors/$type")) {
+ $content = elgg_view("errors/$type", $params);
+ } else {
+ $content = elgg_view("errors/default", $params);
}
+ $body = elgg_view_layout('error', array('content' => $content));
+ echo elgg_view_page('', $body, 'error');
+ exit;
+}
- return FALSE;
+/**
+ * Initializes the page handler/routing system
+ *
+ * @return void
+ * @access private
+ */
+function page_handler_init() {
+ elgg_register_plugin_hook_handler('forward', '404', 'elgg_error_page_handler');
}
+
+elgg_register_event_handler('init', 'system', 'page_handler_init');
diff --git a/engine/lib/pageowner.php b/engine/lib/pageowner.php
index 32c431632..bd63d08c6 100644
--- a/engine/lib/pageowner.php
+++ b/engine/lib/pageowner.php
@@ -1,169 +1,297 @@
<?php
/**
* Elgg page owner library
- * Contains functions for managing page ownership
+ * Contains functions for managing page ownership and context
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage PageOwner
*/
/**
- * Gets the page owner for the current page.
- * @uses $CONFIG
- * @return int|false The current page owner guid (0 if none).
+ * Gets the guid of the entity that owns the current page.
+ *
+ * @param int $guid Optional parameter used by elgg_set_page_owner_guid().
+ *
+ * @return int The current page owner guid (0 if none).
+ * @since 1.8.0
*/
+function elgg_get_page_owner_guid($guid = 0) {
+ static $page_owner_guid;
-function page_owner() {
- global $CONFIG;
+ if ($guid) {
+ $page_owner_guid = $guid;
+ }
+
+ if (isset($page_owner_guid)) {
+ return $page_owner_guid;
+ }
+
+ // return guid of page owner entity
+ $guid = elgg_trigger_plugin_hook('page_owner', 'system', NULL, 0);
+
+ if ($guid) {
+ $page_owner_guid = $guid;
+ }
+
+ return $guid;
+}
+
+/**
+ * Gets the owner entity for the current page.
+ *
+ * @note Access is disabled when getting the page owner entity.
+ *
+ * @return ElggUser|ElggGroup|false The current page owner or false if none.
+ *
+ * @since 1.8.0
+ */
+function elgg_get_page_owner_entity() {
+ $guid = elgg_get_page_owner_guid();
+ if ($guid > 0) {
+ $ia = elgg_set_ignore_access(true);
+ $owner = get_entity($guid);
+ elgg_set_ignore_access($ia);
+
+ return $owner;
+ }
+
+ return false;
+}
+
+/**
+ * Set the guid of the entity that owns this page
+ *
+ * @param int $guid The guid of the page owner
+ * @return void
+ * @since 1.8.0
+ */
+function elgg_set_page_owner_guid($guid) {
+ elgg_get_page_owner_guid($guid);
+}
- $returnval = NULL;
+/**
+ * Sets the page owner based on request
+ *
+ * Tries to figure out the page owner by looking at the URL or a request
+ * parameter. The request parameters used are 'username' and 'owner_guid'. If
+ * the page request is going through the page handling system, this function
+ * attempts to figure out the owner if the url fits the patterns of:
+ * <handler>/owner/<username>
+ * <handler>/friends/<username>
+ * <handler>/view/<entity guid>
+ * <handler>/add/<container guid>
+ * <handler>/edit/<entity guid>
+ * <handler>/group/<group guid>
+ *
+ * @note Access is disabled while finding the page owner for the group gatekeeper functions.
+ *
+ *
+ * @param string $hook 'page_owner'
+ * @param string $entity_type 'system'
+ * @param int $returnvalue Previous function's return value
+ * @param array $params no parameters
+ *
+ * @return int GUID
+ * @access private
+ */
+function default_page_owner_handler($hook, $entity_type, $returnvalue, $params) {
- $setpageowner = set_page_owner();
- if ($setpageowner !== false) {
- return $setpageowner;
+ if ($returnvalue) {
+ return $returnvalue;
}
- if ((!isset($returnval)) && ($username = get_input("username"))) {
- if (substr_count($username,'group:')) {
- preg_match('/group\:([0-9]+)/i',$username,$matches);
+ $ia = elgg_set_ignore_access(true);
+
+ $username = get_input("username");
+ if ($username) {
+ // @todo using a username of group:<guid> is deprecated
+ if (substr_count($username, 'group:')) {
+ preg_match('/group\:([0-9]+)/i', $username, $matches);
$guid = $matches[1];
if ($entity = get_entity($guid)) {
- $returnval = $entity->getGUID();
+ elgg_set_ignore_access($ia);
+ return $entity->getGUID();
}
}
- if ((!isset($returnval)) && ($user = get_user_by_username($username))) {
- $returnval = $user->getGUID();
+
+ if ($user = get_user_by_username($username)) {
+ elgg_set_ignore_access($ia);
+ return $user->getGUID();
}
}
-
- if ((!isset($returnval)) && ($owner = get_input("owner_guid"))) {
+ $owner = get_input("owner_guid");
+ if ($owner) {
if ($user = get_entity($owner)) {
- $returnval = $user->getGUID();
+ elgg_set_ignore_access($ia);
+ return $user->getGUID();
}
}
-
- if ((!isset($returnval)) && (!empty($CONFIG->page_owner_handlers) && is_array($CONFIG->page_owner_handlers))) {
- foreach($CONFIG->page_owner_handlers as $handler) {
- if ((!isset($returnval)) && ($guid = $handler())) {
- $returnval = $guid;
- }
- }
+ // ignore root and query
+ $uri = current_page_url();
+ $path = str_replace(elgg_get_site_url(), '', $uri);
+ $path = trim($path, "/");
+ if (strpos($path, "?")) {
+ $path = substr($path, 0, strpos($path, "?"));
}
- if (isset($returnval)) {
- // Check if this is obtainable, forwarding if not.
- /*
- * If the owner entity has been set, but is inaccessible then we forward to the dashboard. This
- * catches a bunch of WSoDs. It doesn't have much of a performance hit since 99.999% of the time the next thing
- * a page does after calling this function is to retrieve the owner entity - which is of course cashed.
- */
- $owner_entity = get_entity($returnval);
- if (!$owner_entity) {
-
- // Log an error
- error_log(sprintf(elgg_echo('pageownerunavailable'), $returnval));
-
- // Forward
- forward();
+ // @todo feels hacky
+ if (get_input('page', FALSE)) {
+ $segments = explode('/', $path);
+ if (isset($segments[1]) && isset($segments[2])) {
+ switch ($segments[1]) {
+ case 'owner':
+ case 'friends':
+ $user = get_user_by_username($segments[2]);
+ if ($user) {
+ elgg_set_ignore_access($ia);
+ return $user->getGUID();
+ }
+ break;
+ case 'view':
+ case 'edit':
+ $entity = get_entity($segments[2]);
+ if ($entity) {
+ elgg_set_ignore_access($ia);
+ return $entity->getContainerGUID();
+ }
+ break;
+ case 'add':
+ case 'group':
+ $entity = get_entity($segments[2]);
+ if ($entity) {
+ elgg_set_ignore_access($ia);
+ return $entity->getGUID();
+ }
+ break;
+ }
}
-
- // set the page owner so if we're called again we don't have to think.
- set_page_owner($returnval);
- return $returnval;
}
- return 0;
+ elgg_set_ignore_access($ia);
}
/**
- * Gets the page owner for the current page.
- * @uses $CONFIG
- * @return ElggUser|false The current page owner (false if none).
+ * Sets the page context
+ *
+ * Views can modify their output based on the local context. You may want to
+ * display a list of blogs on a blog page or in a small widget. The rendered
+ * output could be different for those two contexts ('blog' vs 'widget').
+ *
+ * Pages that pass through the page handling system set the context to the
+ * first string after the root url. Example: http://example.org/elgg/bookmarks/
+ * results in the initial context being set to 'bookmarks'.
+ *
+ * The context is a stack so that for a widget on a profile, the context stack
+ * may contain first 'profile' and then 'widget'.
+ *
+ * If no context was been set, the default context returned is 'main'.
+ *
+ * @warning The context is not available until the page_handler runs (after
+ * the 'init, system' event processing has completed).
+ *
+ * @param string $context The context of the page
+ * @return bool
+ * @since 1.8.0
*/
-function page_owner_entity() {
+function elgg_set_context($context) {
global $CONFIG;
- $page_owner = page_owner();
- if ($page_owner > 0) {
- return get_entity($page_owner);
+
+ $context = trim($context);
+
+ if (empty($context)) {
+ return false;
}
- return false;
+ $context = strtolower($context);
+
+ array_pop($CONFIG->context);
+ array_push($CONFIG->context, $context);
+
+ return true;
}
/**
- * Adds a page owner handler - a function that will
- * return the page owner if required
- * (Such functions are required to return false if they don't know)
- * @uses $CONFIG
- * @param string $functionname The name of the function to call
- * @return mixed The guid of the owner or false
+ * Get the current context.
+ *
+ * Since context is a stack, this is equivalent to a peek.
+ *
+ * @return string|NULL
+ * @since 1.8.0
*/
-
-function add_page_owner_handler($functionname) {
+function elgg_get_context() {
global $CONFIG;
- if (empty($CONFIG->page_owner_handlers)) {
- $CONFIG->page_owner_handlers = array();
- }
- if (is_callable($functionname)) {
- $CONFIG->page_owner_handlers[] = $functionname;
+
+ if (!$CONFIG->context) {
+ return null;
}
+
+ return $CONFIG->context[count($CONFIG->context) - 1];
}
/**
- * Allows a page to manually set a page owner
+ * Push a context onto the top of the stack
*
- * @param int $entitytoset The GUID of the page owner
- * @return int|false Either the page owner we've just set, or false if unset
+ * @param string $context The context string to add to the context stack
+ * @return void
+ * @since 1.8.0
*/
-function set_page_owner($entitytoset = -1) {
- static $entity;
-
- if (!isset($entity)) {
- $entity = false;
- }
+function elgg_push_context($context) {
+ global $CONFIG;
- if ($entitytoset > -1) {
- $entity = $entitytoset;
- }
+ array_push($CONFIG->context, $context);
+}
- return $entity;
+/**
+ * Removes and returns the top context string from the stack
+ *
+ * @return string|NULL
+ * @since 1.8.0
+ */
+function elgg_pop_context() {
+ global $CONFIG;
+ return array_pop($CONFIG->context);
}
/**
- * Sets the functional context of a page
+ * Check if this context exists anywhere in the stack
*
- * @param string $context The context of the page
- * @return string|false Either the context string, or false on failure
+ * This is useful for situations with more than one element in the stack. For
+ * example, a widget has a context of 'widget'. If a widget view needs to render
+ * itself differently based on being on the dashboard or profile pages, it
+ * can check the stack.
+ *
+ * @param string $context The context string to check for
+ * @return bool
+ * @since 1.8.0
*/
-function set_context($context) {
+function elgg_in_context($context) {
global $CONFIG;
- if (!empty($context)) {
- $context = trim($context);
- $context = strtolower($context);
- $CONFIG->context = $context;
- return $context;
- } else {
- return false;
- }
+
+ return in_array($context, $CONFIG->context);
}
/**
- * Returns the functional context of a page
+ * Initializes the page owner functions
*
- * @return string The context, or 'main' if no context has been provided
+ * @note This is on the 'boot, system' event so that the context is set up quickly.
+ *
+ * @return void
+ * @access private
*/
-function get_context() {
- global $CONFIG;
- if (isset($CONFIG->context) && !empty($CONFIG->context)) {
- return $CONFIG->context;
- }
- if ($context = get_plugin_name(true)) {
- return $context;
+function page_owner_boot() {
+
+ elgg_register_plugin_hook_handler('page_owner', 'system', 'default_page_owner_handler');
+
+ // Bootstrap the context stack by setting its first entry to the handler.
+ // This is the first segment of the URL and the handler is set by the rewrite rules.
+ // @todo this does not work for actions
+ $handler = get_input('handler', FALSE);
+ if ($handler) {
+ elgg_set_context($handler);
}
- return "main";
}
+
+elgg_register_event_handler('boot', 'system', 'page_owner_boot');
diff --git a/engine/lib/pam.php b/engine/lib/pam.php
index 590ef9fde..1c9c3bfe1 100644
--- a/engine/lib/pam.php
+++ b/engine/lib/pam.php
@@ -2,35 +2,42 @@
/**
* Elgg Simple PAM library
* Contains functions for managing authentication.
- * This is not a full implementation of PAM. It supports a single facility
+ * This is not a full implementation of PAM. It supports a single facility
* (authentication) and allows multiple policies (user authentication is the
- * default). There are two control flags possible for each module: sufficient
- * or required. The entire chain for a policy is processed (or until a
- * required module fails). A module fails by returning false or throwing an
- * exception. The order that modules are processed is determined by the order
- * they are registered. For an example of a PAM, see pam_auth_userpass() in
+ * default). There are two control flags possible for each module: sufficient
+ * or required. The entire chain for a policy is processed (or until a
+ * required module fails). A module fails by returning false or throwing an
+ * exception. The order that modules are processed is determined by the order
+ * they are registered. For an example of a PAM, see pam_auth_userpass() in
* sessions.php.
- *
+ *
* For more information on PAMs see:
* http://www.freebsd.org/doc/en/articles/pam/index.html
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @see ElggPAM
+ *
+ * @package Elgg.Core
+ * @subpackage Authentication.PAM
*/
+global $_PAM_HANDLERS;
$_PAM_HANDLERS = array();
-$_PAM_HANDLERS_MSG = array();
/**
* Register a PAM handler.
*
- * @param string $handler The handler function in the format
- * pam_handler($credentials = NULL);
+ * A PAM handler should return true if the authentication attempt passed. For a
+ * failure, return false or throw an exception. Returning nothing indicates that
+ * the handler wants to be skipped.
+ *
+ * Note, $handler must be string callback (not an array/Closure).
+ *
+ * @param string $handler Callable global handler function in the format ()
+ * pam_handler($credentials = NULL);
* @param string $importance The importance - "sufficient" (default) or "required"
- * @param string $policy - the policy type, default is "user"
- * @return boolean
+ * @param string $policy The policy type, default is "user"
+ *
+ * @return bool
*/
function register_pam_handler($handler, $importance = "sufficient", $policy = "user") {
global $_PAM_HANDLERS;
@@ -39,8 +46,9 @@ function register_pam_handler($handler, $importance = "sufficient", $policy = "u
if (!isset($_PAM_HANDLERS[$policy])) {
$_PAM_HANDLERS[$policy] = array();
}
-
- if (is_callable($handler)) {
+
+ // @todo remove requirement that $handle be a global function
+ if (is_string($handler) && is_callable($handler, true)) {
$_PAM_HANDLERS[$policy][$handler] = new stdClass;
$_PAM_HANDLERS[$policy][$handler]->handler = $handler;
@@ -56,66 +64,13 @@ function register_pam_handler($handler, $importance = "sufficient", $policy = "u
* Unregisters a PAM handler.
*
* @param string $handler The PAM handler function name
- * @param string $policy - the policy type, default is "user"
+ * @param string $policy The policy type, default is "user"
+ *
+ * @return void
+ * @since 1.7.0
*/
function unregister_pam_handler($handler, $policy = "user") {
global $_PAM_HANDLERS;
unset($_PAM_HANDLERS[$policy][$handler]);
}
-
-/**
- * Attempt to authenticate.
- * This function will process all registered PAM handlers or stop when the first
- * handler fails. A handler fails by either returning false or throwing an
- * exception. The advantage of throwing an exception is that it returns a message
- * through the global $_PAM_HANDLERS_MSG which can be used in communication with
- * a user. The order that handlers are processed is determined by the order that
- * they were registered.
- *
- * If $credentials are provided the PAM handler should authenticate using the
- * provided credentials, if not then credentials should be prompted for or
- * otherwise retrieved (eg from the HTTP header or $_SESSION).
- *
- * @param mixed $credentials Mixed PAM handler specific credentials (e.g. username, password)
- * @param string $policy - the policy type, default is "user"
- * @return bool true if authenticated, false if not.
- */
-function pam_authenticate($credentials = NULL, $policy = "user") {
- global $_PAM_HANDLERS, $_PAM_HANDLERS_MSG;
-
- $_PAM_HANDLERS_MSG = array();
-
- $authenticated = false;
-
- foreach ($_PAM_HANDLERS[$policy] as $k => $v) {
- $handler = $v->handler;
- $importance = $v->importance;
-
- try {
- // Execute the handler
- if ($handler($credentials)) {
- // Explicitly returned true
- $_PAM_HANDLERS_MSG[$k] = "Authenticated!";
-
- $authenticated = true;
- } else {
- $_PAM_HANDLERS_MSG[$k] = "Not Authenticated.";
-
- // If this is required then abort.
- if ($importance == 'required') {
- return false;
- }
- }
- } catch (Exception $e) {
- $_PAM_HANDLERS_MSG[$k] = "$e";
-
- // If this is required then abort.
- if ($importance == 'required') {
- return false;
- }
- }
- }
-
- return $authenticated;
-} \ No newline at end of file
diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php
index b8cf5c2d2..d5d3db466 100644
--- a/engine/lib/plugins.php
+++ b/engine/lib/plugins.php
@@ -3,304 +3,562 @@
* Elgg plugins library
* Contains functions for managing plugins
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Plugins
*/
+/**
+ * Tells ElggPlugin::start() to include the start.php file.
+ */
+define('ELGG_PLUGIN_INCLUDE_START', 1);
-/// Cache enabled plugins per page
-$ENABLED_PLUGINS_CACHE = NULL;
+/**
+ * Tells ElggPlugin::start() to automatically register the plugin's views.
+ */
+define('ELGG_PLUGIN_REGISTER_VIEWS', 2);
/**
- * PluginException
- *
- * A plugin Exception, thrown when an Exception occurs relating to the plugin mechanism. Subclass for specific plugin Exceptions.
+ * Tells ElggPlugin::start() to automatically register the plugin's languages.
+ */
+define('ELGG_PLUGIN_REGISTER_LANGUAGES', 4);
+
+/**
+ * Tells ElggPlugin::start() to automatically register the plugin's classes.
+ */
+define('ELGG_PLUGIN_REGISTER_CLASSES', 8);
+
+/**
+ * Prefix for plugin setting names
*
- * @package Elgg
- * @subpackage Exceptions
+ * @todo Can't namespace these because many plugins directly call
+ * private settings via $entity->$name.
*/
-class PluginException extends Exception {}
+//define('ELGG_PLUGIN_SETTING_PREFIX', 'plugin:setting:');
/**
- * @class ElggPlugin Object representing a plugin's settings for a given site.
- * This class is currently a stub, allowing a plugin to saving settings in an object's metadata for each site.
- * @author Curverider Ltd
+ * Prefix for plugin user setting names
*/
-class ElggPlugin extends ElggObject {
- protected function initialise_attributes() {
- parent::initialise_attributes();
+define('ELGG_PLUGIN_USER_SETTING_PREFIX', 'plugin:user_setting:');
+
+/**
+ * Internal settings prefix
+ *
+ * @todo This could be resolved by promoting ElggPlugin to a 5th type.
+ */
+define('ELGG_PLUGIN_INTERNAL_PREFIX', 'elgg:internal:');
- $this->attributes['subtype'] = "plugin";
- }
- public function __construct($guid = null) {
- parent::__construct($guid);
+/**
+ * Returns a list of plugin IDs (dir names) from a dir.
+ *
+ * @param string $dir A dir to scan for plugins. Defaults to config's plugins_path.
+ *
+ * @return array
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_get_plugin_ids_in_dir($dir = null) {
+ if (!$dir) {
+ $dir = elgg_get_plugins_path();
}
- /**
- * Override entity get and sets in order to save data to private data store.
- */
- public function get($name) {
- // See if its in our base attribute
- if (isset($this->attributes[$name])) {
- return $this->attributes[$name];
+ $plugin_ids = array();
+ $handle = opendir($dir);
+
+ if ($handle) {
+ while ($plugin_id = readdir($handle)) {
+ // must be directory and not begin with a .
+ if (substr($plugin_id, 0, 1) !== '.' && is_dir($dir . $plugin_id)) {
+ $plugin_ids[] = $plugin_id;
+ }
}
+ }
+
+ sort($plugin_ids);
- // No, so see if its in the private data store.
- // get_private_setting() returns false if it doesn't exist
- $meta = get_private_setting($this->guid, $name);
+ return $plugin_ids;
+}
- if ($meta === false) {
- // Can't find it, so return null
- return NULL;
+/**
+ * Discovers plugins in the plugins_path setting and creates ElggPlugin
+ * entities for them if they don't exist. If there are plugins with entities
+ * but not actual files, will disable the ElggPlugin entities and mark as inactive.
+ * The ElggPlugin object holds config data, so don't delete.
+ *
+ * @todo Crappy name?
+ * @return bool
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_generate_plugin_entities() {
+ // @todo $site unused, can remove?
+ $site = get_config('site');
+
+ $dir = elgg_get_plugins_path();
+ $db_prefix = elgg_get_config('dbprefix');
+
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'plugin',
+ 'selects' => array('plugin_oe.*'),
+ 'joins' => array("JOIN {$db_prefix}objects_entity plugin_oe on plugin_oe.guid = e.guid"),
+ 'limit' => ELGG_ENTITIES_NO_VALUE
+ );
+
+ $old_ia = elgg_set_ignore_access(true);
+ $old_access = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $known_plugins = elgg_get_entities_from_relationship($options);
+ /* @var ElggPlugin[] $known_plugins */
+
+ if (!$known_plugins) {
+ $known_plugins = array();
+ }
+
+ // map paths to indexes
+ $id_map = array();
+ foreach ($known_plugins as $i => $plugin) {
+ // if the ID is wrong, delete the plugin because we can never load it.
+ $id = $plugin->getID();
+ if (!$id) {
+ $plugin->delete();
+ unset($known_plugins[$i]);
+ continue;
}
+ $id_map[$plugin->getID()] = $i;
+ }
+
+ $physical_plugins = elgg_get_plugin_ids_in_dir($dir);
- return $meta;
+ if (!$physical_plugins) {
+ return false;
}
- /**
- * Override entity get and sets in order to save data to private data store.
- */
- public function set($name, $value) {
- if (array_key_exists($name, $this->attributes)) {
- // Check that we're not trying to change the guid!
- if ((array_key_exists('guid', $this->attributes)) && ($name=='guid')) {
- return false;
+ // check real plugins against known ones
+ foreach ($physical_plugins as $plugin_id) {
+ // is this already in the db?
+ if (array_key_exists($plugin_id, $id_map)) {
+ $index = $id_map[$plugin_id];
+ $plugin = $known_plugins[$index];
+ // was this plugin deleted and its entity disabled?
+ if (!$plugin->isEnabled()) {
+ $plugin->enable();
+ $plugin->deactivate();
+ $plugin->setPriority('last');
}
- $this->attributes[$name] = $value;
+ // remove from the list of plugins to disable
+ unset($known_plugins[$index]);
} else {
- return set_private_setting($this->guid, $name, $value);
+ // add new plugins
+ // priority is force to last in save() if not set.
+ $plugin = new ElggPlugin($plugin_id);
+ $plugin->save();
}
+ }
- return true;
+ // everything remaining in $known_plugins needs to be disabled
+ // because they are entities, but their dirs were removed.
+ // don't delete the entities because they hold settings.
+ foreach ($known_plugins as $plugin) {
+ if ($plugin->isActive()) {
+ $plugin->deactivate();
+ }
+ // remove the priority.
+ $name = elgg_namespace_plugin_private_setting('internal', 'priority');
+ remove_private_setting($plugin->guid, $name);
+ $plugin->disable();
}
+
+ access_show_hidden_entities($old_access);
+ elgg_set_ignore_access($old_ia);
+
+ elgg_reindex_plugin_priorities();
+
+ return true;
}
/**
- * Returns a list of plugins to load, in the order that they should be loaded.
- *
- * @return array List of plugins
+ * Cache a reference to this plugin by its ID
+ *
+ * @param ElggPlugin $plugin
+ *
+ * @access private
*/
-function get_plugin_list() {
- global $CONFIG;
+function _elgg_cache_plugin_by_id(ElggPlugin $plugin) {
+ $map = (array) elgg_get_config('plugins_by_id_map');
+ $map[$plugin->getID()] = $plugin;
+ elgg_set_config('plugins_by_id_map', $map);
+}
- if (!empty($CONFIG->pluginlistcache)) {
- return $CONFIG->pluginlistcache;
+/**
+ * Returns an ElggPlugin object with the path $path.
+ *
+ * @param string $plugin_id The id (dir name) of the plugin. NOT the guid.
+ * @return ElggPlugin|false
+ * @since 1.8.0
+ */
+function elgg_get_plugin_from_id($plugin_id) {
+ $map = (array) elgg_get_config('plugins_by_id_map');
+ if (isset($map[$plugin_id])) {
+ return $map[$plugin_id];
}
- if ($site = get_entity($CONFIG->site_guid)) {
- $pluginorder = $site->pluginorder;
- if (!empty($pluginorder)) {
- $plugins = unserialize($pluginorder);
-
- $CONFIG->pluginlistcache = $plugins;
- return $plugins;
- } else {
- $plugins = array();
+ $plugin_id = sanitize_string($plugin_id);
+ $db_prefix = get_config('dbprefix');
- if ($handle = opendir($CONFIG->pluginspath)) {
- while ($mod = readdir($handle)) {
- if (!in_array($mod,array('.','..','.svn','CVS')) && is_dir($CONFIG->pluginspath . "/" . $mod)) {
- $plugins[] = $mod;
- }
- }
- }
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'plugin',
+ 'joins' => array("JOIN {$db_prefix}objects_entity oe on oe.guid = e.guid"),
+ 'selects' => array("oe.title", "oe.description"),
+ 'wheres' => array("oe.title = '$plugin_id'"),
+ 'limit' => 1
+ );
- sort($plugins);
+ $plugins = elgg_get_entities($options);
- $CONFIG->pluginlistcache = $plugins;
- return $plugins;
- }
+ if ($plugins) {
+ return $plugins[0];
}
return false;
}
/**
- * Regenerates the list of known plugins and saves it to the current site
+ * Returns if a plugin exists in the system.
*
- * Important: You should regenerate simplecache and the viewpath cache after executing this function
- * otherwise you may experience view display artifacts. Do this with the following code:
+ * @warning This checks only plugins that are registered in the system!
+ * If the plugin cache is outdated, be sure to regenerate it with
+ * {@link elgg_generate_plugin_objects()} first.
*
- * elgg_view_regenerate_simplecache();
- * elgg_filepath_cache_reset();
+ * @param string $id The plugin ID.
+ * @since 1.8.0
+ * @return bool
+ */
+function elgg_plugin_exists($id) {
+ $plugin = elgg_get_plugin_from_id($id);
+
+ return ($plugin) ? true : false;
+}
+
+/**
+ * Returns the highest priority of the plugins
*
- * @param array $pluginorder Optionally, a list of existing plugins and their orders
- * @return array The new list of plugins and their orders
+ * @return int
+ * @since 1.8.0
+ * @access private
*/
-function regenerate_plugin_list($pluginorder = false) {
- global $CONFIG;
+function elgg_get_max_plugin_priority() {
+ $db_prefix = get_config('dbprefix');
+ $priority = elgg_namespace_plugin_private_setting('internal', 'priority');
+ $plugin_subtype = get_subtype_id('object', 'plugin');
+
+ $q = "SELECT MAX(CAST(ps.value AS unsigned)) as max
+ FROM {$db_prefix}entities e, {$db_prefix}private_settings ps
+ WHERE ps.name = '$priority'
+ AND ps.entity_guid = e.guid
+ AND e.type = 'object' and e.subtype = $plugin_subtype";
+
+ $data = get_data($q);
+ if ($data) {
+ $max = $data[0]->max;
+ } else {
+ $max = 1;
+ }
- $CONFIG->pluginlistcache = null;
+ // can't have a priority of 0.
+ return ($max) ? $max : 1;
+}
- if ($site = get_entity($CONFIG->site_guid)) {
- if (empty($pluginorder)) {
- $pluginorder = $site->pluginorder;
- $pluginorder = unserialize($pluginorder);
- } else {
- ksort($pluginorder);
- }
+/**
+ * Returns if a plugin is active for a current site.
+ *
+ * @param string $plugin_id The plugin ID
+ * @param int $site_guid The site guid
+ * @since 1.8.0
+ * @return bool
+ */
+function elgg_is_active_plugin($plugin_id, $site_guid = null) {
+ if ($site_guid) {
+ $site = get_entity($site_guid);
+ } else {
+ $site = elgg_get_site_entity();
+ }
- if (empty($pluginorder)) {
- $pluginorder = array();
- }
+ if (!($site instanceof ElggSite)) {
+ return false;
+ }
- $max = 0;
- if (sizeof($pluginorder)) {
- foreach($pluginorder as $key => $plugin) {
- if (is_dir($CONFIG->pluginspath . "/" . $plugin)) {
- if ($key > $max)
- $max = $key;
- } else {
- unset($pluginorder[$key]);
- }
- }
- }
- // Add new plugins to the end
- if ($handle = opendir($CONFIG->pluginspath)) {
- while ($mod = readdir($handle)) {
- if (!in_array($mod,array('.','..','.svn','CVS')) && is_dir($CONFIG->pluginspath . "/" . $mod)) {
- if (!in_array($mod, $pluginorder)) {
- $max = $max + 10;
- $pluginorder[$max] = $mod;
- }
- }
- }
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+
+ if (!$plugin) {
+ return false;
+ }
+
+ return $plugin->isActive($site->guid);
+}
+
+/**
+ * Loads all active plugins in the order specified in the tool admin panel.
+ *
+ * @note This is called on every page load. If a plugin is active and problematic, it
+ * will be disabled and a visible error emitted. This does not check the deps system because
+ * that was too slow.
+ *
+ * @return bool
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_load_plugins() {
+ $plugins_path = elgg_get_plugins_path();
+ $start_flags = ELGG_PLUGIN_INCLUDE_START |
+ ELGG_PLUGIN_REGISTER_VIEWS |
+ ELGG_PLUGIN_REGISTER_LANGUAGES |
+ ELGG_PLUGIN_REGISTER_CLASSES;
+
+ if (!$plugins_path) {
+ return false;
+ }
+
+ // 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;
+ }
- ksort($pluginorder);
+ if (elgg_get_config('system_cache_loaded')) {
+ $start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_VIEWS;
+ }
+
+ if (elgg_get_config('i18n_loaded_from_cache')) {
+ $start_flags = $start_flags & ~ELGG_PLUGIN_REGISTER_LANGUAGES;
+ }
+
+ $return = true;
+ $plugins = elgg_get_plugins('active');
+ if ($plugins) {
+ foreach ($plugins as $plugin) {
+ try {
+ $plugin->start($start_flags);
+ } catch (Exception $e) {
+ $plugin->deactivate();
+ $msg = elgg_echo('PluginException:CannotStart',
+ array($plugin->getID(), $plugin->guid, $e->getMessage()));
+ elgg_add_admin_notice('cannot_start' . $plugin->getID(), $msg);
+ $return = false;
- // Now reorder the keys ..
- $key = 10;
- $plugins = array();
- if (sizeof($pluginorder)) {
- foreach($pluginorder as $plugin) {
- $plugins[$key] = $plugin;
- $key = $key + 10;
+ continue;
}
}
+ }
- $plugins = serialize($plugins);
-
- $site->pluginorder = $plugins;
+ return $return;
+}
- // Regenerate caches
- elgg_view_regenerate_simplecache();
- elgg_filepath_cache_reset();
+/**
+ * Returns an ordered list of plugins
+ *
+ * @param string $status The status of the plugins. active, inactive, or all.
+ * @param mixed $site_guid Optional site guid
+ * @return ElggPlugin[]
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_get_plugins($status = 'active', $site_guid = null) {
+ $db_prefix = get_config('dbprefix');
+ $priority = elgg_namespace_plugin_private_setting('internal', 'priority');
- return $plugins;
+ if (!$site_guid) {
+ $site = get_config('site');
+ $site_guid = $site->guid;
+ }
+ // grab plugins
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'plugin',
+ 'limit' => ELGG_ENTITIES_NO_VALUE,
+ 'selects' => array('plugin_oe.*'),
+ 'joins' => array(
+ "JOIN {$db_prefix}private_settings ps on ps.entity_guid = e.guid",
+ "JOIN {$db_prefix}objects_entity plugin_oe on plugin_oe.guid = e.guid"
+ ),
+ 'wheres' => array("ps.name = '$priority'"),
+ 'order_by' => "CAST(ps.value as unsigned), e.guid"
+ );
+
+ switch ($status) {
+ case 'active':
+ $options['relationship'] = 'active_plugin';
+ $options['relationship_guid'] = $site_guid;
+ $options['inverse_relationship'] = true;
+ break;
+
+ case 'inactive':
+ $options['wheres'][] = "NOT EXISTS (
+ SELECT 1 FROM {$db_prefix}entity_relationships active_er
+ WHERE active_er.guid_one = e.guid
+ AND active_er.relationship = 'active_plugin'
+ AND active_er.guid_two = $site_guid)";
+ break;
+
+ case 'all':
+ default:
+ break;
}
- return false;
-}
+ $old_ia = elgg_set_ignore_access(true);
+ $plugins = elgg_get_entities_from_relationship($options);
+ elgg_set_ignore_access($old_ia);
+ return $plugins;
+}
/**
- * For now, loads plugins directly
+ * Reorder plugins to an order specified by the array.
+ * Plugins not included in this array will be appended to the end.
+ *
+ * @note This doesn't use the ElggPlugin->setPriority() method because
+ * all plugins are being changed and we don't want it to automatically
+ * reorder plugins.
*
- * @todo Add proper plugin handler that launches plugins in an admin-defined order and activates them on admin request
- * @package Elgg
- * @subpackage Core
+ * @param array $order An array of plugin ids in the order to set them
+ * @return bool
+ * @since 1.8.0
+ * @access private
*/
-function load_plugins() {
- global $CONFIG;
+function elgg_set_plugin_priorities(array $order) {
+ $name = elgg_namespace_plugin_private_setting('internal', 'priority');
- if (!empty($CONFIG->pluginspath)) {
- // See if we have cached values for things
- $cached_view_paths = elgg_filepath_cache_load();
- if ($cached_view_paths) {
- $CONFIG->views = unserialize($cached_view_paths);
+ $plugins = elgg_get_plugins('any');
+ if (!$plugins) {
+ return false;
+ }
+
+ $return = true;
+
+ // reindex to get standard counting. no need to increment by 10.
+ // though we do start with 1
+ $order = array_values($order);
+
+ $missing_plugins = array();
+ foreach ($plugins as $plugin) {
+ $plugin_id = $plugin->getID();
+
+ if (!in_array($plugin_id, $order)) {
+ $missing_plugins[] = $plugin;
+ continue;
}
- // temporary disable all plugins if there is a file called 'disabled' in the plugin dir
- if (file_exists($CONFIG->pluginspath . "disabled")) {
- return;
+ $priority = array_search($plugin_id, $order) + 1;
+
+ if (!$plugin->set($name, $priority)) {
+ $return = false;
+ break;
}
+ }
- $plugins = get_plugin_list();
-
- if (sizeof($plugins)) {
- foreach($plugins as $mod) {
- if (is_plugin_enabled($mod)) {
- if (file_exists($CONFIG->pluginspath . $mod)) {
- if (!include($CONFIG->pluginspath . $mod . "/start.php")) {
- // automatically disable the bad plugin
- disable_plugin($mod);
-
- // register error rather than rendering the site unusable with exception
- register_error(sprintf(elgg_echo('PluginException:MisconfiguredPlugin'), $mod));
-
- // continue loading remaining plugins
- continue;
- }
-
- if (!$cached_view_paths) {
- $view_dir = $CONFIG->pluginspath . $mod . '/views/';
-
- if (is_dir($view_dir) && ($handle = opendir($view_dir))) {
- while (FALSE !== ($view_type = readdir($handle))) {
- $view_type_dir = $view_dir . $view_type;
-
- if ('.' !== substr($view_type, 0, 1) && is_dir($view_type_dir)) {
- if (autoregister_views('', $view_type_dir, $view_dir, $view_type)) {
- // add the valid view type.
- if (!in_array($view_type, $CONFIG->view_types)) {
- $CONFIG->view_types[] = $view_type;
- }
- }
- }
- }
- }
- }
-
- if (is_dir($CONFIG->pluginspath . $mod . "/languages")) {
- register_translations($CONFIG->pluginspath . $mod . "/languages/");
- }
- }
- }
+ // set the missing plugins' priorities
+ if ($return && $missing_plugins) {
+ if (!isset($priority)) {
+ $priority = 0;
+ }
+ foreach ($missing_plugins as $plugin) {
+ $priority++;
+ if (!$plugin->set($name, $priority)) {
+ $return = false;
+ break;
}
}
+ }
- // Cache results
- if (!$cached_view_paths) {
- elgg_filepath_cache_save(serialize($CONFIG->views));
- }
+ return $return;
+}
+
+/**
+ * Reindexes all plugin priorities starting at 1.
+ *
+ * @todo Can this be done in a single sql command?
+ * @return bool
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_reindex_plugin_priorities() {
+ return elgg_set_plugin_priorities(array());
+}
+
+/**
+ * Namespaces a string to be used as a private setting for a plugin.
+ *
+ * @param string $type The type of value: user_setting or internal.
+ * @param string $name The name to namespace.
+ * @param string $id The plugin's ID to namespace with. Required for user_setting.
+ * @return string
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_namespace_plugin_private_setting($type, $name, $id = null) {
+ switch ($type) {
+ // commented out because it breaks $plugin->$name access to variables
+ //case 'setting':
+ // $name = ELGG_PLUGIN_SETTING_PREFIX . $name;
+ // break;
+
+ case 'user_setting':
+ if (!$id) {
+ $id = elgg_get_calling_plugin_id();
+ }
+ $name = ELGG_PLUGIN_USER_SETTING_PREFIX . "$id:$name";
+ break;
+
+ case 'internal':
+ $name = ELGG_PLUGIN_INTERNAL_PREFIX . $name;
+ break;
}
+
+ return $name;
}
/**
- * Get the name of the most recent plugin to be called in the call stack (or the plugin that owns the current page, if any).
+ * Get the name of the most recent plugin to be called in the
+ * call stack (or the plugin that owns the current page, if any).
+ *
+ * i.e., if the last plugin was in /mod/foobar/, this would return foo_bar.
*
- * i.e., if the last plugin was in /mod/foobar/, get_plugin_name would return foo_bar.
+ * @param boolean $mainfilename If set to true, this will instead determine the
+ * context from the main script filename called by
+ * the browser. Default = false.
*
- * @param boolean $mainfilename If set to true, this will instead determine the context from the main script filename called by the browser. Default = false.
* @return string|false Plugin name, or false if no plugin name was called
+ * @since 1.8.0
+ * @access private
+ *
+ * @todo get rid of this
*/
-function get_plugin_name($mainfilename = false) {
+function elgg_get_calling_plugin_id($mainfilename = false) {
if (!$mainfilename) {
if ($backtrace = debug_backtrace()) {
- foreach($backtrace as $step) {
+ foreach ($backtrace as $step) {
$file = $step['file'];
- $file = str_replace("\\","/",$file);
- $file = str_replace("//","/",$file);
- if (preg_match("/mod\/([a-zA-Z0-9\-\_]*)\/start\.php$/",$file,$matches)) {
+ $file = str_replace("\\", "/", $file);
+ $file = str_replace("//", "/", $file);
+ if (preg_match("/mod\/([a-zA-Z0-9\-\_]*)\/start\.php$/", $file, $matches)) {
return $matches[1];
}
}
}
} else {
- //if (substr_count($file,'handlers/pagehandler')) {
- if (preg_match("/pg\/([a-zA-Z0-9\-\_]*)\//",$_SERVER['REQUEST_URI'],$matches)) {
- return $matches[1];
+ //@todo this is a hack -- plugins do not have to match their page handler names!
+ if ($handler = get_input('handler', FALSE)) {
+ return $handler;
} else {
$file = $_SERVER["SCRIPT_NAME"];
- $file = str_replace("\\","/",$file);
- $file = str_replace("//","/",$file);
- if (preg_match("/mod\/([a-zA-Z0-9\-\_]*)\//",$file,$matches)) {
+ $file = str_replace("\\", "/", $file);
+ $file = str_replace("//", "/", $file);
+ if (preg_match("/mod\/([a-zA-Z0-9\-\_]*)\//", $file, $matches)) {
return $matches[1];
}
}
@@ -309,589 +567,613 @@ function get_plugin_name($mainfilename = false) {
}
/**
- * Load and parse a plugin manifest from a plugin XML file.
- *
- * Example file:
+ * Returns an array of all provides from all active plugins.
*
- * <plugin_manifest>
- * <!-- Basic information -->
- * <field key="name" value="My Plugin" />
- * <field key="description" value="My Plugin's concise description" />
- * <field key="version" value="1.0" />
- * <field key="category" value="theme" />
- * <field key="category" value="bundled" />
- * <field key="screenshot" value="path/relative/to/my_plugin.jpg" />
- * <field key="screenshot" value="path/relative/to/my_plugin_2.jpg" />
+ * Array in the form array(
+ * 'provide_type' => array(
+ * 'provided_name' => array(
+ * 'version' => '1.8',
+ * 'provided_by' => 'provider_plugin_id'
+ * )
+ * )
+ * )
*
- * <field key="author" value="Curverider Ltd" />
- * <field key="website" value="http://www.elgg.org/" />
- * <field key="copyright" value="(C) Curverider 2008-2010" />
- * <field key="licence" value="GNU Public License version 2" />
- * </plugin_manifest>
+ * @param string $type The type of provides to return
+ * @param string $name A specific provided name to return. Requires $provide_type.
*
- * @param string $plugin Plugin name.
- * @return array of values
+ * @return array
+ * @since 1.8.0
+ * @access private
*/
-function load_plugin_manifest($plugin) {
- global $CONFIG;
-
- $xml = xml_to_object(file_get_contents($CONFIG->pluginspath . $plugin. "/manifest.xml"));
-
- if ($xml) {
- // set up some defaults to normalize expected values to arrays
- $elements = array(
- 'screenshot' => array(),
- 'category' => array()
- );
-
- foreach ($xml->children as $element) {
- $key = $element->attributes['key'];
- $value = $element->attributes['value'];
-
- // create arrays if multiple fields are set
- if (array_key_exists($key, $elements)) {
- if (!is_array($elements[$key])) {
- $orig = $elements[$key];
- $elements[$key] = array($orig);
+function elgg_get_plugins_provides($type = null, $name = null) {
+ static $provides = null;
+ $active_plugins = elgg_get_plugins('active');
+
+ if (!isset($provides)) {
+ $provides = array();
+
+ foreach ($active_plugins as $plugin) {
+ $plugin_provides = array();
+ $manifest = $plugin->getManifest();
+ if ($manifest instanceof ElggPluginManifest) {
+ $plugin_provides = $plugin->getManifest()->getProvides();
+ }
+ if ($plugin_provides) {
+ foreach ($plugin_provides as $provided) {
+ $provides[$provided['type']][$provided['name']] = array(
+ 'version' => $provided['version'],
+ 'provided_by' => $plugin->getID()
+ );
}
-
- $elements[$key][] = $value;
- } else {
- $elements[$key] = $value;
}
}
+ }
- // handle plugins that don't define a name
- if (!isset($elements['name'])) {
- $elements['name'] = ucwords($plugin);
+ if ($type && $name) {
+ if (isset($provides[$type][$name])) {
+ return $provides[$type][$name];
+ } else {
+ return false;
+ }
+ } elseif ($type) {
+ if (isset($provides[$type])) {
+ return $provides[$type];
+ } else {
+ return false;
}
-
- return $elements;
}
- return false;
+ return $provides;
}
/**
- * This function checks a plugin manifest 'elgg_version' value against the current install
- * returning TRUE if the elgg_version is >= the current install's version.
- * @param $manifest_elgg_version_string The build version (eg 2009010201).
- * @return bool
+ * Checks if a plugin is currently providing $type and $name, and optionally
+ * checking a version.
+ *
+ * @param string $type The type of the provide
+ * @param string $name The name of the provide
+ * @param string $version A version to check against
+ * @param string $comparison The comparison operator to use in version_compare()
+ *
+ * @return array An array in the form array(
+ * 'status' => bool Does the provide exist?,
+ * 'value' => string The version provided
+ * )
+ * @since 1.8.0
+ * @access private
*/
-function check_plugin_compatibility($manifest_elgg_version_string) {
- $version = get_version();
-
- if (strpos($manifest_elgg_version_string, '.') === false) {
- // Using version
- $req_version = (int)$manifest_elgg_version_string;
+function elgg_check_plugins_provides($type, $name, $version = null, $comparison = 'ge') {
+ $provided = elgg_get_plugins_provides($type, $name);
+ if (!$provided) {
+ return array(
+ 'status' => false,
+ 'version' => ''
+ );
+ }
- return ($version >= $req_version);
+ if ($version) {
+ $status = version_compare($provided['version'], $version, $comparison);
+ } else {
+ $status = true;
}
- return false;
+ return array(
+ 'status' => $status,
+ 'value' => $provided['version']
+ );
}
/**
- * Shorthand function for finding the plugin settings.
+ * Returns an array of parsed strings for a dependency in the
+ * format: array(
+ * 'type' => requires, conflicts, or provides.
+ * 'name' => The name of the requirement / conflict
+ * 'value' => A string representing the expected value: <1, >=3, !=enabled
+ * 'local_value' => The current value, ("Not installed")
+ * 'comment' => Free form text to help resovle the problem ("Enable / Search for plugin <link>")
+ * )
*
- * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you
- * are calling from.
+ * @param array $dep An ElggPluginPackage dependency array
+ * @return array
+ * @since 1.8.0
+ * @access private
*/
-function find_plugin_settings($plugin_name = "") {
- $options = array('type' => 'object', 'subtype' => 'plugin', 'limit' => 9999);
- $plugins = elgg_get_entities($options);
- $plugin_name = sanitise_string($plugin_name);
- if (!$plugin_name) {
- $plugin_name = get_plugin_name();
+function elgg_get_plugin_dependency_strings($dep) {
+ $dep_system = elgg_extract('type', $dep);
+ $info = elgg_extract('dep', $dep);
+ $type = elgg_extract('type', $info);
+
+ if (!$dep_system || !$info || !$type) {
+ return false;
}
- if ($plugins) {
- foreach ($plugins as $plugin) {
- if (strcmp($plugin->title, $plugin_name)==0) {
- return $plugin;
+ // rewrite some of these to be more readable
+ switch($info['comparison']) {
+ case 'lt':
+ $comparison = '<';
+ break;
+ case 'gt':
+ $comparison = '>';
+ break;
+ case 'ge':
+ $comparison = '>=';
+ break;
+ case 'le':
+ $comparison = '<=';
+ break;
+ default;
+ $comparison = $info['comparison'];
+ break;
+ }
+
+ /*
+ 'requires' 'plugin oauth_lib' <1.3 1.3 'downgrade'
+ 'requires' 'php setting bob' >3 3 'change it'
+ 'conflicts' 'php setting' >3 4 'change it'
+ 'conflicted''plugin profile' any 1.8 'disable profile'
+ 'provides' 'plugin oauth_lib' 1.3 -- --
+ 'priority' 'before blog' -- after 'move it'
+ */
+ $strings = array();
+ $strings['type'] = elgg_echo('ElggPlugin:Dependencies:' . ucwords($dep_system));
+
+ switch ($type) {
+ case 'elgg_version':
+ case 'elgg_release':
+ // 'Elgg Version'
+ $strings['name'] = elgg_echo('ElggPlugin:Dependencies:Elgg');
+ $strings['expected_value'] = "$comparison {$info['version']}";
+ $strings['local_value'] = $dep['value'];
+ $strings['comment'] = '';
+ break;
+
+ case 'php_extension':
+ // PHP Extension %s [version]
+ $strings['name'] = elgg_echo('ElggPlugin:Dependencies:PhpExtension', array($info['name']));
+ if ($info['version']) {
+ $strings['expected_value'] = "$comparison {$info['version']}";
+ $strings['local_value'] = $dep['value'];
+ } else {
+ $strings['expected_value'] = '';
+ $strings['local_value'] = '';
}
+ $strings['comment'] = '';
+ break;
+
+ case 'php_ini':
+ $strings['name'] = elgg_echo('ElggPlugin:Dependencies:PhpIni', array($info['name']));
+ $strings['expected_value'] = "$comparison {$info['value']}";
+ $strings['local_value'] = $dep['value'];
+ $strings['comment'] = '';
+ break;
+
+ case 'plugin':
+ $strings['name'] = elgg_echo('ElggPlugin:Dependencies:Plugin', array($info['name']));
+ $expected = $info['version'] ? "$comparison {$info['version']}" : elgg_echo('any');
+ $strings['expected_value'] = $expected;
+ $strings['local_value'] = $dep['value'] ? $dep['value'] : '--';
+ $strings['comment'] = '';
+ break;
+
+ case 'priority':
+ $expected_priority = ucwords($info['priority']);
+ $real_priority = ucwords($dep['value']);
+ $strings['name'] = elgg_echo('ElggPlugin:Dependencies:Priority');
+ $strings['expected_value'] = elgg_echo("ElggPlugin:Dependencies:Priority:$expected_priority", array($info['plugin']));
+ $strings['local_value'] = elgg_echo("ElggPlugin:Dependencies:Priority:$real_priority", array($info['plugin']));
+ $strings['comment'] = '';
+ break;
+ }
+
+ if ($dep['type'] == 'suggests') {
+ if ($dep['status']) {
+ $strings['comment'] = elgg_echo('ok');
+ } else {
+ $strings['comment'] = elgg_echo('ElggPlugin:Dependencies:Suggests:Unsatisfied');
+ }
+ } else {
+ if ($dep['status']) {
+ $strings['comment'] = elgg_echo('ok');
+ } else {
+ $strings['comment'] = elgg_echo('error');
}
}
- return false;
+ return $strings;
}
/**
- * Find the plugin settings for a user.
+ * Returns the ElggPlugin entity of the last plugin called.
*
- * @param string $plugin_name Plugin name.
- * @param int $user_guid The guid who's settings to retrieve.
- * @return array of settings in an associative array minus prefix.
+ * @return mixed ElggPlugin or false
+ * @since 1.8.0
+ * @access private
*/
-function find_plugin_usersettings($plugin_name = "", $user_guid = 0) {
- $plugin_name = sanitise_string($plugin_name);
- $user_guid = (int)$user_guid;
+function elgg_get_calling_plugin_entity() {
+ $plugin_id = elgg_get_calling_plugin_id();
- if (!$plugin_name) {
- $plugin_name = get_plugin_name();
+ if ($plugin_id) {
+ return elgg_get_plugin_from_id($plugin_id);
}
- if ($user_guid == 0) {
- $user_guid = get_loggedin_userid();
+ return false;
+}
+
+/**
+ * Returns an array of all plugin settings for a user.
+ *
+ * @param mixed $user_guid The user GUID or null for the currently logged in user.
+ * @param string $plugin_id The plugin ID
+ * @param bool $return_obj Return settings as an object? This can be used to in reusable
+ * views where the settings are passed as $vars['entity'].
+ * @return array
+ * @since 1.8.0
+ */
+function elgg_get_all_plugin_user_settings($user_guid = null, $plugin_id = null, $return_obj = false) {
+ if ($plugin_id) {
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+ } else {
+ $plugin = elgg_get_calling_plugin_entity();
}
- // Get metadata for user
- $all_metadata = get_all_private_settings($user_guid); //get_metadata_for_entity($user_guid);
- if ($all_metadata) {
- $prefix = "plugin:settings:$plugin_name:";
- $return = new stdClass;
+ if (!$plugin instanceof ElggPlugin) {
+ return false;
+ }
- foreach ($all_metadata as $key => $meta) {
- $name = substr($key, strlen($prefix));
- $value = $meta;
+ $settings = $plugin->getAllUserSettings($user_guid);
- if (strpos($key, $prefix) === 0) {
- $return->$name = $value;
- }
+ if ($settings && $return_obj) {
+ $return = new stdClass;
+
+ foreach ($settings as $k => $v) {
+ $return->$k = $v;
}
return $return;
+ } else {
+ return $settings;
}
-
- return false;
}
/**
* Set a user specific setting for a plugin.
*
- * @param string $name The name - note, can't be "title".
- * @param mixed $value The value.
- * @param int $user_guid Optional user.
- * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from.
+ * @param string $name The name - note, can't be "title".
+ * @param mixed $value The value.
+ * @param int $user_guid Optional user.
+ * @param string $plugin_id Optional plugin name, if not specified then it
+ * is detected from where you are calling from.
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function set_plugin_usersetting($name, $value, $user_guid = 0, $plugin_name = "") {
- $plugin_name = sanitise_string($plugin_name);
- $user_guid = (int)$user_guid;
- $name = sanitise_string($name);
-
- if (!$plugin_name) {
- $plugin_name = get_plugin_name();
- }
-
- $user = get_entity($user_guid);
- if (!$user) {
- $user = get_loggedin_user();
+function elgg_set_plugin_user_setting($name, $value, $user_guid = null, $plugin_id = null) {
+ if ($plugin_id) {
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+ } else {
+ $plugin = elgg_get_calling_plugin_entity();
}
- if (($user) && ($user instanceof ElggUser)) {
- $prefix = "plugin:settings:$plugin_name:$name";
- //$user->$prefix = $value;
- //$user->save();
-
- // Hook to validate setting
- $value = trigger_plugin_hook('plugin:usersetting', 'user', array(
- 'user' => $user,
- 'plugin' => $plugin_name,
- 'name' => $name,
- 'value' => $value
- ), $value);
-
- return set_private_setting($user->guid, $prefix, $value);
+ if (!$plugin) {
+ return false;
}
- return false;
+ return $plugin->setUserSetting($name, $value, $user_guid);
}
/**
- * Clears a user-specific plugin setting
+ * Unsets a user-specific plugin setting
*
- * @param str $name Name of the plugin setting
- * @param int $user_guid Defaults to logged in user
- * @param str $plugin_name Defaults to contextual plugin name
- * @return bool Success
+ * @param string $name Name of the setting
+ * @param int $user_guid Defaults to logged in user
+ * @param string $plugin_id Defaults to contextual plugin name
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function clear_plugin_usersetting($name, $user_guid=0, $plugin_name='') {
- $plugin_name = sanitise_string($plugin_name);
- $name = sanitise_string($name);
-
- if (!$plugin_name) {
- $plugin_name = get_plugin_name();
- }
-
- $user = get_entity((int) $user_guid);
- if (!$user) {
- $user = get_loggedin_user();
+function elgg_unset_plugin_user_setting($name, $user_guid = null, $plugin_id = null) {
+ if ($plugin_id) {
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+ } else {
+ $plugin = elgg_get_calling_plugin_entity();
}
- if (($user) && ($user instanceof ElggUser)) {
- $prefix = "plugin:settings:$plugin_name:$name";
-
- return remove_private_setting($user->getGUID(), $prefix);
+ if (!$plugin) {
+ return false;
}
- return FALSE;
+ return $plugin->unsetUserSetting($name, $user_guid);
}
/**
* Get a user specific setting for a plugin.
*
- * @param string $name The name.
- * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from.
+ * @param string $name The name of the setting.
+ * @param int $user_guid Guid of owning user
+ * @param string $plugin_id Optional plugin name, if not specified
+ * it is detected from where you are calling.
+ *
+ * @return mixed
+ * @since 1.8.0
*/
-function get_plugin_usersetting($name, $user_guid = 0, $plugin_name = "") {
- $plugin_name = sanitise_string($plugin_name);
- $user_guid = (int)$user_guid;
- $name = sanitise_string($name);
-
- if (!$plugin_name) {
- $plugin_name = get_plugin_name();
- }
-
- $user = get_entity($user_guid);
- if (!$user) {
- $user = get_loggedin_user();
+function elgg_get_plugin_user_setting($name, $user_guid = null, $plugin_id = null) {
+ if ($plugin_id) {
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+ } else {
+ $plugin = elgg_get_calling_plugin_entity();
}
- if (($user) && ($user instanceof ElggUser)) {
- $prefix = "plugin:settings:$plugin_name:$name";
- return get_private_setting($user->guid, $prefix); //$user->$prefix;
+ if (!$plugin) {
+ return false;
}
- return false;
+ return $plugin->getUserSetting($name, $user_guid);
}
/**
* Set a setting for a plugin.
*
- * @param string $name The name - note, can't be "title".
- * @param mixed $value The value.
- * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from.
+ * @param string $name The name of the setting - note, can't be "title".
+ * @param mixed $value The value.
+ * @param string $plugin_id Optional plugin name, if not specified
+ * then it is detected from where you are calling from.
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function set_plugin_setting($name, $value, $plugin_name = "") {
- if (!$plugin_name) {
- $plugin_name = get_plugin_name();
+function elgg_set_plugin_setting($name, $value, $plugin_id = null) {
+ if ($plugin_id) {
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+ } else {
+ $plugin = elgg_get_calling_plugin_entity();
}
- $plugin = find_plugin_settings($plugin_name);
if (!$plugin) {
- $plugin = new ElggPlugin();
- }
-
- if ($name!='title') {
- // Hook to validate setting
- $value = trigger_plugin_hook('plugin:setting', 'plugin', array(
- 'plugin' => $plugin_name,
- 'name' => $name,
- 'value' => $value
- ), $value);
-
- $plugin->title = $plugin_name;
- $plugin->access_id = ACCESS_PUBLIC;
- $plugin->save();
- $plugin->$name = $value;
-
- return $plugin->getGUID();
+ return false;
}
- return false;
+ return $plugin->setSetting($name, $value);
}
/**
* Get setting for a plugin.
*
- * @param string $name The name.
- * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from.
+ * @param string $name The name of the setting.
+ * @param string $plugin_id Optional plugin name, if not specified
+ * then it is detected from where you are calling from.
+ *
+ * @return mixed
+ * @since 1.8.0
+ * @todo make $plugin_id required in future version
*/
-function get_plugin_setting($name, $plugin_name = "") {
- $plugin = find_plugin_settings($plugin_name);
+function elgg_get_plugin_setting($name, $plugin_id = null) {
+ if ($plugin_id) {
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+ } else {
+ $plugin = elgg_get_calling_plugin_entity();
+ }
- if ($plugin) {
- return $plugin->$name;
+ if (!$plugin) {
+ return false;
}
- return false;
+ return $plugin->getSetting($name);
}
/**
- * Clear a plugin setting.
+ * Unsets a plugin setting.
+ *
+ * @param string $name The name of the setting.
+ * @param string $plugin_id Optional plugin name, if not specified
+ * then it is detected from where you are calling from.
*
- * @param string $name The name.
- * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from.
+ * @return bool
+ * @since 1.8.0
*/
-function clear_plugin_setting($name, $plugin_name = "") {
- $plugin = find_plugin_settings($plugin_name);
+function elgg_unset_plugin_setting($name, $plugin_id = null) {
+ if ($plugin_id) {
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+ } else {
+ $plugin = elgg_get_calling_plugin_entity();
+ }
- if ($plugin) {
- return remove_private_setting($plugin->guid, $name);
+ if (!$plugin) {
+ return false;
}
- return FALSE;
+ return $plugin->unsetSetting($name);
}
/**
- * Clear all plugin settings.
+ * Unsets all plugin settings for a plugin.
*
- * @param string $plugin_name Optional plugin name, if not specified then it is detected from where you are calling from.
+ * @param string $plugin_id Optional plugin name, if not specified
+ * then it is detected from where you are calling from.
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function clear_all_plugin_settings($plugin_name = "") {
- $plugin = find_plugin_settings($plugin_name);
-
- if ($plugin) {
- return remove_all_private_settings($plugin->guid);
+function elgg_unset_all_plugin_settings($plugin_id = null) {
+ if ($plugin_id) {
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+ } else {
+ $plugin = elgg_get_calling_plugin_entity();
}
- return FALSE;
-}
-
-/**
- * Return an array of installed plugins.
- */
-function get_installed_plugins() {
- global $CONFIG;
-
- $installed_plugins = array();
-
- if (!empty($CONFIG->pluginspath)) {
- $plugins = get_plugin_list();
-
- foreach($plugins as $mod) {
- // require manifest.
- if (!$manifest = load_plugin_manifest($mod)) {
- continue;
- }
- $installed_plugins[$mod] = array();
- $installed_plugins[$mod]['active'] = is_plugin_enabled($mod);
- $installed_plugins[$mod]['manifest'] = $manifest;
- }
+ if (!$plugin) {
+ return false;
}
- return $installed_plugins;
+ return $plugin->unsetAllSettings();
}
/**
- * Enable a plugin for a site (default current site)
+ * Returns entities based upon plugin settings.
+ * Takes all the options for {@see elgg_get_entities_from_private_settings()}
+ * in addition to the ones below.
+ *
+ * @param array $options Array in the format:
*
- * Important: You should regenerate simplecache and the viewpath cache after executing this function
- * otherwise you may experience view display artifacts. Do this with the following code:
+ * plugin_id => NULL|STR The plugin id. Defaults to calling plugin
*
- * elgg_view_regenerate_simplecache();
- * elgg_filepath_cache_reset();
+ * plugin_user_setting_names => NULL|ARR private setting names
*
- * @param string $plugin The plugin name.
- * @param int $site_guid The site id, if not specified then this is detected.
+ * plugin_user_setting_values => NULL|ARR metadata values
+ *
+ * plugin_user_setting_name_value_pairs => NULL|ARR (
+ * name => 'name',
+ * value => 'value',
+ * 'operand' => '=',
+ * )
+ * Currently if multiple values are sent via
+ * an array (value => array('value1', 'value2')
+ * the pair's operand will be forced to "IN".
+ *
+ * plugin_user_setting_name_value_pairs_operator => NULL|STR The operator to use for combining
+ * (name = value) OPERATOR (name = value); default AND
+ *
+ * @return mixed int If count, int. If not count, array. false on errors.
*/
-function enable_plugin($plugin, $site_guid = 0) {
- global $CONFIG, $ENABLED_PLUGINS_CACHE;
-
- $plugin = sanitise_string($plugin);
- $site_guid = (int) $site_guid;
- if ($site_guid == 0) {
- $site_guid = $CONFIG->site_guid;
+function elgg_get_entities_from_plugin_user_settings(array $options = array()) {
+ // if they're passing it don't bother
+ if (!isset($options['plugin_id'])) {
+ $options['plugin_id'] = elgg_get_calling_plugin_id();
}
- $site = get_entity($site_guid);
- if (!($site instanceof ElggSite)) {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $site_guid, "ElggSite"));
- }
+ $singulars = array('plugin_user_setting_name', 'plugin_user_setting_value',
+ 'plugin_user_setting_name_value_pair');
- if (!$plugin_info = load_plugin_manifest($plugin)) {
- return FALSE;
- }
-
- // getMetadata() doesn't return an array if only one plugin is enabled
- if ($enabled = $site->enabled_plugins) {
- if (!is_array($enabled)) {
- $enabled = array($enabled);
- }
- } else {
- $enabled = array();
- }
+ $options = elgg_normalise_plural_options_array($options, $singulars);
- $enabled[] = $plugin;
- $enabled = array_unique($enabled);
+ // rewrite plugin_user_setting_name_* to the right PS ones.
+ $map = array(
+ 'plugin_user_setting_names' => 'private_setting_names',
+ 'plugin_user_setting_values' => 'private_setting_values',
+ 'plugin_user_setting_name_value_pairs' => 'private_setting_name_value_pairs',
+ 'plugin_user_setting_name_value_pairs_operator' => 'private_setting_name_value_pairs_operator'
+ );
- if ($return = $site->setMetaData('enabled_plugins', $enabled)) {
-
- // for other plugins that want to hook into this.
- if ($return && !trigger_elgg_event('enable', 'plugin', array('plugin' => $plugin, 'manifest' => $plugin_info))) {
- $return = FALSE;
+ foreach ($map as $plugin => $private) {
+ if (!isset($options[$plugin])) {
+ continue;
}
- // for this plugin's on_enable
- if ($return && isset($plugin_info['on_enable'])) {
- // pull in the actual plugin's start so the on_enable function is callabe
- // NB: this will not run re-run the init hooks!
- $start = "{$CONFIG->pluginspath}$plugin/start.php";
- if (!file_exists($start) || !include($start)) {
- $return = FALSE;
+ if (isset($options[$private])) {
+ if (!is_array($options[$private])) {
+ $options[$private] = array($options[$private]);
}
- // need language files for the messages
- $translations = "{$CONFIG->pluginspath}$plugin/languages/";
- register_translations($translations);
- if (!is_callable($plugin_info['on_enable'])) {
- $return = FALSE;
- } else {
- $on_enable = call_user_func($plugin_info['on_enable']);
- // allow null to mean "I don't care" like other subsystems
- $return = ($on_disable === FALSE) ? FALSE : TRUE;
- }
+ $options[$private] = array_merge($options[$private], $options[$plugin]);
+ } else {
+ $options[$private] = $options[$plugin];
}
+ }
- // disable the plugin if the on_enable or trigger results failed
- if (!$return) {
- array_pop($enabled);
- $site->setMetaData('enabled_plugins', $enabled);
- }
- $ENABLED_PLUGINS_CACHE = $enabled;
- }
+ $plugin_id = $options['plugin_id'];
+ $prefix = elgg_namespace_plugin_private_setting('user_setting', '', $plugin_id);
+ $options['private_setting_name_prefix'] = $prefix;
- return $return;
+ return elgg_get_entities_from_private_settings($options);
}
/**
- * Disable a plugin for a site (default current site)
+ * Register object, plugin entities as ElggPlugin classes
*
- * Important: You should regenerate simplecache and the viewpath cache after executing this function
- * otherwise you may experience view display artifacts. Do this with the following code:
+ * @return void
+ * @access private
+ */
+function plugin_run_once() {
+ add_subtype("object", "plugin", "ElggPlugin");
+}
+
+/**
+ * Runs unit tests for the entity objects.
*
- * elgg_view_regenerate_simplecache();
- * elgg_filepath_cache_reset();
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of tests
+ * @param mixed $params Params
*
- * @param string $plugin The plugin name.
- * @param int $site_guid The site id, if not specified then this is detected.
+ * @return array
+ * @access private
*/
-function disable_plugin($plugin, $site_guid = 0) {
- global $CONFIG, $ENABLED_PLUGINS_CACHE;
-
- $plugin = sanitise_string($plugin);
- $site_guid = (int) $site_guid;
- if ($site_guid == 0) {
- $site_guid = $CONFIG->site_guid;
- }
-
- $site = get_entity($site_guid);
- if (!($site instanceof ElggSite)) {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $site_guid, "ElggSite"));
- }
-
- if (!$plugin_info = load_plugin_manifest($plugin)) {
- return FALSE;
- }
-
- // getMetadata() doesn't return an array if only one plugin is enabled
- if ($enabled = $site->enabled_plugins) {
- if (!is_array($enabled)) {
- $enabled = array($enabled);
- }
- } else {
- $enabled = array();
- }
-
- $old_enabled = $enabled;
-
- // remove the disabled plugin from the array
- if (FALSE !== $i = array_search($plugin, $enabled)) {
- unset($enabled[$i]);
- }
-
- // if we're unsetting all the plugins, this will return an empty array.
- // it will fail with FALSE, though.
- $return = (FALSE === $site->enabled_plugins = $enabled) ? FALSE : TRUE;
-
- if ($return) {
- // for other plugins that want to hook into this.
- if ($return && !trigger_elgg_event('disable', 'plugin', array('plugin' => $plugin, 'manifest' => $plugin_info))) {
- $return = FALSE;
- }
-
- // for this plugin's on_disable
- if ($return && isset($plugin_info['on_disable'])) {
- if (!is_callable($plugin_info['on_disable'])) {
- $return = FALSE;
- } else {
- $on_disable = call_user_func($plugin_info['on_disable']);
- // allow null to mean "I don't care" like other subsystems
- $return = ($on_disable === FALSE) ? FALSE : TRUE;
- }
- }
-
- // disable the plugin if the on_enable or trigger results failed
- if (!$return) {
- $site->enabled_plugins = $old_enabled;
- $ENABLED_PLUGINS_CACHE = $old_enabled;
- } else {
- $ENABLED_PLUGINS_CACHE = $enabled;
- }
- }
-
- return $return;
+function plugins_test($hook, $type, $value, $params) {
+ global $CONFIG;
+ $value[] = $CONFIG->path . 'engine/tests/api/plugins.php';
+ return $value;
}
/**
- * Return whether a plugin is enabled or not.
+ * Checks on deactivate plugin event if disabling it won't create unmet dependencies and blocks disable in such case.
*
- * @param string $plugin The plugin name.
- * @param int $site_guid The site id, if not specified then this is detected.
- * @return bool
+ * @param string $event deactivate
+ * @param string $type plugin
+ * @param array $params Parameters array containing entry with ELggPlugin instance under 'plugin_entity' key
+ * @return bool false to block plugin deactivation action
+ *
+ * @access private
*/
-function is_plugin_enabled($plugin, $site_guid = 0) {
- global $CONFIG, $ENABLED_PLUGINS_CACHE;
+function _plugins_deactivate_dependency_check($event, $type, $params) {
+ $plugin_id = $params['plugin_entity']->getManifest()->getPluginID();
+ $plugin_name = $params['plugin_entity']->getManifest()->getName();
- if (!file_exists($CONFIG->pluginspath . $plugin)) {
- return false;
- }
+ $active_plugins = elgg_get_plugins();
- $site_guid = (int) $site_guid;
- if ($site_guid == 0) {
- $site_guid = $CONFIG->site_guid;
- }
+ $dependents = array();
+ foreach ($active_plugins as $plugin) {
+ $manifest = $plugin->getManifest();
+ $requires = $manifest->getRequires();
- if (!$ENABLED_PLUGINS_CACHE) {
- $site = get_entity($site_guid);
- if (!($site instanceof ElggSite)) {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $site_guid, "ElggSite"));
- }
-
- $enabled_plugins = $site->enabled_plugins;
- if ($enabled_plugins && !is_array($enabled_plugins)) {
- $enabled_plugins = array($enabled_plugins);
+ foreach ($requires as $required) {
+ if ($required['type'] == 'plugin' && $required['name'] == $plugin_id) {
+ // there are active dependents
+ $dependents[$manifest->getPluginID()] = $plugin;
+ }
}
- $ENABLED_PLUGINS_CACHE = $enabled_plugins;
}
- foreach ($ENABLED_PLUGINS_CACHE as $e) {
- if ($e == $plugin) {
- return true;
+ if ($dependents) {
+ $list = '<ul>';
+ // construct error message and prevent disabling
+ foreach ($dependents as $dependent) {
+ $list .= '<li>' . $dependent->getManifest()->getName() . '</li>';
}
- }
+ $list .= '</ul>';
- return false;
-}
+ register_error(elgg_echo('ElggPlugin:Dependencies:ActiveDependent', array($plugin_name, $list)));
-/**
- * Run once and only once.
- */
-function plugin_run_once() {
- // Register a class
- add_subtype("object", "plugin", "ElggPlugin");
+ return false;
+ }
}
/**
- * Initialise the file modules.
- * Listens to system boot and registers any appropriate file types and classes
+ * Initialize the plugin system
+ * Listens to system init and registers actions
+ *
+ * @return void
+ * @access private
*/
function plugin_init() {
- // Now run this stuff, but only once
run_function_once("plugin_run_once");
- // Register some actions
- register_action("plugins/settings/save", false, "", true);
- register_action("plugins/usersettings/save");
+ elgg_register_plugin_hook_handler('unit_test', 'system', 'plugins_test');
+
+ // note - plugins are booted by the time this handler is registered
+ // deactivation due to error may have already occurred
+ elgg_register_event_handler('deactivate', 'plugin', '_plugins_deactivate_dependency_check');
+
+ elgg_register_action("plugins/settings/save", '', 'admin');
+ elgg_register_action("plugins/usersettings/save");
+
+ elgg_register_action('admin/plugins/activate', '', 'admin');
+ elgg_register_action('admin/plugins/deactivate', '', 'admin');
+ elgg_register_action('admin/plugins/activate_all', '', 'admin');
+ elgg_register_action('admin/plugins/deactivate_all', '', 'admin');
- register_action('admin/plugins/enable', false, "", true); // Enable
- register_action('admin/plugins/disable', false, "", true); // Disable
- register_action('admin/plugins/enableall', false, "", true); // Enable all
- register_action('admin/plugins/disableall', false, "", true); // Disable all
+ elgg_register_action('admin/plugins/set_priority', '', 'admin');
- register_action('admin/plugins/reorder', false, "", true); // Reorder
+ elgg_register_library('elgg:markdown', elgg_get_root_path() . 'vendors/markdown/markdown.php');
}
-// Register a startup event
-register_elgg_event_handler('init','system','plugin_init');
+elgg_register_event_handler('init', 'system', 'plugin_init');
diff --git a/engine/lib/private_settings.php b/engine/lib/private_settings.php
new file mode 100644
index 000000000..7541f7b3b
--- /dev/null
+++ b/engine/lib/private_settings.php
@@ -0,0 +1,414 @@
+<?php
+/**
+ * Private settings for entities
+ * Private settings provide metadata like storage of settings for plugins
+ * and users.
+ *
+ * @package Elgg.Core
+ * @subpackage PrivateSettings
+ */
+
+/**
+ * Returns entities based upon private settings. Also accepts all
+ * options available to elgg_get_entities(). Supports
+ * the singular option shortcut.
+ *
+ * @see elgg_get_entities
+ *
+ * @param array $options Array in format:
+ *
+ * private_setting_names => NULL|ARR private setting names
+ *
+ * private_setting_values => NULL|ARR metadata values
+ *
+ * private_setting_name_value_pairs => NULL|ARR (
+ * name => 'name',
+ * value => 'value',
+ * 'operand' => '=',
+ * )
+ * Currently if multiple values are sent via
+ * an array (value => array('value1', 'value2')
+ * the pair's operand will be forced to "IN".
+ *
+ * private_setting_name_value_pairs_operator => NULL|STR The operator to use for combining
+ * (name = value) OPERATOR (name = value); default AND
+ *
+ * private_setting_name_prefix => STR A prefix to apply to all private settings. Used to
+ * namespace plugin user settings or by plugins to namespace
+ * their own settings.
+ *
+ *
+ * @return mixed int If count, int. If not count, array. false on errors.
+ * @since 1.8.0
+ */
+function elgg_get_entities_from_private_settings(array $options = array()) {
+ $defaults = array(
+ 'private_setting_names' => ELGG_ENTITIES_ANY_VALUE,
+ 'private_setting_values' => ELGG_ENTITIES_ANY_VALUE,
+ 'private_setting_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE,
+ 'private_setting_name_value_pairs_operator' => 'AND',
+ 'private_setting_name_prefix' => '',
+ );
+
+ $options = array_merge($defaults, $options);
+
+ $singulars = array('private_setting_name', 'private_setting_value',
+ 'private_setting_name_value_pair');
+
+ $options = elgg_normalise_plural_options_array($options, $singulars);
+
+ $clauses = elgg_get_entity_private_settings_where_sql('e', $options['private_setting_names'],
+ $options['private_setting_values'], $options['private_setting_name_value_pairs'],
+ $options['private_setting_name_value_pairs_operator'], $options['private_setting_name_prefix']);
+
+ if ($clauses) {
+ // merge wheres to pass to get_entities()
+ if (isset($options['wheres']) && !is_array($options['wheres'])) {
+ $options['wheres'] = array($options['wheres']);
+ } elseif (!isset($options['wheres'])) {
+ $options['wheres'] = array();
+ }
+
+ $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']);
+
+ // merge joins to pass to get_entities()
+ if (isset($options['joins']) && !is_array($options['joins'])) {
+ $options['joins'] = array($options['joins']);
+ } elseif (!isset($options['joins'])) {
+ $options['joins'] = array();
+ }
+
+ $options['joins'] = array_merge($options['joins'], $clauses['joins']);
+ }
+
+ return elgg_get_entities($options);
+}
+
+/**
+ * Returns private setting name and value SQL where/join clauses for entities.
+ *
+ * @param string $table Entities table name
+ * @param array|null $names Array of names
+ * @param array|null $values Array of values
+ * @param array|null $pairs Array of names / values / operands
+ * @param string $pair_operator Operator for joining pairs where clauses
+ * @param string $name_prefix A string to prefix all names with
+ * @return array
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_get_entity_private_settings_where_sql($table, $names = NULL, $values = NULL,
+$pairs = NULL, $pair_operator = 'AND', $name_prefix = '') {
+
+ global $CONFIG;
+
+ // @todo short circuit test
+
+ $return = array (
+ 'joins' => array (),
+ 'wheres' => array(),
+ );
+
+ $return['joins'][] = "JOIN {$CONFIG->dbprefix}private_settings ps on
+ {$table}.guid = ps.entity_guid";
+
+ $wheres = array();
+
+ // get names wheres
+ $names_where = '';
+ if ($names !== NULL) {
+ if (!is_array($names)) {
+ $names = array($names);
+ }
+
+ $sanitised_names = array();
+ foreach ($names as $name) {
+ $name = $name_prefix . $name;
+ $sanitised_names[] = '\'' . sanitise_string($name) . '\'';
+ }
+
+ $names_str = implode(',', $sanitised_names);
+ if ($names_str) {
+ $names_where = "(ps.name IN ($names_str))";
+ }
+ }
+
+ // get values wheres
+ $values_where = '';
+ if ($values !== NULL) {
+ if (!is_array($values)) {
+ $values = array($values);
+ }
+
+ $sanitised_values = array();
+ foreach ($values as $value) {
+ // normalize to 0
+ if (!$value) {
+ $value = 0;
+ }
+ $sanitised_values[] = '\'' . sanitise_string($value) . '\'';
+ }
+
+ $values_str = implode(',', $sanitised_values);
+ if ($values_str) {
+ $values_where = "(ps.value IN ($values_str))";
+ }
+ }
+
+ if ($names_where && $values_where) {
+ $wheres[] = "($names_where AND $values_where)";
+ } elseif ($names_where) {
+ $wheres[] = "($names_where)";
+ } elseif ($values_where) {
+ $wheres[] = "($values_where)";
+ }
+
+ // add pairs which must be in arrays.
+ if (is_array($pairs)) {
+ // join counter for incremental joins in pairs
+ $i = 1;
+
+ // check if this is an array of pairs or just a single pair.
+ if (isset($pairs['name']) || isset($pairs['value'])) {
+ $pairs = array($pairs);
+ }
+
+ $pair_wheres = array();
+
+ foreach ($pairs as $index => $pair) {
+ // @todo move this elsewhere?
+ // support shortcut 'n' => 'v' method.
+ if (!is_array($pair)) {
+ $pair = array(
+ 'name' => $index,
+ 'value' => $pair
+ );
+ }
+
+ // must have at least a name and value
+ if (!isset($pair['name']) || !isset($pair['value'])) {
+ // @todo should probably return false.
+ continue;
+ }
+
+ if (isset($pair['operand'])) {
+ $operand = sanitise_string($pair['operand']);
+ } else {
+ $operand = ' = ';
+ }
+
+ // for comparing
+ $trimmed_operand = trim(strtolower($operand));
+
+ // if the value is an int, don't quote it because str '15' < str '5'
+ // if the operand is IN don't quote it because quoting should be done already.
+ if (is_numeric($pair['value'])) {
+ $value = sanitise_string($pair['value']);
+ } else if (is_array($pair['value'])) {
+ $values_array = array();
+
+ foreach ($pair['value'] as $pair_value) {
+ if (is_numeric($pair_value)) {
+ $values_array[] = sanitise_string($pair_value);
+ } else {
+ $values_array[] = "'" . sanitise_string($pair_value) . "'";
+ }
+ }
+
+ if ($values_array) {
+ $value = '(' . implode(', ', $values_array) . ')';
+ }
+
+ // @todo allow support for non IN operands with array of values.
+ // will have to do more silly joins.
+ $operand = 'IN';
+ } else if ($trimmed_operand == 'in') {
+ $value = "({$pair['value']})";
+ } else {
+ $value = "'" . sanitise_string($pair['value']) . "'";
+ }
+
+ $name = sanitise_string($name_prefix . $pair['name']);
+
+ // @todo The multiple joins are only needed when the operator is AND
+ $return['joins'][] = "JOIN {$CONFIG->dbprefix}private_settings ps{$i}
+ on {$table}.guid = ps{$i}.entity_guid";
+
+ $pair_wheres[] = "(ps{$i}.name = '$name' AND ps{$i}.value
+ $operand $value)";
+
+ $i++;
+ }
+
+ $where = implode(" $pair_operator ", $pair_wheres);
+ if ($where) {
+ $wheres[] = "($where)";
+ }
+ }
+
+ $where = implode(' AND ', $wheres);
+ if ($where) {
+ $return['wheres'][] = "($where)";
+ }
+
+ return $return;
+}
+
+/**
+ * Gets a private setting for an entity.
+ *
+ * Plugin authors can set private data on entities. By default
+ * private data will not be searched or exported.
+ *
+ * @internal Private data is used to store settings for plugins
+ * and user settings.
+ *
+ * @param int $entity_guid The entity GUID
+ * @param string $name The name of the setting
+ *
+ * @return mixed The setting value, or false on failure
+ * @see set_private_setting()
+ * @see get_all_private_settings()
+ * @see remove_private_setting()
+ * @see remove_all_private_settings()
+ * @link http://docs.elgg.org/DataModel/Entities/PrivateSettings
+ */
+function get_private_setting($entity_guid, $name) {
+ global $CONFIG;
+ $entity_guid = (int) $entity_guid;
+ $name = sanitise_string($name);
+
+ $entity = get_entity($entity_guid);
+ if (!$entity instanceof ElggEntity) {
+ return false;
+ }
+
+ $query = "SELECT value from {$CONFIG->dbprefix}private_settings
+ where name = '{$name}' and entity_guid = {$entity_guid}";
+ $setting = get_data_row($query);
+
+ if ($setting) {
+ return $setting->value;
+ }
+ return false;
+}
+
+/**
+ * Return an array of all private settings.
+ *
+ * @param int $entity_guid The entity GUID
+ *
+ * @return array|false
+ * @see set_private_setting()
+ * @see get_private_settings()
+ * @see remove_private_setting()
+ * @see remove_all_private_settings()
+ * @link http://docs.elgg.org/DataModel/Entities/PrivateSettings
+ */
+function get_all_private_settings($entity_guid) {
+ global $CONFIG;
+
+ $entity_guid = (int) $entity_guid;
+ $entity = get_entity($entity_guid);
+ if (!$entity instanceof ElggEntity) {
+ return false;
+ }
+
+ $query = "SELECT * from {$CONFIG->dbprefix}private_settings where entity_guid = {$entity_guid}";
+ $result = get_data($query);
+ if ($result) {
+ $return = array();
+ foreach ($result as $r) {
+ $return[$r->name] = $r->value;
+ }
+
+ return $return;
+ }
+
+ return false;
+}
+
+/**
+ * Sets a private setting for an entity.
+ *
+ * @param int $entity_guid The entity GUID
+ * @param string $name The name of the setting
+ * @param string $value The value of the setting
+ *
+ * @return bool
+ * @see get_private_setting()
+ * @see get_all_private_settings()
+ * @see remove_private_setting()
+ * @see remove_all_private_settings()
+ * @link http://docs.elgg.org/DataModel/Entities/PrivateSettings
+ */
+function set_private_setting($entity_guid, $name, $value) {
+ global $CONFIG;
+
+ $entity_guid = (int) $entity_guid;
+ $name = sanitise_string($name);
+ $value = sanitise_string($value);
+
+ $result = insert_data("INSERT into {$CONFIG->dbprefix}private_settings
+ (entity_guid, name, value) VALUES
+ ($entity_guid, '$name', '$value')
+ ON DUPLICATE KEY UPDATE value='$value'");
+
+ return $result !== false;
+}
+
+/**
+ * Deletes a private setting for an entity.
+ *
+ * @param int $entity_guid The Entity GUID
+ * @param string $name The name of the setting
+ *
+ * @return bool
+ * @see get_private_setting()
+ * @see get_all_private_settings()
+ * @see set_private_setting()
+ * @see remove_all_private_settings()
+ * @link http://docs.elgg.org/DataModel/Entities/PrivateSettings
+ */
+function remove_private_setting($entity_guid, $name) {
+ global $CONFIG;
+
+ $entity_guid = (int) $entity_guid;
+
+ $entity = get_entity($entity_guid);
+ if (!$entity instanceof ElggEntity) {
+ return false;
+ }
+
+ $name = sanitise_string($name);
+
+ return delete_data("DELETE from {$CONFIG->dbprefix}private_settings
+ WHERE name = '{$name}'
+ AND entity_guid = {$entity_guid}");
+}
+
+/**
+ * Deletes all private settings for an entity.
+ *
+ * @param int $entity_guid The Entity GUID
+ *
+ * @return bool
+ * @see get_private_setting()
+ * @see get_all_private_settings()
+ * @see set_private_setting()
+ * @see remove_private_settings()
+ * @link http://docs.elgg.org/DataModel/Entities/PrivateSettings
+ */
+function remove_all_private_settings($entity_guid) {
+ global $CONFIG;
+
+ $entity_guid = (int) $entity_guid;
+
+ $entity = get_entity($entity_guid);
+ if (!$entity instanceof ElggEntity) {
+ return false;
+ }
+
+ return delete_data("DELETE from {$CONFIG->dbprefix}private_settings
+ WHERE entity_guid = {$entity_guid}");
+}
diff --git a/engine/lib/query.php b/engine/lib/query.php
deleted file mode 100644
index 619a2d288..000000000
--- a/engine/lib/query.php
+++ /dev/null
@@ -1,889 +0,0 @@
-<?php
- /**
- * Elgg database query
- * Contains a wrapper for performing database queries in a structured way.
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-
-
- /**
- * @class QueryComponent Query component superclass.
- * Component of a query.
- * @author Curverider Ltd
- * @see Query
- */
- abstract class QueryComponent
- {
- /**
- * Associative array of fields and values
- */
- private $fields;
-
- function __construct()
- {
- $this->fields = array();
- }
-
- /**
- * Class member get overloading
- *
- * @param string $name
- * @return mixed
- */
- function __get($name) {
- return $this->fields[$name];
- }
-
- /**
- * Class member set overloading
- *
- * @param string $name
- * @param mixed $value
- * @return void
- */
- function __set($name, $value) {
- $this->fields[$name] = $value;
-
- return true;
- }
- }
-
- /**
- * @class SelectFieldQueryComponent Class representing a select field.
- * This class represents a select field component.
- * @author Curverider Ltd
- * @see Query
- */
- class SelectFieldQueryComponent extends QueryComponent
- {
- /**
- * Construct a select field component
- *
- * @param string $table The table containing the field.
- * @param string $field The field or "*"
- */
- function __construct($table, $field)
- {
- global $CONFIG;
-
- $this->table = $CONFIG->dbprefix . sanitise_string($table);
- $this->field = sanitise_string($field);
- }
-
- function __toString()
- {
- return "{$this->table}.{$this->field}";
- }
- }
-
- /**
- * @class LimitOffsetQueryComponent
- * Limit and offset clauses of a query.
- * @author Curverider Ltd
- * @see Query
- */
- class LimitOffsetQueryComponent extends QueryComponent
- {
- /**
- * Specify a limit and an offset.
- *
- * @param int $limit The limit.
- * @param int $offset The offset.
- */
- function __construct($limit = 25, $offset = 0)
- {
- $this->limit = (int)$limit;
- $this->offset = (int)$offset;
- }
-
- function __toString()
- {
- return "limit {$this->offset}, {$this->limit}";
- }
- }
-
- /**
- * @class OrderQueryComponent
- * Order the query results.
- * @author Curverider Ltd
- * @see Query
- */
- class OrderQueryComponent extends QueryComponent
- {
- function __construct($table, $field, $order = "asc")
- {
- global $CONFIG;
-
- $this->table = $CONFIG->dbprefix . sanitise_string($table);
- $this->field = sanitise_string($field);
- $this->order = sanitise_string($order);
- }
-
- function __toString()
- {
- return "order by {$this->table}.{$this->field} {$this->order}";
- }
- }
-
- /**
- * @class TableQueryComponent
- * List of tables to select from or insert into.
- * @author Curverider Ltd
- * @see Query
- */
- class TableQueryComponent extends QueryComponent
- {
- function __construct($table)
- {
- global $CONFIG;
-
- $this->table = $CONFIG->dbprefix . sanitise_string($table);
- }
-
- function __toString()
- {
- return $this->table;
- }
- }
-
- /**
- * @class AccessControlQueryComponent
- * Access control component.
- * @author Curverider Ltd
- * @see Query
- */
- class AccessControlQueryComponent extends QueryComponent
- {
- /**
- * Construct the ACL.
- *
- * @param string $acl_table The table where the access control field is.
- * @param string $acl_field The field containing the access control.
- * @param string $object_owner_table The table containing the owner information for the stuff you're retrieving.
- * @param string $object_owner_id_field The field in $object_owner_table containing the owner information
- */
- function __construct($acl_table = "entities", $acl_field = "access_id", $object_owner_table = "entities", $object_owner_id_field = "owner_guid")
- {
- global $CONFIG;
-
- $this->acl_table = $CONFIG->dbprefix . sanitise_string($acl_table);
- $this->acl_field = sanitise_string($acl_field);
- $this->object_owner_table = $CONFIG->dbprefix . sanitise_string($object_owner_table);
- $this->object_owner_id_field = sanitise_string($object_owner_id_field);
- }
-
- function __toString()
- {
- //$access = get_access_list();
- // KJ - changed to use get_access_sql_suffix
- // Note: currently get_access_sql_suffix is hardwired to use
- // $acl_field = "access_id", $object_owner_table = $acl_table, and
- // $object_owner_id_field = "owner_guid"
- // TODO: recode get_access_sql_suffix to make it possible to specify alternate field names
- return "and ".get_access_sql_suffix($this->acl_table); // Add access controls
-
- //return "and ({$this->acl_table}.{$this->acl_field} in {$access} or ({$this->acl_table}.{$this->acl_field} = 0 and {$this->object_owner_table}.{$this->object_owner_id_field} = {$_SESSION['id']}))";
- }
- }
-
- /**
- * @class JoinQueryComponent Join query.
- * Represents a join query.
- * @author Curverider Ltd
- * @see Query
- */
- class JoinQueryComponent extends QueryComponent
- {
- /**
- * Construct a join query.
- * @param string $table Table one to join...
- * @param string $field Field 1 with...
- * @param string $table2 Table 2 ...
- * @param string $field2 Field...
- * @param string $operator Using this operator
- */
- function __construct($table1, $field1, $table2, $field2, $operator = "=")
- {
- global $CONFIG;
-
- $this->table1 = $CONFIG->dbprefix . sanitise_string($table1);
- $this->field1 = sanitise_string($field1);
- $this->table2 = $CONFIG->dbprefix . sanitise_string($table2);
- $this->field2 = sanitise_string($field2);
- $this->operator = sanitise_string($operator);
- }
-
- function __toString()
- {
- return "join {$this->table2} on {$this->$table}.{$this->$field} {$this->$operator} {$this->$table2}.{$this->$field2}";
- }
- }
-
- /**
- * @class SetQueryComponent Set query.
- * Represents an update set query.
- * @author Curverider Ltd
- * @see Query
- */
- class SetQueryComponent extends QueryComponent
- {
- /**
- * Construct a setting query
- *
- * @param string $table The table to modify
- * @param string $field The field to modify
- * @param mixed $value The value to set it to
- */
- function __construct($table, $field, $value)
- {
- global $CONFIG;
-
- $this->table = $CONFIG->dbprefix . sanitise_string($table);
- $this->field = sanitise_string($field);
- if (is_numeric($value))
- $this->value = (int)$value;
- else
- $this->value = "'".sanitise_string($value)."'";
- }
-
- function __toString()
- {
- return "{$this->table}.{$this->field}={$this->value}";
- }
- }
-
- /**
- * @class WhereQueryComponent
- * A component of a where query.
- * @author Curverider Ltd
- * @see Query
- */
- class WhereQueryComponent extends QueryComponent
- {
- /**
- * A where query.
- *
- * @param string $left_table The table on the left of the operator
- * @param string $left_field The left field
- * @param string $operator The operator eg "=" or "<"
- * @param string $right_table The table on the right of the operator
- * @param string $right_field The right field
- * @param string $link_operator How this where clause links with the previous clause, eg. "and" "or"
- */
- function __construct($left_table, $left_field, $operator, $right_table, $right_field, $link_operator = "and")
- {
- global $CONFIG;
-
- $this->link_operator = sanitise_string($link_operator);
- $this->left_table = $CONFIG->dbprefix . sanitise_string($left_table);
- $this->left_field = sanitise_string($left_field);
- $this->operator = sanitise_string($operator);
- $this->right_table = $CONFIG->dbprefix . sanitise_string($right_table);
- $this->right_field = sanitise_string($right_field);
- }
-
- /**
- * Return the SQL without the link operator.
- */
- public function toStringNoLink()
- {
- return "{$this->left_table }.{$this->left_field} {$this->operator} {$this->right_table}.{$this->right_field}";
- }
-
- function __toString()
- {
- return "{$this->link_operator} " . $this->toStringNoLink();
- }
- }
-
- /**
- * @class WhereStaticQueryComponent
- * A component of a where query where there is no right hand table, rather a static value.
- * @author Curverider Ltd
- * @see Query
- */
- class WhereStaticQueryComponent extends WhereQueryComponent
- {
- /**
- * A where query.
- *
- * @param string $left_table The table on the left of the operator
- * @param string $left_field The left field
- * @param string $operator The operator eg "=" or "<"
- * @param string $value The value
- * @param string $link_operator How this where clause links with the previous clause, eg. "and" "or"
- */
- function __construct($left_table, $left_field, $operator, $value, $link_operator = "and")
- {
- global $CONFIG;
-
- $this->link_operator = sanitise_string($link_operator);
- $this->left_table = $CONFIG->dbprefix . sanitise_string($left_table);
- $this->left_field = sanitise_string($left_field);
- $this->operator = sanitise_string($operator);
- if (is_numeric($value))
- $this->value = (int)$value;
- else
- $this->value = "'".sanitise_string($value)."'";
- }
-
- /**
- * Return the SQL without the link operator.
- */
- public function toStringNoLink()
- {
- return "{$this->left_table }.{$this->left_field} {$this->operator} {$this->value}";
- }
- }
-
- /**
- * @class WhereSetQueryComponent
- * A where query that may contain other where queries (in brackets).
- * @author Curverider Ltd
- * @see Query
- */
- class WhereSetQueryComponent extends WhereQueryComponent
- {
- /**
- * Construct a subset of wheres.
- *
- * @param array $wheres An array of WhereQueryComponent
- * @param string $link_operator How this where clause links with the previous clause, eg. "and" "or"
- */
- function __construct(array $wheres, $link_operator = "and")
- {
- $this->link_operator = sanitise_string($link_operator);
- $this->wheres = $wheres;
- }
-
- public function toStringNoLink()
- {
- $cnt = 0;
- $string = " (";
- foreach ($this->wheres as $where) {
- if (!($where instanceof WhereQueryComponent))
- throw new DatabaseException(elgg_echo('DatabaseException:WhereSetNonQuery'));
-
- if (!$cnt)
- $string.= $where->toStringNoLink();
- else
- $string.=" $where ";
-
- $cnt ++;
- }
- $string .= ")";
-
- return $string;
- }
- }
-
- /**
- * @class QueryTypeQueryComponent
- * What type of query is this?
- * @author Curverider Ltd
- * @see Query
- */
- abstract class QueryTypeQueryComponent extends QueryComponent
- {
- function __toString()
- {
- return $this->query_type;
- }
- }
-
- /**
- * @class SelectQueryTypeQueryComponent
- * A select query.
- * @author Curverider Ltd
- * @see Query
- */
- class SelectQueryTypeQueryComponent extends QueryTypeQueryComponent
- {
- function __construct()
- {
- $this->query_type = "SELECT";
- }
- }
-
- /**
- * @class InsertQueryTypeQueryComponent
- * An insert query.
- * @author Curverider Ltd
- * @see Query
- */
- class InsertQueryTypeQueryComponent extends QueryTypeQueryComponent
- {
- function __construct()
- {
- $this->query_type = "INSERT INTO";
- }
- }
-
- /**
- * @class DeleteQueryTypeQueryComponent
- * A delete query.
- * @author Curverider Ltd
- * @see Query
- */
- class DeleteQueryTypeQueryComponent extends QueryTypeQueryComponent
- {
- function __construct()
- {
- $this->query_type = "DELETE FROM";
- }
- }
-
- /**
- * @class UpdateQueryTypeQueryComponent
- * An update query.
- * @author Curverider Ltd
- * @see Query
- */
- class UpdateQueryTypeQueryComponent extends QueryTypeQueryComponent
- {
- function __construct()
- {
- $this->query_type = "UPDATE";
- }
- }
-
- /**
- * @class Query Provides a framework to construct complex queries in a safer environment.
- *
- * The usage of this class depends on the type of query you are executing, but the basic idea is to
- * construct a query out of pluggable classes.
- *
- * Once constructed SQL can be generated using the toString method, this should happen automatically
- * if you pass the Query object to get_data or similar.
- *
- * To construct a query, create a new Query() object and begin populating it with the various classes
- * that define the various aspects of the query.
- *
- * Notes:
- * - You do not have to specify things in any particular order, provided you specify all required
- * components.
- * - With database tables you do not have to specify your db prefix, this will be added automatically.
- * - When constructing your query keep an eye on the error log - any problems will get spit out here.
- * Note also that __toString won't let you throw Exceptions (!!!) so these are caught and echoed to
- * the log instead.
- *
- * Here is an example of a select query which requests some data out of the entities table with an
- * order and limit that uses a subset where and some normal where queries:
- *
- * <blockquote>
- * // Construct the query
- * $query = new Query();
- *
- * // Say which table we're interested in
- * $query->addTable(new TableQueryComponent("entities"));
- *
- * // What fields are we interested in
- * $query->addSelectField(new SelectFieldQueryComponent("entities","*"));
- *
- * // Add access control (Default access control uses default fields on entities table.
- * // Note that it will error without something specified here!
- * $query->setAccessControl(new AccessControlQueryComponent());
- *
- * // Set a limit and offset, may be omitted.
- * $query->setLimitAndOffset(new LimitOffsetQueryComponent(10,0));
- *
- * // Specify the order, may be omitted
- * $query->setOrder(new OrderQueryComponent("entities", "subtype", "desc"));
- *
- * // Construct a where query
- * //
- * // This demonstrates a WhereSet which lets you have sub wheres, a
- * // WhereStatic which lets you compare a table field against a value and a
- * // Where which lets you compare a table/field with another table/field.
- * $query->addWhere(
- * new WhereSetQueryComponent(
- * array(
- * new WhereStaticQueryComponent("entities", "subtype","=", 1),
- * new WhereQueryComponent("entities","subtype","=", "entities", "subtype")
- * )
- * )
- * );
- *
- * get_data($query);
- * </blockquote>
- *
- * @author Curverider Ltd
- */
- class Query
- {
-
- /// The limit of the query
- private $limit_and_offset;
-
- /// Fields to return on a query
- private $fields;
-
- /// Tables to use in a from query
- private $tables;
-
- /// Join tables
- private $joins;
-
- /// Set values
- private $sets;
-
- /// Where query
- private $where;
-
- /// Order by
- private $order;
-
- /// The query type
- private $query_type;
-
- /// ACL
- private $access_control;
-
- /**
- * Construct query & initialise variables
- */
- function __construct()
- {
- $this->fields = array();
- $this->tables = array();
- $this->joins = array();
- $this->where = array();
- $this->sets = array();
-
- $this->setQueryType(new SelectQueryTypeQueryComponent());
- }
-
- /**
- * Add limits and offsets to the query.
- *
- * @param LimitOffsetQueryComponent $component The limit and offset.
- */
- public function setLimitAndOffset(LimitOffsetQueryComponent $component) { $this->limit_and_offset = $component; }
-
- /**
- * Reset and set the field to the select statement.
- *
- * @param SelectFieldQueryComponent $component Table and field component.
- */
- public function setSelectField(SelectFieldQueryComponent $component)
- {
- $this->fields = array();
- return $this->addSelectField($component);
- }
-
- /**
- * Add a select field.
- *
- * @param SelectFieldQueryComponent $component Add a component.
- */
- public function addSelectField(SelectFieldQueryComponent $component) { $this->fields[] = $component; }
-
- /**
- * Add a join to the component.
- *
- * @param JoinQueryComponent $component The join.
- */
- public function addJoin(JoinQueryComponent $component) { $this->joins[] = $component; }
-
- /**
- * Set a field value in an update or insert statement.
- *
- * @param SetQueryComponent $component Fields to set.
- */
- public function addSet(SetQueryComponent $component) { $this->sets[] = $component; }
-
- /**
- * Set the query type, i.e. "select", "update", "insert" & "delete".
- *
- * @param QueryTypeQueryComponent $component The query type.
- */
- public function setQueryType(QueryTypeQueryComponent $component) { $this->query_type = $component; }
-
- /**
- * Attach an order component.
- *
- * @param OrderQueryComponent $component The order component.
- */
- public function setOrder(OrderQueryComponent $component) { $this->order = $component; }
-
- /**
- * Add a table to the query.
- *
- * @param TableQueryComponent $component Table to add.
- */
- public function addTable(TableQueryComponent $component) { $this->tables[] = $component; }
-
- /**
- * Add a where clause to the query.
- *
- * @param WhereQueryComponent $component The where component
- */
- public function addWhere(WhereQueryComponent $component) { $this->where[] = $component; }
-
- /**
- * Set access control.
- *
- * @param AccessControlQueryComponent $component Access control.
- */
- public function setAccessControl(AccessControlQueryComponent $component) { $this->access_control = $component; }
-
- public function __toString()
- {
- global $CONFIG;
-
- $sql = "";
-
- try
- {
- // Query prefix & fields
- if (!empty($this->query_type))
- {
- $sql .= "{$this->query_type} ";
-
- if (!empty($this->fields))
- {
- $fields = "";
-
- foreach ($this->fields as $field)
- $fields .= "$field";
-
- $sql .= " $fields from ";
- }
- else
- throw new DatabaseException(elgg_echo('DatabaseException:SelectFieldsMissing'));
- }
- else
- throw new DatabaseException(elgg_echo('DatabaseException:UnspecifiedQueryType'));
-
- // Tables
- if (!empty($this->tables))
- {
- foreach($this->tables as $table)
- $sql .= "$table, ";
-
- $sql = trim($sql, ", ");
- }
- else
- throw new DatabaseException(elgg_echo('DatabaseException:NoTablesSpecified'));
-
- // Joins on select queries
- if ($this->query_type->query_type == 'select')
- {
- if (!empty($this->joins))
- {
- foreach($this->joins as $join)
- $sql .= "$join ";
- }
- }
-
- // Setting values
- if (
- ($this->query_type->query_type == 'update') ||
- ($this->query_type->query_type == 'insert')
- )
- {
- $sql .= "set ";
-
- foreach ($this->sets as $set)
- $sql .= "$set, ";
-
- $sql = trim($sql, ", ") . " ";
- }
-
- // Where
- if (!empty($this->where))
- {
- $sql .= " where 1 ";
-
- foreach ($this->where as $where)
- $sql .= "$where ";
- }
-
- // Access control
- if (!empty($this->access_control))
- {
-
- // Catch missing Where
- if (empty($this->where))
- $sql .= " where 1 ";
-
- $sql .= "{$this->access_control} ";
- }
- else
- throw new DatabaseException(elgg_echo('DatabaseException:NoACL'));
-
- // Order by
- if (!empty($this->order))
- $sql .= "{$this->order} ";
-
- // Limits
- if (!empty($this->limit_and_offset))
- $sql .= "{$this->limit_and_offset} ";
-
-
-
- } catch (Exception $e) {
- trigger_error($e, E_USER_WARNING);
- }
-
-
- return $sql;
- }
-
- }
-
- /**
- * @class SimpleQuery A wrapper for Query which provides simple interface for common functions.
- *
- * This class provides simple interface functions for constructing a (reasonably) standard database
- * query.
- *
- * The constructor for this class sets a number of defaults, for example sets default access controls
- * and a limit and offset - to change this then set it manually.
- *
- * @author Curverider Ltd
- * @see Query
- */
- class SimpleQuery extends Query
- {
- function __construct()
- {
- parent::__construct();
-
- // Set a default query type (select)
- $this->simpleQueryType();
-
- // Set a default access control
- $this->simpleAccessControl();
-
- // Set default limit and offset
- $this->simpleLimitAndOffset();
- }
-
- /**
- * Set the query type.
- *
- * @param string $type The type of search - available are "select", "update", "delete", "insert".
- */
- public function simpleQueryType($type = "select")
- {
- $type = strtolower(sanitise_string($type));
-
- switch ($type)
- {
- case "insert" :
- return $this->setQueryType(InsertQueryTypeQueryComponent());
- break;
- case "delete" :
- return $this->setQueryType(DeleteQueryTypeQueryComponent());
- break;
- case "update" :
- return $this->setQueryType(UpdateQueryTypeQueryComponent());
- break;
- default: return $this->setQueryType(SelectQueryTypeQueryComponent());
- }
- }
-
- /**
- * Set a field to query in a select statement.
- *
- * @param string $table Table to query.
- * @param string $field Field in that table.
- */
- public function simpleSelectField($table, $field) { return $this->setSelectField(new SelectFieldQueryComponent($table, $field)); }
-
- /**
- * Add a select field to query in a select statement.
- *
- * @param string $table Table to query.
- * @param string $field Field in that table.
- */
- public function simpleAddSelectField($table, $field) { return $this->addSelectField(new SelectFieldQueryComponent($table, $field)); }
-
- /**
- * Add a set value to an update query.
- *
- * @param string $table The table to update.
- * @param string $field The field in the table.
- * @param mixed $value The value to set it to.
- */
- public function simpleSet($table, $field, $value) { return $this->addSet(new SetQueryComponent($table, $field, $value)); }
-
- /**
- * Add a join to the table.
- *
- * @param string $table Table one to join...
- * @param string $field Field 1 with...
- * @param string $table2 Table 2 ...
- * @param string $field2 Field...
- * @param string $operator Using this operator
- */
- public function simpleJoin($table1, $field1, $table2, $field2, $operator = "=") { return $this->addJoin(new JoinQueryComponent($table1, $field1, $table2, $field2, $operator)); }
-
- /**
- * Add a table to the query.
- *
- * @param string $table The table.
- */
- public function simpleTable($table) { return $this->addTable(new TableQueryComponent($table)); }
-
- /**
- * Compare one table/field to another table/field.
- *
- * @param string $left_table The table on the left of the operator
- * @param string $left_field The left field
- * @param string $operator The operator eg "=" or "<"
- * @param string $right_table The table on the right of the operator
- * @param string $right_field The right field
- * @param string $link_operator How this where clause links with the previous clause, eg. "and" "or"
- */
- public function simpleWhereOnTable($left_table, $left_field, $operator, $right_table, $right_field, $link_operator = "and") { return $this->addWhere(new WhereQueryComponent($left_table, $left_field, $operator, $right_table, $right_field, $link_operator)); }
-
- /**
- * Compare one table/field to a value.
- *
- * @param string $left_table The table on the left of the operator
- * @param string $left_field The left field
- * @param string $operator The operator eg "=" or "<"
- * @param string $value The value
- * @param string $link_operator How this where clause links with the previous clause, eg. "and" "or"
- */
- public function simpleWhereOnValue($left_table, $left_field, $operator, $value, $link_operator = "and") { return $this->addWhere(new WhereStaticQueryComponent($left_table, $left_field, $operator, $value, $link_operator)); }
-
- /**
- * Set access control.
- *
- * @param string $acl_table The table where the access control field is.
- * @param string $acl_field The field containing the access control.
- * @param string $object_owner_id_field The field in $object_owner_table containing the owner information.
- */
- public function simpleAccessControl($acl_table = "entities", $acl_field = "access_id", $object_owner_id_field = "owner_guid") { return $this->setAccessControl(new AccessControlQueryComponent($acl_table, $acl_field, $acl_table, $object_owner_id_field)); }
-
- /**
- * Set the limit and offset.
- *
- * @param int $limit The limit.
- * @param int $offset The offset.
- */
- public function simpleLimitAndOffset($limit = 25, $offset = 0) { return $this->setLimitAndOffset(new LimitOffsetQueryComponent($limit, $offset)); }
-
- /**
- * Set the order query.
- *
- * @param string $table The table to query
- * @param string $field The field to query
- * @param string $order Order the query
- */
- public function simpleOrder($table, $field, $order = "desc")
- {
- $table = sanitise_string($table);
- $field = sanitise_string($field);
- $order = strtolower(sanitise_string($order));
-
- return $this->setOrder(new OrderQueryComponent($table, $field, $order)); break;
- }
- }
diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php
index f813cacba..b0cd627fc 100644
--- a/engine/lib/relationships.php
+++ b/engine/lib/relationships.php
@@ -3,304 +3,17 @@
* Elgg relationships.
* Stub containing relationship functions, making import and export easier.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage DataModel.Relationship
*/
/**
- * Relationship class.
- *
- * @author Curverider Ltd
- * @package Elgg
- * @subpackage Core
- */
-class ElggRelationship implements
- Importable,
- Exportable,
- Loggable, // Can events related to this object class be logged
- Iterator, // Override foreach behaviour
- ArrayAccess // Override for array access
- {
- /**
- * This contains the site's main properties (id, etc)
- * @var array
- */
- protected $attributes;
-
- /**
- * Construct a new site object, optionally from a given id value or row.
- *
- * @param mixed $id
- */
- function __construct($id = null) {
- $this->attributes = array();
-
- if (!empty($id)) {
- if ($id instanceof stdClass) {
- $relationship = $id; // Create from db row
- } else {
- $relationship = get_relationship($id);
- }
-
- if ($relationship) {
- $objarray = (array) $relationship;
- foreach($objarray as $key => $value) {
- $this->attributes[$key] = $value;
- }
- }
- }
- }
-
- /**
- * Class member get overloading
- *
- * @param string $name
- * @return mixed
- */
- function __get($name) {
- if (isset($this->attributes[$name])) {
- return $this->attributes[$name];
- }
-
- return null;
- }
-
- /**
- * Class member set overloading
- *
- * @param string $name
- * @param mixed $value
- * @return mixed
- */
- function __set($name, $value) {
- $this->attributes[$name] = $value;
- return true;
- }
-
- /**
- * Save the relationship
- *
- * @return int the relationship id
- */
- public function save() {
- if ($this->id > 0) {
- delete_relationship($this->id);
- }
-
- $this->id = add_entity_relationship($this->guid_one, $this->relationship, $this->guid_two);
- if (!$this->id) {
- throw new IOException(sprintf(elgg_new('IOException:UnableToSaveNew'), get_class()));
- }
-
- return $this->id;
- }
-
- /**
- * Delete a given relationship.
- */
- public function delete() {
- return delete_relationship($this->id);
- }
-
- /**
- * Get a URL for this relationship.
- *
- * @return string
- */
- public function getURL() {
- return get_relationship_url($this->id);
- }
-
- // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an array of fields which can be exported.
- */
- public function getExportableValues() {
- return array(
- 'id',
- 'guid_one',
- 'relationship',
- 'guid_two'
- );
- }
-
- /**
- * Export this relationship
- *
- * @return array
- */
- public function export() {
- $uuid = get_uuid_from_object($this);
- $relationship = new ODDRelationship(
- guid_to_uuid($this->guid_one),
- $this->relationship,
- guid_to_uuid($this->guid_two)
- );
-
- $relationship->setAttribute('uuid', $uuid);
-
- return $relationship;
- }
-
- // IMPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Import a relationship
- *
- * @param array $data
- * @param int $version
- * @return ElggRelationship
- * @throws ImportException
- */
- public function import(ODD $data) {
- if (!($element instanceof ODDRelationship)) {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnexpectedODDClass'));
- }
-
- $uuid_one = $data->getAttribute('uuid1');
- $uuid_two = $data->getAttribute('uuid2');
-
- // See if this entity has already been imported, if so then we need to link to it
- $entity1 = get_entity_from_uuid($uuid_one);
- $entity2 = get_entity_from_uuid($uuid_two);
- if (($entity1) && ($entity2)) {
- // Set the item ID
- $this->attributes['guid_one'] = $entity1->getGUID();
- $this->attributes['guid_two'] = $entity2->getGUID();
-
- // Map verb to relationship
- //$verb = $data->getAttribute('verb');
- //$relationship = get_relationship_from_verb($verb);
- $relationship = $data->getAttribute('type');
-
- if ($relationship) {
- $this->attributes['relationship'] = $relationship;
- // save
- $result = $this->save();
- if (!$result) {
- throw new ImportException(sprintf(elgg_echo('ImportException:ProblemSaving'), get_class()));
- }
-
- return $this;
- }
- }
- }
-
- // SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an identification for the object for storage in the system log.
- * This id must be an integer.
- *
- * @return int
- */
- public function getSystemLogID() {
- return $this->id;
- }
-
- /**
- * Return the class name of the object.
- */
- public function getClassName() {
- return get_class($this);
- }
-
- /**
- * For a given ID, return the object associated with it.
- * This is used by the river functionality primarily.
- * This is useful for checking access permissions etc on objects.
- */
- public function getObjectFromID($id) {
- return get_relationship($id);
- }
-
- /**
- * Return the GUID of the owner of this object.
- */
- public function getObjectOwnerGUID() {
- return $this->owner_guid;
- }
-
- /**
- * Return a type of the object - eg. object, group, user, relationship, metadata, annotation etc
- */
- public function getType() {
- return 'relationship';
- }
-
- /**
- * Return a subtype. For metadata & annotations this is the 'name' and for relationship this is the relationship type.
- */
- public function getSubtype() {
- return $this->relationship;
- }
-
- // ITERATOR INTERFACE //////////////////////////////////////////////////////////////
- /*
- * This lets an entity's attributes be displayed using foreach as a normal array.
- * Example: http://www.sitepoint.com/print/php5-standard-library
- */
-
- private $valid = FALSE;
-
- function rewind() {
- $this->valid = (FALSE !== reset($this->attributes));
- }
-
- function current() {
- return current($this->attributes);
- }
-
- function key() {
- return key($this->attributes);
- }
-
- function next() {
- $this->valid = (FALSE !== next($this->attributes));
- }
-
- function valid() {
- return $this->valid;
- }
-
- // ARRAY ACCESS INTERFACE //////////////////////////////////////////////////////////
- /*
- * This lets an entity's attributes be accessed like an associative array.
- * Example: http://www.sitepoint.com/print/php5-standard-library
- */
-
- function offsetSet($key, $value) {
- if ( array_key_exists($key, $this->attributes) ) {
- $this->attributes[$key] = $value;
- }
- }
-
- function offsetGet($key) {
- if ( array_key_exists($key, $this->attributes) ) {
- return $this->attributes[$key];
- }
- }
-
- function offsetUnset($key) {
- if ( array_key_exists($key, $this->attributes) ) {
- $this->attributes[$key] = ""; // Full unsetting is dangerious for our objects
- }
- }
-
- function offsetExists($offset) {
- return array_key_exists($offset, $this->attributes);
- }
-}
-
-
-/**
* Convert a database row to a new ElggRelationship
*
- * @param stdClass $row
- * @return stdClass or ElggMetadata
+ * @param stdClass $row Database row from the relationship table
+ *
+ * @return ElggRelationship|stdClass
+ * @access private
*/
function row_to_elggrelationship($row) {
if (!($row instanceof stdClass)) {
@@ -313,20 +26,25 @@ function row_to_elggrelationship($row) {
/**
* Return a relationship.
*
- * @param int $id
+ * @param int $id The ID of a relationship
+ *
+ * @return ElggRelationship|false
*/
function get_relationship($id) {
global $CONFIG;
$id = (int)$id;
- return row_to_elggrelationship(get_data_row("SELECT * from {$CONFIG->dbprefix}entity_relationships where id=$id"));
+ $query = "SELECT * from {$CONFIG->dbprefix}entity_relationships where id=$id";
+ return row_to_elggrelationship(get_data_row($query));
}
/**
* Delete a specific relationship.
*
- * @param int $id
+ * @param int $id The relationship ID
+ *
+ * @return bool
*/
function delete_relationship($id) {
global $CONFIG;
@@ -335,7 +53,7 @@ function delete_relationship($id) {
$relationship = get_relationship($id);
- if (trigger_elgg_event('delete', 'relationship', $relationship)) {
+ if (elgg_trigger_event('delete', 'relationship', $relationship)) {
return delete_data("delete from {$CONFIG->dbprefix}entity_relationships where id=$id");
}
@@ -348,9 +66,11 @@ function delete_relationship($id) {
*
* This function lets you make the statement "$guid_one is a $relationship of $guid_two".
*
- * @param int $guid_one
- * @param string $relationship
- * @param int $guid_two
+ * @param int $guid_one First GUID
+ * @param string $relationship Relationship name
+ * @param int $guid_two Second GUID
+ *
+ * @return bool
*/
function add_entity_relationship($guid_one, $relationship, $guid_two) {
global $CONFIG;
@@ -358,17 +78,20 @@ function add_entity_relationship($guid_one, $relationship, $guid_two) {
$guid_one = (int)$guid_one;
$relationship = sanitise_string($relationship);
$guid_two = (int)$guid_two;
+ $time = time();
// Check for duplicates
if (check_entity_relationship($guid_one, $relationship, $guid_two)) {
return false;
}
- $result = insert_data("INSERT into {$CONFIG->dbprefix}entity_relationships (guid_one, relationship, guid_two) values ($guid_one, '$relationship', $guid_two)");
+ $result = insert_data("INSERT into {$CONFIG->dbprefix}entity_relationships
+ (guid_one, relationship, guid_two, time_created)
+ values ($guid_one, '$relationship', $guid_two, $time)");
- if ($result!==false) {
+ if ($result !== false) {
$obj = get_relationship($result);
- if (trigger_elgg_event('create', $relationship, $obj)) {
+ if (elgg_trigger_event('create', $relationship, $obj)) {
return true;
} else {
delete_relationship($result);
@@ -379,12 +102,14 @@ function add_entity_relationship($guid_one, $relationship, $guid_two) {
}
/**
- * Determine whether or not a relationship between two entities exists and returns the relationship object if it does
+ * Determine if a relationship between two entities exists
+ * and returns the relationship object if it does
*
- * @param int $guid_one The GUID of the entity "owning" the relationship
+ * @param int $guid_one The GUID of the entity "owning" the relationship
* @param string $relationship The type of relationship
- * @param int $guid_two The GUID of the entity the relationship is with
- * @return object|false Depending on success
+ * @param int $guid_two The GUID of the entity the relationship is with
+ *
+ * @return ElggRelationship|false Depending on success
*/
function check_entity_relationship($guid_one, $relationship, $guid_two) {
global $CONFIG;
@@ -393,7 +118,13 @@ function check_entity_relationship($guid_one, $relationship, $guid_two) {
$relationship = sanitise_string($relationship);
$guid_two = (int)$guid_two;
- if ($row = get_data_row("SELECT * FROM {$CONFIG->dbprefix}entity_relationships WHERE guid_one=$guid_one AND relationship='$relationship' AND guid_two=$guid_two limit 1")) {
+ $query = "SELECT * FROM {$CONFIG->dbprefix}entity_relationships
+ WHERE guid_one=$guid_one
+ AND relationship='$relationship'
+ AND guid_two=$guid_two limit 1";
+
+ $row = row_to_elggrelationship(get_data_row($query));
+ if ($row) {
return $row;
}
@@ -403,9 +134,11 @@ function check_entity_relationship($guid_one, $relationship, $guid_two) {
/**
* Remove an arbitrary relationship between two entities.
*
- * @param int $guid_one
- * @param string $relationship
- * @param int $guid_two
+ * @param int $guid_one First GUID
+ * @param string $relationship Relationship name
+ * @param int $guid_two Second GUID
+ *
+ * @return bool
*/
function remove_entity_relationship($guid_one, $relationship, $guid_two) {
global $CONFIG;
@@ -419,8 +152,13 @@ function remove_entity_relationship($guid_one, $relationship, $guid_two) {
return false;
}
- if (trigger_elgg_event('delete', $relationship, $obj)) {
- return delete_data("DELETE from {$CONFIG->dbprefix}entity_relationships where guid_one=$guid_one and relationship='$relationship' and guid_two=$guid_two");
+ if (elgg_trigger_event('delete', $relationship, $obj)) {
+ $query = "DELETE from {$CONFIG->dbprefix}entity_relationships
+ where guid_one=$guid_one
+ and relationship='$relationship'
+ and guid_two=$guid_two";
+
+ return (bool)delete_data($query);
} else {
return false;
}
@@ -429,11 +167,12 @@ function remove_entity_relationship($guid_one, $relationship, $guid_two) {
/**
* Removes all arbitrary relationships originating from a particular entity
*
- * @param int $guid_one The GUID of the entity
- * @param string $relationship The name of the relationship (optionally)
- * @param true|false $inverse Whether we're deleting inverse relationships (default false)
- * @param string $type The type of entity to limit this relationship delete to (defaults to all)
- * @return true|false Depending on success
+ * @param int $guid_one The GUID of the entity
+ * @param string $relationship The name of the relationship (optional)
+ * @param bool $inverse Whether we're deleting inverse relationships (default false)
+ * @param string $type The type of entity to the delete to (defaults to all)
+ *
+ * @return bool Depending on success
*/
function remove_entity_relationships($guid_one, $relationship = "", $inverse = false, $type = '') {
global $CONFIG;
@@ -461,10 +200,16 @@ function remove_entity_relationships($guid_one, $relationship = "", $inverse = f
}
if (!$inverse) {
- $sql = "DELETE er from {$CONFIG->dbprefix}entity_relationships as er {$join} where guid_one={$guid_one} {$where}";
+ $sql = "DELETE er from {$CONFIG->dbprefix}entity_relationships as er
+ {$join}
+ where guid_one={$guid_one} {$where}";
+
return delete_data($sql);
} else {
- $sql = "DELETE er from {$CONFIG->dbprefix}entity_relationships as er {$join} where guid_two={$guid_one} {$where}";
+ $sql = "DELETE er from {$CONFIG->dbprefix}entity_relationships as er
+ {$join} where
+ guid_two={$guid_one} {$where}";
+
return delete_data($sql);
}
}
@@ -472,7 +217,10 @@ function remove_entity_relationships($guid_one, $relationship = "", $inverse = f
/**
* Get all the relationships for a given guid.
*
- * @param int $guid
+ * @param int $guid The GUID of the relationship owner
+ * @param bool $inverse_relationship Inverse relationship owners?
+ *
+ * @return ElggRelationship[]
*/
function get_entity_relationships($guid, $inverse_relationship = FALSE) {
global $CONFIG;
@@ -486,9 +234,22 @@ function get_entity_relationships($guid, $inverse_relationship = FALSE) {
return get_data($query, "row_to_elggrelationship");
}
-
/**
* Return entities matching a given query joining against a relationship.
+ * Also accepts all options available to elgg_get_entities() and
+ * elgg_get_entities_from_metadata().
+ *
+ * To ask for entities that do not have a particulat relationship to an entity,
+ * use a custom where clause like the following:
+ *
+ * $options['wheres'][] = "NOT EXISTS (
+ * SELECT 1 FROM {$db_prefix}entity_relationships
+ * WHERE guid_one = e.guid
+ * AND relationship = '$relationship'
+ * )";
+ *
+ * @see elgg_get_entities
+ * @see elgg_get_entities_from_metadata
*
* @param array $options Array in format:
*
@@ -498,7 +259,8 @@ function get_entity_relationships($guid, $inverse_relationship = FALSE) {
*
* inverse_relationship => BOOL Inverse the relationship
*
- * @return array
+ * @return ElggEntity[]|mixed If count, int. If not count, array. false on errors.
+ * @since 1.7.0
*/
function elgg_get_entities_from_relationship($options) {
$defaults = array(
@@ -509,7 +271,7 @@ function elgg_get_entities_from_relationship($options) {
$options = array_merge($defaults, $options);
- $clauses = elgg_get_entity_relationship_where_sql('e', $options['relationship'],
+ $clauses = elgg_get_entity_relationship_where_sql('e.guid', $options['relationship'],
$options['relationship_guid'], $options['inverse_relationship']);
if ($clauses) {
@@ -530,6 +292,16 @@ function elgg_get_entities_from_relationship($options) {
}
$options['joins'] = array_merge($options['joins'], $clauses['joins']);
+
+ if (isset($options['selects']) && !is_array($options['selects'])) {
+ $options['selects'] = array($options['selects']);
+ } elseif (!isset($options['selects'])) {
+ $options['selects'] = array();
+ }
+
+ $select = array('r.id');
+
+ $options['selects'] = array_merge($options['selects'], $select);
}
return elgg_get_entities_from_metadata($options);
@@ -540,13 +312,20 @@ function elgg_get_entities_from_relationship($options) {
*
* @todo add support for multiple relationships and guids.
*
- * @param $table Entities table name
- * @param $relationship relationship string
- * @param $entity_guid entity guid to check
+ * @param string $column Column name the guid should be checked against.
+ * Provide in table.column format.
+ * @param string $relationship Relationship string
+ * @param int $relationship_guid Entity guid to check
+ * @param bool $inverse_relationship Inverse relationship check?
+ *
* @return mixed
+ * @since 1.7.0
+ * @access private
*/
-function elgg_get_entity_relationship_where_sql($table, $relationship = NULL, $relationship_guid = NULL, $inverse_relationship = FALSE) {
- if ($relationship == NULL && $entity_guid == NULL) {
+function elgg_get_entity_relationship_where_sql($column, $relationship = NULL,
+$relationship_guid = NULL, $inverse_relationship = FALSE) {
+
+ if ($relationship == NULL && $relationship_guid == NULL) {
return '';
}
@@ -556,11 +335,9 @@ function elgg_get_entity_relationship_where_sql($table, $relationship = NULL, $r
$joins = array();
if ($inverse_relationship) {
- $joins[] = "JOIN {$CONFIG->dbprefix}entity_relationships r on r.guid_one = e.guid";
- //$wheres[] = "{$table}.guid = {$CONFIG->dbprefix}entity_relationships.guid_two";
+ $joins[] = "JOIN {$CONFIG->dbprefix}entity_relationships r on r.guid_one = $column";
} else {
- $joins[] = "JOIN {$CONFIG->dbprefix}entity_relationships r on r.guid_two = e.guid";
- //$wheres[] = "{$table}.guid = {$CONFIG->dbprefix}entity_relationships.guid_one";
+ $joins[] = "JOIN {$CONFIG->dbprefix}entity_relationships r on r.guid_two = $column";
}
if ($relationship) {
@@ -575,7 +352,7 @@ function elgg_get_entity_relationship_where_sql($table, $relationship = NULL, $r
}
}
- if ($where_str = implode(' AND ' , $wheres)) {
+ if ($where_str = implode(' AND ', $wheres)) {
return array('wheres' => array("($where_str)"), 'joins' => $joins);
}
@@ -584,302 +361,62 @@ function elgg_get_entity_relationship_where_sql($table, $relationship = NULL, $r
}
/**
- * @deprecated 1.7. Use elgg_get_entities_from_relationship().
- * @param $relationship
- * @param $relationship_guid
- * @param $inverse_relationship
- * @param $type
- * @param $subtype
- * @param $owner_guid
- * @param $order_by
- * @param $limit
- * @param $offset
- * @param $count
- * @param $site_guid
- * @return unknown_type
- */
-function get_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) {
-
- elgg_deprecated_notice('get_entities_from_relationship() was deprecated by elgg_get_entities_from_relationship()!', 1.7);
-
- $options = array();
-
- $options['relationship'] = $relationship;
- $options['relationship_guid'] = $relationship_guid;
- $options['inverse_relationship'] = $inverse_relationship;
-
- if ($type) {
- $options['types'] = $type;
- }
-
- if ($subtype) {
- $options['subtypes'] = $subtype;
- }
-
- if ($owner_guid) {
- $options['owner_guid'] = $owner_guid;
- }
-
- if ($limit) {
- $options['limit'] = $limit;
- }
-
- if ($offset) {
- $options['offset'] = $offset;
- }
-
- if ($order_by) {
- $options['order_by'];
- }
-
- if ($site_guid) {
- $options['site_guid'];
- }
-
- if ($count) {
- $options['count'] = $count;
- }
-
- return elgg_get_entities_from_relationship($options);
-}
-
-/**
* Returns a viewable list of entities by relationship
*
- * @see elgg_view_entity_list
+ * @param array $options Options array for retrieval of entities
+ *
+ * @see elgg_list_entities()
+ * @see elgg_get_entities_from_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 instead say "give me all entities for whome $relationship_guid is a $relationship of"
- * @param string $type The type of entity (eg 'object')
- * @param string $subtype The entity subtype
- * @param int $owner_guid The owner (default: all)
- * @param int $limit The number of entities to display on a page
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow gallery view
- * @param true|false $pagination Whether to display pagination (default: true)
* @return string The viewable list of entities
*/
-function list_entities_from_relationship($relationship, $relationship_guid, $inverse_relationship = false, $type = ELGG_ENTITIES_ANY_VALUE, $subtype = ELGG_ENTITIES_ANY_VALUE, $owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = false, $pagination = true) {
- $limit = (int) $limit;
- $offset = (int) get_input('offset');
- $options = array(
- 'relationship' => $relationship,
- 'relationship_guid' => $relationship_guid,
- 'inverse_relationship' => $inverse_relationship,
- 'types' => $type,
- 'subtypes' => $subtype,
- 'owner_guid' => $owner_guid,
- 'order_by' => '',
- 'limit' => $limit,
- 'offset' => $offset,
- 'count' => TRUE
- );
- $count = elgg_get_entities_from_relationship($options);
- $options['count'] = FALSE;
- $entities = elgg_get_entities_from_relationship($options);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
+function elgg_list_entities_from_relationship(array $options = array()) {
+ return elgg_list_entities($options, 'elgg_get_entities_from_relationship');
}
/**
* Gets the number of entities by a the number of entities related to them in a particular way.
- * This is a good way to get out the users with the most friends, or the groups with the most members.
+ * This is a good way to get out the users with the most friends, or the groups with the
+ * most members.
*
- * @param string $relationship The relationship eg "friends_of"
- * @param bool $inverse_relationship Reverse the normal function of the query to instead say "give me all entities for whome $relationship_guid is a $relationship of" (default: true)
- * @param string $type The type of entity (default: all)
- * @param string $subtype The entity subtype (default: all)
- * @param int $owner_guid The owner of the entities (default: none)
- * @param int $limit
- * @param int $offset
- * @param boolean $count Set to true if you want to count the number of entities instead (default false)
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @return array|int|false An array of entities, or the number of entities, or false on failure
+ * @param array $options An options array compatible with
+ * elgg_get_entities_from_relationship()
+ * @return ElggEntity[]|mixed int If count, int. If not count, array. false on errors.
+ * @since 1.8.0
*/
-
-function get_entities_by_relationship_count($relationship, $inverse_relationship = true, $type = "", $subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $count = false, $site_guid = 0) {
- global $CONFIG;
-
- $relationship = sanitise_string($relationship);
- $inverse_relationship = (bool)$inverse_relationship;
- $type = sanitise_string($type);
- if ($subtype AND !$subtype = get_subtype_id($type, $subtype)) {
- return false;
- }
- $owner_guid = (int)$owner_guid;
- $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 ($inverse_relationship) {
- $on = 'e.guid = r.guid_two';
- } else {
- $on = 'e.guid = r.guid_one';
- }
- 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}";
- }
-
- if ($count) {
- $query = "SELECT count(distinct e.guid) as total ";
- } else {
- $query = "SELECT e.*, count(e.guid) as total ";
- }
-
- $query .= " from {$CONFIG->dbprefix}entity_relationships r JOIN {$CONFIG->dbprefix}entities e on {$on} where ";
-
- if (!empty($where)) {
- foreach ($where as $w) {
- $query .= " $w and ";
- }
- }
- $query .= get_access_sql_suffix("e"); // Add access controls
-
- if (!$count) {
- $query .= " group by e.guid ";
- $query .= " order by total desc 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;
+function elgg_get_entities_from_relationship_count(array $options = array()) {
+ $options['selects'][] = "COUNT(e.guid) as total";
+ $options['group_by'] = 'r.guid_two';
+ $options['order_by'] = 'total desc';
+ return elgg_get_entities_from_relationship($options);
}
/**
- * Displays a human-readable list of entities
+ * Returns a list of entities by relationship count
*
- * @param string $relationship The relationship eg "friends_of"
- * @param bool $inverse_relationship Reverse the normal function of the query to instead say "give me all entities for whome $relationship_guid is a $relationship of" (default: true)
- * @param string $type The type of entity (eg 'object')
- * @param string $subtype The entity subtype
- * @param int $owner_guid The owner (default: all)
- * @param int $limit The number of entities to display on a page
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow gallery view
- * @param true|false $pagination Whether to display pagination (default: true)
- * @return string The viewable list of entities
- */
-
-function list_entities_by_relationship_count($relationship, $inverse_relationship = true, $type = "", $subtype = "", $owner_guid = 0, $limit = 10, $fullview = true, $viewtypetoggle = false, $pagination = true) {
- $limit = (int) $limit;
- $offset = (int) get_input('offset');
- $count = get_entities_by_relationship_count($relationship,$inverse_relationship,$type,$subtype,$owner_guid,0,0,true);
- $entities = get_entities_by_relationship_count($relationship,$inverse_relationship,$type,$subtype,$owner_guid,$limit,$offset);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
-}
-
-/**
- * Gets the number of entities by a the number of entities related to them in a particular way also constrained by
- * metadata
+ * @see elgg_get_entities_from_relationship_count()
*
- * @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 instead say "give me all entities for whome $relationship_guid is a $relationship of" (default: true)
- * @param String $meta_name The metadata name
- * @param String $meta_value The metadata value
- * @param string $type The type of entity (default: all)
- * @param string $subtype The entity subtype (default: all)
- * @param int $owner_guid The owner of the entities (default: none)
- * @param int $limit
- * @param int $offset
- * @param boolean $count Set to true if you want to count the number of entities instead (default false)
- * @param int $site_guid The site to get entities for. Leave as 0 (default) for the current site; -1 for all sites.
- * @return array|int|false An array of entities, or the number of entities, or false on failure
+ * @param array $options Options array
+ *
+ * @return string
+ * @since 1.8.0
*/
-function get_entities_from_relationships_and_meta($relationship, $relationship_guid, $inverse_relationship = false, $meta_name = "", $meta_value = "", $type = "", $subtype = "", $owner_guid = 0, $limit = 10, $offset = 0, $count = false, $site_guid = 0) {
- elgg_deprecated_notice('get_entities_from_relationship_and_meta() was deprecated by elgg_get_entities_from_relationship()!', 1.7);
-
- $options = array();
-
- $options['relationship'] = $relationship;
- $options['relationship_guid'] = $relationship_guid;
- $options['inverse_relationship'] = $inverse_relationship;
-
- if ($meta_value) {
- $options['values'] = $meta_value;
- }
-
- if ($entity_type) {
- $options['types'] = $entity_type;
- }
-
- if ($type) {
- $options['types'] = $type;
- }
-
- if (subtype) {
- $options['subtypes'] = $subtype;
- }
-
- if ($owner_guid) {
- $options['owner_guid'] = $owner_guid;
- }
-
- if ($limit) {
- $options['limit'] = $limit;
- }
-
- if ($offset) {
- $options['offset'] = $offset;
- }
-
- if ($order_by) {
- $options['order_by'];
- }
-
- if ($site_guid) {
- $options['site_guid'];
- }
-
- if ($count) {
- $options['count'] = $count;
- }
-
- return elgg_get_entities_from_relationship($options);
+function elgg_list_entities_from_relationship_count($options) {
+ return elgg_list_entities($options, 'elgg_get_entities_from_relationship_count');
}
/**
* Sets the URL handler for a particular relationship type
*
- * @param string $function_name The function to register
* @param string $relationship_type The relationship type.
- * @return true|false Depending on success
+ * @param string $function_name The function to register
+ *
+ * @return bool Depending on success
*/
-function register_relationship_url_handler($function_name, $relationship_type = "all") {
+function elgg_register_relationship_url_handler($relationship_type, $function_name) {
global $CONFIG;
- if (!is_callable($function_name)) {
+ if (!is_callable($function_name, true)) {
return false;
}
@@ -895,8 +432,9 @@ function register_relationship_url_handler($function_name, $relationship_type =
/**
* Get the url for a given relationship.
*
- * @param unknown_type $id
- * @return unknown
+ * @param int $id Relationship ID
+ *
+ * @return string
*/
function get_relationship_url($id) {
global $CONFIG;
@@ -920,13 +458,13 @@ function get_relationship_url($id) {
}
if (is_callable($function)) {
- $url = $function($relationship);
+ $url = call_user_func($function, $relationship);
}
if ($url == "") {
$nameid = $relationship->id;
- $url = $CONFIG->wwwroot . "export/$view/$guid/relationship/$nameid/";
+ $url = elgg_get_site_url() . "export/$view/$guid/relationship/$nameid/";
}
return $url;
@@ -936,16 +474,19 @@ function get_relationship_url($id) {
}
/**** HELPER FUNCTIONS FOR RELATIONSHIPS OF TYPE 'ATTACHED' ****/
+// @todo what is this?
/**
* Function to determine if the object trying to attach to other, has already done so
+ *
* @param int $guid_one This is the target object
* @param int $guid_two This is the object trying to attach to $guid_one
- * @return true | false
- **/
-
-function already_attached($guid_one, $guid_two){
- if ($attached = check_entity_relationship($guid_one, "attached", $guid_two)){
+ *
+ * @return bool
+ * @access private
+ */
+function already_attached($guid_one, $guid_two) {
+ if ($attached = check_entity_relationship($guid_one, "attached", $guid_two)) {
return true;
} else {
return false;
@@ -954,37 +495,56 @@ function already_attached($guid_one, $guid_two){
/**
* Function to get all objects attached to a particular object
- * @param int $guid
- * @param string $type - the type of object to return e.g. 'file', 'friend_of' etc
- * @return an array of objects
-**/
-
-function get_attachments($guid, $type=""){
- $attached = elgg_get_entities_from_relationship(array('relationship' => 'attached', 'relationship_guid' => $guid, 'inverse_relationship' => $inverse_relationship = false, 'types' => $type, 'subtypes' => '', 'owner_guid' => 0, 'order_by' => 'time_created desc', 'limit' => 10, 'offset' => 0, 'count' => false, 'site_guid' => 0));
+ *
+ * @param int $guid Entity GUID
+ * @param string $type The type of object to return e.g. 'file', 'friend_of' etc
+ *
+ * @return ElggEntity[]
+ * @access private
+ */
+function get_attachments($guid, $type = "") {
+ $options = array(
+ 'relationship' => 'attached',
+ 'relationship_guid' => $guid,
+ 'inverse_relationship' => false,
+ 'type' => $type,
+ 'subtypes' => '',
+ 'owner_guid' => 0,
+ 'order_by' => 'time_created desc',
+ 'limit' => 10,
+ 'offset' => 0,
+ 'count' => false,
+ 'site_guid' => 0
+ );
+ $attached = elgg_get_entities_from_relationship($options);
return $attached;
}
/**
* Function to remove a particular attachment between two objects
+ *
* @param int $guid_one This is the target object
* @param int $guid_two This is the object to remove from $guid_one
- * @return a view
-**/
-
-function remove_attachment($guid_one, $guid_two){
- if(already_attached($guid_one, $guid_two)) {
+ *
+ * @return void
+ * @access private
+ */
+function remove_attachment($guid_one, $guid_two) {
+ if (already_attached($guid_one, $guid_two)) {
remove_entity_relationship($guid_one, "attached", $guid_two);
}
}
/**
* Function to start the process of attaching one object to another
+ *
* @param int $guid_one This is the target object
* @param int $guid_two This is the object trying to attach to $guid_one
- * @return a view
-**/
-
-function make_attachment($guid_one, $guid_two){
+ *
+ * @return true|void
+ * @access private
+ */
+function make_attachment($guid_one, $guid_two) {
if (!(already_attached($guid_one, $guid_two))) {
if (add_entity_relationship($guid_one, "attached", $guid_two)) {
return true;
@@ -993,7 +553,15 @@ function make_attachment($guid_one, $guid_two){
}
/**
- * Handler called by trigger_plugin_hook on the "import" event.
+ * Handler called by trigger_plugin_hook on the "import" event.
+ *
+ * @param string $hook import
+ * @param string $entity_type all
+ * @param mixed $returnvalue Value from previous hook
+ * @param mixed $params Array of params
+ *
+ * @return mixed
+ * @access private
*/
function import_relationship_plugin_hook($hook, $entity_type, $returnvalue, $params) {
$element = $params['element'];
@@ -1003,17 +571,24 @@ function import_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par
if ($element instanceof ODDRelationship) {
$tmp = new ElggRelationship();
$tmp->import($element);
-
- return $tmp;
}
+ return $tmp;
}
/**
- * Handler called by trigger_plugin_hook on the "export" event.
+ * Handler called by trigger_plugin_hook on the "export" event.
+ *
+ * @param string $hook export
+ * @param string $entity_type all
+ * @param mixed $returnvalue Previous hook return value
+ * @param array $params Parameters
+ *
+ * @elgg_event_handler export all
+ * @return mixed
+ * @throws InvalidParameterException
+ * @access private
*/
function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $params) {
- global $CONFIG;
-
// Sanity check values
if ((!is_array($params)) && (!isset($params['guid']))) {
throw new InvalidParameterException(elgg_echo('InvalidParameterException:GUIDNotForExport'));
@@ -1037,36 +612,32 @@ function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par
}
/**
- * An event listener which will notify users based on certain events.
+ * Notify user that someone has friended them
+ *
+ * @param string $event Event name
+ * @param string $type Object type
+ * @param mixed $object Object
*
- * @param unknown_type $event
- * @param unknown_type $object_type
- * @param unknown_type $object
+ * @return bool
+ * @access private
*/
-function relationship_notification_hook($event, $object_type, $object) {
- global $CONFIG;
-
- if (
- ($object instanceof ElggRelationship) &&
- ($event == 'create') &&
- ($object_type == 'friend')
- )
- {
- $user_one = get_entity($object->guid_one);
- $user_two = get_entity($object->guid_two);
-
- // Notify target user
- return notify_user($object->guid_two, $object->guid_one, sprintf(elgg_echo('friend:newfriend:subject'), $user_one->name),
- sprintf(elgg_echo("friend:newfriend:body"), $user_one->name, $user_one->getURL())
- );
- }
+function relationship_notification_hook($event, $type, $object) {
+ /* @var ElggRelationship $object */
+ $user_one = get_entity($object->guid_one);
+ /* @var ElggUser $user_one */
+
+ return notify_user($object->guid_two,
+ $object->guid_one,
+ elgg_echo('friend:newfriend:subject', array($user_one->name)),
+ elgg_echo("friend:newfriend:body", array($user_one->name, $user_one->getURL()))
+ );
}
-/** Register the import hook */
-register_plugin_hook("import", "all", "import_relationship_plugin_hook", 3);
+// Register the import hook
+elgg_register_plugin_hook_handler("import", "all", "import_relationship_plugin_hook", 3);
-/** Register the hook, ensuring entities are serialised first */
-register_plugin_hook("export", "all", "export_relationship_plugin_hook", 3);
+// Register the hook, ensuring entities are serialised first
+elgg_register_plugin_hook_handler("export", "all", "export_relationship_plugin_hook", 3);
-/** Register event to listen to some events **/
-register_elgg_event_handler('create','friend','relationship_notification_hook');
+// Register event to listen to some events
+elgg_register_event_handler('create', 'friend', 'relationship_notification_hook');
diff --git a/engine/lib/river.php b/engine/lib/river.php
index 74e7322e8..e92040eb7 100644
--- a/engine/lib/river.php
+++ b/engine/lib/river.php
@@ -1,27 +1,31 @@
<?php
/**
- * Elgg river 2.0.
- * Functions for listening for and generating the river separately from the system log.
+ * Elgg river.
+ * Activity stream functions.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage SocialModel.River
*/
/**
* Adds an item to the river.
*
- * @param string $view The view that will handle the river item (must exist)
- * @param string $action_type An arbitrary one-word string to define the action (eg 'comment', 'create')
- * @param int $subject_guid The GUID of the entity doing the action
- * @param int $object_guid The GUID of the entity being acted upon
- * @param int $access_id The access ID of the river item (default: same as the object)
- * @param int $posted The UNIX epoch timestamp of the river item (default: now)
- * @return true|false Depending on success
+ * @param string $view The view that will handle the river item (must exist)
+ * @param string $action_type An arbitrary string to define the action (eg 'comment', 'create')
+ * @param int $subject_guid The GUID of the entity doing the action
+ * @param int $object_guid The GUID of the entity being acted upon
+ * @param int $access_id The access ID of the river item (default: same as the object)
+ * @param int $posted The UNIX epoch timestamp of the river item (default: now)
+ * @param int $annotation_id The annotation ID associated with this river entry
+ *
+ * @return int/bool River ID or false on failure
*/
-function add_to_river($view,$action_type,$subject_guid,$object_guid,$access_id = "",$posted = 0, $annotation_id = 0) {
- // use default viewtype for when called from REST api
+function add_to_river($view, $action_type, $subject_guid, $object_guid, $access_id = "",
+$posted = 0, $annotation_id = 0) {
+
+ global $CONFIG;
+
+ // use default viewtype for when called from web services api
if (!elgg_view_exists($view, 'default')) {
return false;
}
@@ -40,660 +44,660 @@ function add_to_river($view,$action_type,$subject_guid,$object_guid,$access_id =
if ($access_id === "") {
$access_id = $object->access_id;
}
- $annotation_id = (int)$annotation_id;
$type = $object->getType();
$subtype = $object->getSubtype();
- $action_type = sanitise_string($action_type);
- // Load config
- global $CONFIG;
+ $view = sanitise_string($view);
+ $action_type = sanitise_string($action_type);
+ $subject_guid = sanitise_int($subject_guid);
+ $object_guid = sanitise_int($object_guid);
+ $access_id = sanitise_int($access_id);
+ $posted = sanitise_int($posted);
+ $annotation_id = sanitise_int($annotation_id);
+
+ $values = array(
+ 'type' => $type,
+ 'subtype' => $subtype,
+ 'action_type' => $action_type,
+ 'access_id' => $access_id,
+ 'view' => $view,
+ 'subject_guid' => $subject_guid,
+ 'object_guid' => $object_guid,
+ 'annotation_id' => $annotation_id,
+ 'posted' => $posted,
+ );
+
+ // return false to stop insert
+ $values = elgg_trigger_plugin_hook('creating', 'river', null, $values);
+ if ($values == false) {
+ // inserting did not fail - it was just prevented
+ return true;
+ }
+
+ extract($values);
// Attempt to save river item; return success status
- $insert_data = insert_data("insert into {$CONFIG->dbprefix}river " .
- " set type = '{$type}', " .
- " subtype = '{$subtype}', " .
- " action_type = '{$action_type}', " .
- " access_id = {$access_id}, " .
- " view = '{$view}', " .
- " subject_guid = {$subject_guid}, " .
- " object_guid = {$object_guid}, " .
- " annotation_id = {$annotation_id}, " .
- " posted = {$posted} ");
-
- //update the entities which had the action carried out on it
- if($insert_data){
+ $id = insert_data("insert into {$CONFIG->dbprefix}river " .
+ " set type = '$type', " .
+ " subtype = '$subtype', " .
+ " action_type = '$action_type', " .
+ " access_id = $access_id, " .
+ " view = '$view', " .
+ " subject_guid = $subject_guid, " .
+ " object_guid = $object_guid, " .
+ " annotation_id = $annotation_id, " .
+ " posted = $posted");
+
+ // update the entities which had the action carried out on it
+ // @todo shouldn't this be down elsewhere? Like when an annotation is saved?
+ if ($id) {
update_entity_last_action($object_guid, $posted);
- return $insert_data;
+
+ $river_items = elgg_get_river(array('id' => $id));
+ if ($river_items) {
+ elgg_trigger_event('created', 'river', $river_items[0]);
+ }
+ return $id;
+ } else {
+ return false;
}
}
/**
- * Removes all items relating to a particular acting entity from the river
+ * Delete river items
+ *
+ * @warning not checking access (should we?)
*
- * @param int $subject_guid The GUID of the entity
- * @return true|false Depending on success
+ * @param array $options Parameters:
+ * ids => INT|ARR River item id(s)
+ * subject_guids => INT|ARR Subject guid(s)
+ * object_guids => INT|ARR Object guid(s)
+ * annotation_ids => INT|ARR The identifier of the annotation(s)
+ * action_types => STR|ARR The river action type(s) identifier
+ * views => STR|ARR River view(s)
+ *
+ * types => STR|ARR Entity type string(s)
+ * subtypes => STR|ARR Entity subtype string(s)
+ * type_subtype_pairs => ARR Array of type => subtype pairs where subtype
+ * can be an array of subtype strings
+ *
+ * posted_time_lower => INT The lower bound on the time posted
+ * posted_time_upper => INT The upper bound on the time posted
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function remove_from_river_by_subject($subject_guid) {
- // Sanitise
- $subject_guid = (int) $subject_guid;
-
- // Load config
+function elgg_delete_river(array $options = array()) {
global $CONFIG;
- // Remove
- return delete_data("delete from {$CONFIG->dbprefix}river where subject_guid = {$subject_guid}");
-}
+ $defaults = array(
+ 'ids' => ELGG_ENTITIES_ANY_VALUE,
-/**
- * Removes all items relating to a particular entity being acted upon from the river
- *
- * @param int $object_guid The GUID of the entity
- * @return true|false Depending on success
- */
-function remove_from_river_by_object($object_guid) {
- // Sanitise
- $object_guid = (int) $object_guid;
+ 'subject_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'object_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'annotation_ids' => ELGG_ENTITIES_ANY_VALUE,
- // Load config
- global $CONFIG;
+ 'views' => ELGG_ENTITIES_ANY_VALUE,
+ 'action_types' => ELGG_ENTITIES_ANY_VALUE,
- // Remove
- return delete_data("delete from {$CONFIG->dbprefix}river where object_guid = {$object_guid}");
-}
+ 'types' => ELGG_ENTITIES_ANY_VALUE,
+ 'subtypes' => ELGG_ENTITIES_ANY_VALUE,
+ 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
-/**
- * Removes all items relating to a particular annotation being acted upon from the river
- *
- * @param int annotation_id The ID of the annotation
- * @return true|false Depending on success
- */
-function remove_from_river_by_annotation($annotation_id) {
- // Sanitise
- $annotation_id = (int) $annotation_id;
+ 'posted_time_lower' => ELGG_ENTITIES_ANY_VALUE,
+ 'posted_time_upper' => ELGG_ENTITIES_ANY_VALUE,
- // Load config
- global $CONFIG;
+ 'wheres' => array(),
+ 'joins' => array(),
- // Remove
- return delete_data("delete from {$CONFIG->dbprefix}river where annotation_id = {$annotation_id}");
+ );
+
+ $options = array_merge($defaults, $options);
+
+ $singulars = array('id', 'subject_guid', 'object_guid', 'annotation_id', 'action_type', 'view', 'type', 'subtype');
+ $options = elgg_normalise_plural_options_array($options, $singulars);
+
+ $wheres = $options['wheres'];
+
+ $wheres[] = elgg_get_guid_based_where_sql('rv.id', $options['ids']);
+ $wheres[] = elgg_get_guid_based_where_sql('rv.subject_guid', $options['subject_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('rv.object_guid', $options['object_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('rv.annotation_id', $options['annotation_ids']);
+ $wheres[] = elgg_river_get_action_where_sql($options['action_types']);
+ $wheres[] = elgg_river_get_view_where_sql($options['views']);
+ $wheres[] = elgg_get_river_type_subtype_where_sql('rv', $options['types'],
+ $options['subtypes'], $options['type_subtype_pairs']);
+
+ if ($options['posted_time_lower'] && is_int($options['posted_time_lower'])) {
+ $wheres[] = "rv.posted >= {$options['posted_time_lower']}";
+ }
+
+ if ($options['posted_time_upper'] && is_int($options['posted_time_upper'])) {
+ $wheres[] = "rv.posted <= {$options['posted_time_upper']}";
+ }
+
+ // see if any functions failed
+ // remove empty strings on successful functions
+ foreach ($wheres as $i => $where) {
+ if ($where === FALSE) {
+ return FALSE;
+ } elseif (empty($where)) {
+ unset($wheres[$i]);
+ }
+ }
+
+ // remove identical where clauses
+ $wheres = array_unique($wheres);
+
+ $query = "DELETE rv.* FROM {$CONFIG->dbprefix}river rv ";
+
+ // remove identical join clauses
+ $joins = array_unique($options['joins']);
+
+ // add joins
+ foreach ($joins as $j) {
+ $query .= " $j ";
+ }
+
+ // add wheres
+ $query .= ' WHERE ';
+
+ foreach ($wheres as $w) {
+ $query .= " $w AND ";
+ }
+ $query .= "1=1";
+
+ return delete_data($query);
}
/**
- * Removes a single river entry
+ * Get river items
+ *
+ * @note If using types and subtypes in a query, they are joined with an AND.
+ *
+ * @param array $options Parameters:
+ * ids => INT|ARR River item id(s)
+ * subject_guids => INT|ARR Subject guid(s)
+ * object_guids => INT|ARR Object guid(s)
+ * annotation_ids => INT|ARR The identifier of the annotation(s)
+ * action_types => STR|ARR The river action type(s) identifier
+ * posted_time_lower => INT The lower bound on the time posted
+ * posted_time_upper => INT The upper bound on the time posted
+ *
+ * types => STR|ARR Entity type string(s)
+ * subtypes => STR|ARR Entity subtype string(s)
+ * type_subtype_pairs => ARR Array of type => subtype pairs where subtype
+ * can be an array of subtype strings
+ *
+ * relationship => STR Relationship identifier
+ * relationship_guid => INT|ARR Entity guid(s)
+ * inverse_relationship => BOOL Subject or object of the relationship (false)
+ *
+ * limit => INT Number to show per page (20)
+ * offset => INT Offset in list (0)
+ * count => BOOL Count the river items? (false)
+ * order_by => STR Order by clause (rv.posted desc)
+ * group_by => STR Group by clause
*
- * @param int $id The ID of the river entry
- * @return true|false Depending on success
- * @since 1.7.2
+ * @return array|int
+ * @since 1.8.0
*/
-function remove_from_river_by_id($id) {
+function elgg_get_river(array $options = array()) {
global $CONFIG;
- // Sanitise
- $id = (int) $id;
+ $defaults = array(
+ 'ids' => ELGG_ENTITIES_ANY_VALUE,
- return delete_data("delete from {$CONFIG->dbprefix}river where id = {$id}");
-}
+ 'subject_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'object_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'annotation_ids' => ELGG_ENTITIES_ANY_VALUE,
+ 'action_types' => ELGG_ENTITIES_ANY_VALUE,
+ 'relationship' => NULL,
+ 'relationship_guid' => NULL,
+ 'inverse_relationship' => FALSE,
-/**
- * Sets the access ID on river items for a particular object
- *
- * @param int $object_guid The GUID of the entity
- * @param int $access_id The access ID
- * @return true|false Depending on success
- */
-function update_river_access_by_object($object_guid, $access_id) {
- // Sanitise
- $object_guid = (int) $object_guid;
- $access_id = (int) $access_id;
+ 'types' => ELGG_ENTITIES_ANY_VALUE,
+ 'subtypes' => ELGG_ENTITIES_ANY_VALUE,
+ 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
- // Load config
- global $CONFIG;
+ 'posted_time_lower' => ELGG_ENTITIES_ANY_VALUE,
+ 'posted_time_upper' => ELGG_ENTITIES_ANY_VALUE,
- // Remove
- return update_data("update {$CONFIG->dbprefix}river set access_id = {$access_id} where object_guid = {$object_guid}");
-}
+ 'limit' => 20,
+ 'offset' => 0,
+ 'count' => FALSE,
-/**
- * Retrieves items from the river. All parameters are optional.
- *
- * @param int|array $subject_guid Acting entity to restrict to. Default: all
- * @param int|array $object_guid Entity being acted on to restrict to. Default: all
- * @param string $subject_relationship If set to a relationship type, this will use
- * $subject_guid as the starting point and set the subjects to be all users this
- * entity has this relationship with (eg 'friend'). Default: blank
- * @param string $type The type of entity to restrict to. Default: all
- * @param string $subtype The subtype of entity to restrict to. Default: all
- * @param string $action_type The type of river action to restrict to. Default: all
- * @param int $limit The number of items to retrieve. Default: 20
- * @param int $offset The page offset. Default: 0
- * @param int $posted_min The minimum time period to look at. Default: none
- * @param int $posted_max The maximum time period to look at. Default: none
- * @return array|false Depending on success
- */
-function get_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '', $type = '',
- $subtype = '', $action_type = '', $limit = 20, $offset = 0, $posted_min = 0, $posted_max = 0) {
+ 'order_by' => 'rv.posted desc',
+ 'group_by' => ELGG_ENTITIES_ANY_VALUE,
- // Get config
- global $CONFIG;
+ 'wheres' => array(),
+ 'joins' => array(),
+ );
- // Sanitise variables
- if (!is_array($subject_guid)) {
- $subject_guid = (int) $subject_guid;
- } else {
- foreach($subject_guid as $key => $temp) {
- $subject_guid[$key] = (int) $temp;
- }
- }
- if (!is_array($object_guid)) {
- $object_guid = (int) $object_guid;
- } else {
- foreach($object_guid as $key => $temp) {
- $object_guid[$key] = (int) $temp;
- }
- }
- if (!empty($type)) {
- $type = sanitise_string($type);
- }
- if (!empty($subtype)) {
- $subtype = sanitise_string($subtype);
+ $options = array_merge($defaults, $options);
+
+ $singulars = array('id', 'subject_guid', 'object_guid', 'annotation_id', 'action_type', 'type', 'subtype');
+ $options = elgg_normalise_plural_options_array($options, $singulars);
+
+ $wheres = $options['wheres'];
+
+ $wheres[] = elgg_get_guid_based_where_sql('rv.id', $options['ids']);
+ $wheres[] = elgg_get_guid_based_where_sql('rv.subject_guid', $options['subject_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('rv.object_guid', $options['object_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('rv.annotation_id', $options['annotation_ids']);
+ $wheres[] = elgg_river_get_action_where_sql($options['action_types']);
+ $wheres[] = elgg_get_river_type_subtype_where_sql('rv', $options['types'],
+ $options['subtypes'], $options['type_subtype_pairs']);
+
+ if ($options['posted_time_lower'] && is_int($options['posted_time_lower'])) {
+ $wheres[] = "rv.posted >= {$options['posted_time_lower']}";
}
- if (!empty($action_type)) {
- $action_type = sanitise_string($action_type);
+
+ if ($options['posted_time_upper'] && is_int($options['posted_time_upper'])) {
+ $wheres[] = "rv.posted <= {$options['posted_time_upper']}";
}
- $limit = (int) $limit;
- $offset = (int) $offset;
- $posted_min = (int) $posted_min;
- $posted_max = (int) $posted_max;
- // Construct 'where' clauses for the river
- $where = array();
- // river table does not have columns expected by get_access_sql_suffix so we modify its output
- $where[] = str_replace("and enabled='yes'",'',str_replace('owner_guid','subject_guid',get_access_sql_suffix()));
+ $joins = $options['joins'];
- if (empty($subject_relationship)) {
- if (!empty($subject_guid)) {
- if (!is_array($subject_guid)) {
- $where[] = " subject_guid = {$subject_guid} ";
- } else {
- $where[] = " subject_guid in (" . implode(',',$subject_guid) . ") ";
- }
- }
- } else {
- if (!is_array($subject_guid)) {
- if ($entities = elgg_get_entities_from_relationship(array(
- 'relationship' => $subject_relationship,
- 'relationship_guid' => $subject_guid,
- 'limit' => 9999))
- ) {
- $guids = array();
- foreach($entities as $entity) {
- $guids[] = (int) $entity->guid;
- }
- // $guids[] = $subject_guid;
- $where[] = " subject_guid in (" . implode(',',$guids) . ") ";
- } else {
- return array();
- }
+ if ($options['relationship_guid']) {
+ $clauses = elgg_get_entity_relationship_where_sql(
+ 'rv.subject_guid',
+ $options['relationship'],
+ $options['relationship_guid'],
+ $options['inverse_relationship']);
+ if ($clauses) {
+ $wheres = array_merge($wheres, $clauses['wheres']);
+ $joins = array_merge($joins, $clauses['joins']);
}
}
- if (!empty($object_guid))
- if (!is_array($object_guid)) {
- $where[] = " object_guid = {$object_guid} ";
- } else {
- $where[] = " object_guid in (" . implode(',',$object_guid) . ") ";
+
+ // see if any functions failed
+ // remove empty strings on successful functions
+ foreach ($wheres as $i => $where) {
+ if ($where === FALSE) {
+ return FALSE;
+ } elseif (empty($where)) {
+ unset($wheres[$i]);
}
- if (!empty($type)) {
- $where[] = " type = '{$type}' ";
- }
- if (!empty($subtype)) {
- $where[] = " subtype = '{$subtype}' ";
}
- if (!empty($action_type)) {
- $where[] = " action_type = '{$action_type}' ";
+
+ // remove identical where clauses
+ $wheres = array_unique($wheres);
+
+ if (!$options['count']) {
+ $query = "SELECT DISTINCT rv.* FROM {$CONFIG->dbprefix}river rv ";
+ } else {
+ $query = "SELECT count(DISTINCT rv.id) as total FROM {$CONFIG->dbprefix}river rv ";
}
- if (!empty($posted_min)) {
- $where[] = " posted > {$posted_min} ";
+
+ // add joins
+ foreach ($joins as $j) {
+ $query .= " $j ";
}
- if (!empty($posted_max)) {
- $where[] = " posted < {$posted_max} ";
+
+ // add wheres
+ $query .= ' WHERE ';
+
+ foreach ($wheres as $w) {
+ $query .= " $w AND ";
}
- $whereclause = implode(' and ', $where);
+ $query .= elgg_river_get_access_sql();
+
+ if (!$options['count']) {
+ $options['group_by'] = sanitise_string($options['group_by']);
+ if ($options['group_by']) {
+ $query .= " GROUP BY {$options['group_by']}";
+ }
+
+ $options['order_by'] = sanitise_string($options['order_by']);
+ $query .= " ORDER BY {$options['order_by']}";
- // Construct main SQL
- $sql = "select id,type,subtype,action_type,access_id,view,subject_guid,object_guid,annotation_id,posted" .
- " from {$CONFIG->dbprefix}river where {$whereclause} order by posted desc limit {$offset},{$limit}";
+ if ($options['limit']) {
+ $limit = sanitise_int($options['limit']);
+ $offset = sanitise_int($options['offset'], false);
+ $query .= " LIMIT $offset, $limit";
+ }
+
+ $river_items = get_data($query, 'elgg_row_to_elgg_river_item');
+ _elgg_prefetch_river_entities($river_items);
- // Get data
- return get_data($sql);
+ return $river_items;
+ } else {
+ $total = get_data_row($query);
+ return (int)$total->total;
+ }
}
/**
- * Retrieves items from the river. All parameters are optional.
+ * Prefetch entities that will be displayed in the river.
*
- * @param int|array $subject_guid Acting entity to restrict to. Default: all
- * @param int|array $object_guid Entity being acted on to restrict to. Default: all
- * @param string $subject_relationship If set to a relationship type, this will use
- * $subject_guid as the starting point and set the subjects to be all users this
- * entity has this relationship with (eg 'friend'). Default: blank
- * @param string $type The type of entity to restrict to. Default: all
- * @param string $subtype The subtype of entity to restrict to. Default: all
- * @param string $action_type The type of river action to restrict to. Default: all
- * @param int $limit The number of items to retrieve. Default: 20
- * @param int $offset The page offset. Default: 0
- * @param int $posted_min The minimum time period to look at. Default: none
- * @param int $posted_max The maximum time period to look at. Default: none
- * @return array|false Depending on success
+ * @param ElggRiverItem[] $river_items
+ * @access private
*/
-function elgg_get_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '', $type = '',
- $subtype = '', $action_type = '', $limit = 10, $offset = 0, $posted_min = 0, $posted_max = 0) {
-
- // Get config
- global $CONFIG;
-
- // Sanitise variables
- if (!is_array($subject_guid)) {
- $subject_guid = (int) $subject_guid;
- } else {
- foreach($subject_guid as $key => $temp) {
- $subject_guid[$key] = (int) $temp;
+function _elgg_prefetch_river_entities(array $river_items) {
+ // prefetch objects and subjects
+ $guids = array();
+ foreach ($river_items as $item) {
+ if ($item->subject_guid && !_elgg_retrieve_cached_entity($item->subject_guid)) {
+ $guids[$item->subject_guid] = true;
}
- }
- if (!is_array($object_guid)) {
- $object_guid = (int) $object_guid;
- } else {
- foreach($object_guid as $key => $temp) {
- $object_guid[$key] = (int) $temp;
+ if ($item->object_guid && !_elgg_retrieve_cached_entity($item->object_guid)) {
+ $guids[$item->object_guid] = true;
}
}
- if (!empty($type)) {
- $type = sanitise_string($type);
- }
- if (!empty($subtype)) {
- $subtype = sanitise_string($subtype);
- }
- if (!empty($action_type)) {
- $action_type = sanitise_string($action_type);
+ if ($guids) {
+ // avoid creating oversized query
+ // @todo how to better handle this?
+ $guids = array_slice($guids, 0, 300, true);
+ // return value unneeded, just priming cache
+ elgg_get_entities(array(
+ 'guids' => array_keys($guids),
+ 'limit' => 0,
+ ));
}
- $limit = (int) $limit;
- $offset = (int) $offset;
- $posted_min = (int) $posted_min;
- $posted_max = (int) $posted_max;
- // Construct 'where' clauses for the river
- $where = array();
- // river table does not have columns expected by get_access_sql_suffix so we modify its output
- $where[] = str_replace("and enabled='yes'",'',str_replace('owner_guid','subject_guid',get_access_sql_suffix_new('er','e')));
-
- if (empty($subject_relationship)) {
- if (!empty($subject_guid)) {
- if (!is_array($subject_guid)) {
- $where[] = " subject_guid = {$subject_guid} ";
- } else {
- $where[] = " subject_guid in (" . implode(',',$subject_guid) . ") ";
- }
+ // prefetch object containers
+ $guids = array();
+ foreach ($river_items as $item) {
+ $object = $item->getObjectEntity();
+ if ($object->container_guid && !_elgg_retrieve_cached_entity($object->container_guid)) {
+ $guids[$object->container_guid] = true;
}
- } else {
- if (!is_array($subject_guid)) {
- $entities = elgg_get_entities_from_relationship(array(
- 'relationship' => $subject_relationship,
- 'relationship_guid' => $subject_guid,
- 'limit' => 9999,
- ));
- if (is_array($entities) && !empty($entities)) {
- $guids = array();
- foreach($entities as $entity) {
- $guids[] = (int) $entity->guid;
- }
- // $guids[] = $subject_guid;
- $where[] = " subject_guid in (" . implode(',',$guids) . ") ";
- } else {
- return array();
- }
- }
- }
- if (!empty($object_guid))
- if (!is_array($object_guid)) {
- $where[] = " object_guid = {$object_guid} ";
- } else {
- $where[] = " object_guid in (" . implode(',',$object_guid) . ") ";
- }
- if (!empty($type)) {
- $where[] = " er.type = '{$type}' ";
- }
- if (!empty($subtype)) {
- $where[] = " er.subtype = '{$subtype}' ";
- }
- if (!empty($action_type)) {
- $where[] = " action_type = '{$action_type}' ";
- }
- if (!empty($posted_min)) {
- $where[] = " posted > {$posted_min} ";
}
- if (!empty($posted_max)) {
- $where[] = " posted < {$posted_max} ";
+ if ($guids) {
+ $guids = array_slice($guids, 0, 300, true);
+ elgg_get_entities(array(
+ 'guids' => array_keys($guids),
+ 'limit' => 0,
+ ));
}
-
- $whereclause = implode(' and ', $where);
-
- // Construct main SQL
- $sql = "select er.*" .
- " from {$CONFIG->dbprefix}river er, {$CONFIG->dbprefix}entities e " .
- " where {$whereclause} AND er.object_guid = e.guid GROUP BY object_guid " .
- " ORDER BY e.last_action desc LIMIT {$offset},{$limit}";
-
- // Get data
- return get_data($sql);
}
/**
- * Returns a human-readable representation of a river item
+ * List river items
*
- * @see get_river_items
+ * @param array $options Any options from elgg_get_river() plus:
+ * pagination => BOOL Display pagination links (true)
*
- * @param stdClass $item A river item object as returned from get_river_items
- * @return string|false Depending on success
+ * @return string
+ * @since 1.8.0
*/
-function elgg_view_river_item($item) {
- if (isset($item->view)) {
- $object = get_entity($item->object_guid);
- $subject = get_entity($item->subject_guid);
- if (!$object || !$subject) {
- // probably means an entity is disabled
- return false;
- } else {
- if (elgg_view_exists($item->view)) {
- $body = elgg_view($item->view,array(
- 'item' => $item
- ));
- }
- }
- return elgg_view('river/item/wrapper',array(
- 'item' => $item,
- 'body' => $body
- ));
- }
- return false;
+function elgg_list_river(array $options = array()) {
+ global $autofeed;
+ $autofeed = true;
+
+ $defaults = array(
+ 'offset' => (int) max(get_input('offset', 0), 0),
+ 'limit' => (int) max(get_input('limit', 20), 0),
+ 'pagination' => TRUE,
+ 'list_class' => 'elgg-list-river elgg-river', // @todo remove elgg-river in Elgg 1.9
+ );
+
+ $options = array_merge($defaults, $options);
+
+ if (!$options["limit"] && !$options["offset"]) {
+ // no need for pagination if listing is unlimited
+ $options["pagination"] = false;
+ }
+
+ $options['count'] = TRUE;
+ $count = elgg_get_river($options);
+
+ $options['count'] = FALSE;
+ $items = elgg_get_river($options);
+
+ $options['count'] = $count;
+ $options['items'] = $items;
+
+ return elgg_view('page/components/list', $options);
}
/**
- * Returns a human-readable version of the river.
+ * Convert a database row to a new ElggRiverItem
*
- * @param int|array $subject_guid Acting entity to restrict to. Default: all
- * @param int|array $object_guid Entity being acted on to restrict to. Default: all
- * @param string $subject_relationship If set to a relationship type, this will use
- * $subject_guid as the starting point and set the subjects to be all users this
- * entity has this relationship with (eg 'friend'). Default: blank
- * @param string $type The type of entity to restrict to. Default: all
- * @param string $subtype The subtype of entity to restrict to. Default: all
- * @param string $action_type The type of river action to restrict to. Default: all
- * @param int $limit The number of items to retrieve. Default: 20
- * @param int $posted_min The minimum time period to look at. Default: none
- * @param int $posted_max The maximum time period to look at. Default: none
- * @return string Human-readable river.
+ * @param stdClass $row Database row from the river table
+ *
+ * @return ElggRiverItem
+ * @since 1.8.0
+ * @access private
*/
-function elgg_view_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '',
- $type = '', $subtype = '', $action_type = '', $limit = 20, $posted_min = 0, $posted_max = 0, $pagination = true, $chronological = false) {
-
- // Get input from outside world and sanitise it
- $offset = (int) get_input('offset',0);
-
- // Get the correct function
- if($chronological == true){
- $riveritems = get_river_items($subject_guid,$object_guid,$subject_relationship,$type,$subtype,$action_type,($limit + 1),$offset,$posted_min,$posted_max);
- }else{
- $riveritems = elgg_get_river_items($subject_guid,$object_guid,$subject_relationship,$type,$subtype,$action_type,($limit + 1),$offset,$posted_min,$posted_max);
- }
-
- // Get river items, if they exist
- if ($riveritems) {
-
- return elgg_view('river/item/list',array(
- 'limit' => $limit,
- 'offset' => $offset,
- 'items' => $riveritems,
- 'pagination' => $pagination
- ));
-
+function elgg_row_to_elgg_river_item($row) {
+ if (!($row instanceof stdClass)) {
+ return NULL;
}
- return '';
+ return new ElggRiverItem($row);
}
/**
- * This function has been added here until we decide if it is going to roll into core or not
- * Add access restriction sql code to a given query.
- * Note that if this code is executed in privileged mode it will return blank.
- * @TODO: DELETE once Query classes are fully integrated
+ * Get the river's access where clause
*
- * @param string $table_prefix Optional table. prefix for the access code.
- * @param int $owner
+ * @return string
+ * @since 1.8.0
+ * @access private
*/
-function get_access_sql_suffix_new($table_prefix_one = '', $table_prefix_two = '', $owner = null) {
- global $ENTITY_SHOW_HIDDEN_OVERRIDE, $CONFIG;
-
- $sql = "";
- $friends_bit = "";
- $enemies_bit = "";
+function elgg_river_get_access_sql() {
+ // rewrite default access where clause to work with river table
+ return str_replace("and enabled='yes'", '',
+ str_replace('owner_guid', 'rv.subject_guid',
+ str_replace('access_id', 'rv.access_id', get_access_sql_suffix())));
+}
- if ($table_prefix_one) {
- $table_prefix_one = sanitise_string($table_prefix_one) . ".";
+/**
+ * Returns SQL where clause for type and subtype on river table
+ *
+ * @internal This is a simplified version of elgg_get_entity_type_subtype_where_sql()
+ * which could be used for all queries once the subtypes have been denormalized.
+ *
+ * @param string $table 'rv'
+ * @param NULL|array $types Array of types or NULL if none.
+ * @param NULL|array $subtypes Array of subtypes or NULL if none
+ * @param NULL|array $pairs Array of pairs of types and subtypes
+ *
+ * @return string
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_get_river_type_subtype_where_sql($table, $types, $subtypes, $pairs) {
+ // short circuit if nothing is requested
+ if (!$types && !$subtypes && !$pairs) {
+ return '';
}
- if ($table_prefix_two) {
- $table_prefix_two = sanitise_string($table_prefix_two) . ".";
- }
+ $wheres = array();
+ $types_wheres = array();
+ $subtypes_wheres = array();
- if (!isset($owner)) {
- $owner = get_loggedin_userid();
- }
+ // if no pairs, use types and subtypes
+ if (!is_array($pairs)) {
+ if ($types) {
+ if (!is_array($types)) {
+ $types = array($types);
+ }
+ foreach ($types as $type) {
+ $type = sanitise_string($type);
+ $types_wheres[] = "({$table}.type = '$type')";
+ }
+ }
- if (!$owner) {
- $owner = -1;
- }
+ if ($subtypes) {
+ if (!is_array($subtypes)) {
+ $subtypes = array($subtypes);
+ }
+ foreach ($subtypes as $subtype) {
+ $subtype = sanitise_string($subtype);
+ $subtypes_wheres[] = "({$table}.subtype = '$subtype')";
+ }
+ }
- $ignore_access = elgg_check_access_overrides($owner);
- $access = get_access_list($owner);
+ if (is_array($types_wheres) && count($types_wheres)) {
+ $types_wheres = array(implode(' OR ', $types_wheres));
+ }
- if ($ignore_access) {
- $sql = " (1 = 1) ";
- } else if ($owner != -1) {
- $friends_bit = "{$table_prefix_one}access_id = " . ACCESS_FRIENDS . "
- AND {$table_prefix_one}owner_guid IN (
- SELECT guid_one FROM {$CONFIG->dbprefix}entity_relationships
- WHERE relationship='friend' AND guid_two=$owner
- )";
+ if (is_array($subtypes_wheres) && count($subtypes_wheres)) {
+ $subtypes_wheres = array('(' . implode(' OR ', $subtypes_wheres) . ')');
+ }
- $friends_bit = '('.$friends_bit.') OR ';
+ $wheres = array(implode(' AND ', array_merge($types_wheres, $subtypes_wheres)));
- if ((isset($CONFIG->user_block_and_filter_enabled)) && ($CONFIG->user_block_and_filter_enabled)) {
- // check to see if the user is in the entity owner's block list
- // or if the entity owner is in the user's filter list
- // if so, disallow access
- $enemies_bit = get_annotation_sql('elgg_block_list', "{$table_prefix_one}owner_guid", $owner, false);
- $enemies_bit = '('
- . $enemies_bit
- . ' AND ' . get_annotation_sql('elgg_filter_list', $owner, "{$table_prefix_one}owner_guid", false)
- . ')';
+ } else {
+ // using type/subtype pairs
+ foreach ($pairs as $paired_type => $paired_subtypes) {
+ $paired_type = sanitise_string($paired_type);
+ if (is_array($paired_subtypes)) {
+ $paired_subtypes = array_map('sanitise_string', $paired_subtypes);
+ $paired_subtype_str = implode("','", $paired_subtypes);
+ if ($paired_subtype_str) {
+ $wheres[] = "({$table}.type = '$paired_type'"
+ . " AND {$table}.subtype IN ('$paired_subtype_str'))";
+ }
+ } else {
+ $paired_subtype = sanitise_string($paired_subtypes);
+ $wheres[] = "({$table}.type = '$paired_type'"
+ . " AND {$table}.subtype = '$paired_subtype')";
+ }
}
}
- if (empty($sql)) {
- $sql = " $friends_bit ({$table_prefix_one}access_id IN {$access}
- OR ({$table_prefix_one}owner_guid = {$owner})
- OR (
- {$table_prefix_one}access_id = " . ACCESS_PRIVATE . "
- AND {$table_prefix_one}owner_guid = $owner
- )
- )";
- }
-
- if ($enemies_bit) {
- $sql = "$enemies_bit AND ($sql)";
+ if (is_array($wheres) && count($wheres)) {
+ $where = implode(' OR ', $wheres);
+ return "($where)";
}
- if (!$ENTITY_SHOW_HIDDEN_OVERRIDE)
- $sql .= " and {$table_prefix_two}enabled='yes'";
- return '('.$sql.')';
+ return '';
}
/**
- * Construct and execute the query required for the activity stream.
+ * Get the where clause based on river action type strings
*
- * @deprecated 1.8
+ * @param array $types Array of action type strings
*
- * @param int $limit Limit the query.
- * @param int $offset Execute from the given object
- * @param mixed $type A type, or array of types to look for. Note: This is how they appear in the SYSTEM LOG.
- * @param mixed $subtype A subtype, or array of types to look for. Note: This is how they appear in the SYSTEM LOG.
- * @param mixed $owner_guid The guid or a collection of GUIDs
- * @param string $owner_relationship If defined, the relationship between $owner_guid and the entity owner_guid - so "is $owner_guid $owner_relationship with $entity->owner_guid"
- * @return array An array of system log entries.
+ * @return string
+ * @since 1.8.0
+ * @access private
*/
-function get_activity_stream_data($limit = 10, $offset = 0, $type = "", $subtype = "", $owner_guid = "", $owner_relationship = "") {
- global $CONFIG;
-
- $limit = (int)$limit;
- $offset = (int)$offset;
-
- if ($type) {
- if (!is_array($type)) {
- $type = array(sanitise_string($type));
- } else {
- foreach ($type as $k => $v) {
- $type[$k] = sanitise_string($v);
- }
- }
+function elgg_river_get_action_where_sql($types) {
+ if (!$types) {
+ return '';
}
- if ($subtype) {
- if (!is_array($subtype)) {
- $subtype = array(sanitise_string($subtype));
- } else {
- foreach ($subtype as $k => $v) {
- $subtype[$k] = sanitise_string($v);
- }
- }
+ if (!is_array($types)) {
+ $types = sanitise_string($types);
+ return "(rv.action_type = '$types')";
}
- if ($owner_guid) {
- if (is_array($owner_guid)) {
- foreach ($owner_guid as $k => $v) {
- $owner_guid[$k] = (int)$v;
- }
- } else {
- $owner_guid = array((int)$owner_guid);
- }
+ // sanitize types array
+ $types_sanitized = array();
+ foreach ($types as $type) {
+ $types_sanitized[] = sanitise_string($type);
}
- $owner_relationship = sanitise_string($owner_relationship);
-
- // Get a list of possible views
- $activity_events= array();
- $activity_views = array_merge(elgg_view_tree('activity', 'default'), elgg_view_tree('river', 'default')); // Join activity with river
+ $type_str = implode("','", $types_sanitized);
+ return "(rv.action_type IN ('$type_str'))";
+}
- $done = array();
+/**
+ * Get the where clause based on river view strings
+ *
+ * @param array $views Array of view strings
+ *
+ * @return string
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_river_get_view_where_sql($views) {
+ if (!$views) {
+ return '';
+ }
- foreach ($activity_views as $view) {
- $fragments = explode('/', $view);
- $tmp = explode('/',$view, 2);
- $tmp = $tmp[1];
+ if (!is_array($views)) {
+ $views = sanitise_string($views);
+ return "(rv.view = '$views')";
+ }
- if ((isset($fragments[0])) && (($fragments[0] == 'river') || ($fragments[0] == 'activity'))
- && (!in_array($tmp, $done))) {
+ // sanitize views array
+ $views_sanitized = array();
+ foreach ($views as $view) {
+ $views_sanitized[] = sanitise_string($view);
+ }
- if (isset($fragments[1])) {
- $f = array();
- for ($n = 1; $n < count($fragments); $n++) {
- $val = sanitise_string($fragments[$n]);
- switch($n) {
- case 1: $key = 'type'; break;
- case 2: $key = 'subtype'; break;
- case 3: $key = 'event'; break;
- }
- $f[$key] = $val;
- }
+ $view_str = implode("','", $views_sanitized);
+ return "(rv.view IN ('$view_str'))";
+}
- // Filter result based on parameters
- $add = true;
- if ($type) {
- if (!in_array($f['type'], $type)) {
- $add = false;
- }
- }
- if (($add) && ($subtype)) {
- if (!in_array($f['subtype'], $subtype)) {
- $add = false;
- }
- }
- if (($add) && ($event)) {
- if (!in_array($f['event'], $event)) {
- $add = false;
- }
- }
+/**
+ * Sets the access ID on river items for a particular object
+ *
+ * @param int $object_guid The GUID of the entity
+ * @param int $access_id The access ID
+ *
+ * @return bool Depending on success
+ */
+function update_river_access_by_object($object_guid, $access_id) {
+ // Sanitise
+ $object_guid = (int) $object_guid;
+ $access_id = (int) $access_id;
- if ($add) {
- $activity_events[] = $f;
- }
- }
+ // Load config
+ global $CONFIG;
- $done[] = $tmp;
- }
- }
+ // Remove
+ $query = "update {$CONFIG->dbprefix}river
+ set access_id = {$access_id}
+ where object_guid = {$object_guid}";
+ return update_data($query);
+}
- $n = 0;
- foreach ($activity_events as $details) {
- // Get what we're talking about
- if ($details['subtype'] == 'default') {
- $details['subtype'] = '';
- }
+/**
+ * Page handler for activity
+ *
+ * @param array $page
+ * @return bool
+ * @access private
+ */
+function elgg_river_page_handler($page) {
+ global $CONFIG;
- if (($details['type']) && ($details['event'])) {
- if ($n > 0) {
- $obj_query .= " or ";
- }
+ elgg_set_page_owner_guid(elgg_get_logged_in_user_guid());
- $access = "";
- if ($details['type']!='relationship') {
- $access = " and " . get_access_sql_suffix('sl');
- }
+ // make a URL segment available in page handler script
+ $page_type = elgg_extract(0, $page, 'all');
+ $page_type = preg_replace('[\W]', '', $page_type);
+ if ($page_type == 'owner') {
+ $page_type = 'mine';
+ }
+ set_input('page_type', $page_type);
- $obj_query .= "( sl.object_type='{$details['type']}'
- AND sl.object_subtype='{$details['subtype']}'
- AND sl.event='{$details['event']}' $access )";
+ require_once("{$CONFIG->path}pages/river.php");
+ return true;
+}
- $n++;
- }
- }
+/**
+ * Register river unit tests
+ * @access private
+ */
+function elgg_river_test($hook, $type, $value) {
+ global $CONFIG;
+ $value[] = $CONFIG->path . 'engine/tests/api/river.php';
+ return $value;
+}
- // User
- if ((count($owner_guid)) && ($owner_guid[0] != 0)) {
- $user = " and sl.performed_by_guid in (".implode(',', $owner_guid).")";
-
- if ($owner_relationship) {
- $friendsarray = "";
- if ($friends = elgg_get_entities_from_relationship(array(
- 'relationship' => $owner_relationship,
- 'relationship_guid' => $owner_guid[0],
- 'inverse_relationship' => FALSE,
- 'types' => 'user',
- 'subtypes' => $subtype,
- 'limit' => 9999))
- ) {
-
- $friendsarray = array();
- foreach($friends as $friend) {
- $friendsarray[] = $friend->getGUID();
- }
+/**
+ * Initialize river library
+ * @access private
+ */
+function elgg_river_init() {
+ elgg_register_page_handler('activity', 'elgg_river_page_handler');
+ $item = new ElggMenuItem('activity', elgg_echo('activity'), 'activity');
+ elgg_register_menu_item('site', $item);
+
+ elgg_register_widget_type('river_widget', elgg_echo('river:widget:title'), elgg_echo('river:widget:description'));
- $user = " and sl.performed_by_guid in (" . implode(',', $friendsarray).")";
- }
- }
- }
+ elgg_register_action('river/delete', '', 'admin');
- $query = "SELECT sl.* FROM {$CONFIG->dbprefix}system_log sl
- WHERE 1 $user AND ($obj_query)
- ORDER BY sl.time_created desc limit $offset, $limit";
- return get_data($query);
+ elgg_register_plugin_hook_handler('unit_test', 'system', 'elgg_river_test');
}
+
+elgg_register_event_handler('init', 'system', 'elgg_river_init');
diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php
index b74b2f524..e3d5ce9cd 100644
--- a/engine/lib/sessions.php
+++ b/engine/lib/sessions.php
@@ -4,119 +4,23 @@
* Elgg session management
* Functions to manage logins
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Session
*/
/** Elgg magic session */
global $SESSION;
/**
- * Magic session class.
- * This class is intended to extend the $_SESSION magic variable by providing an API hook
- * to plug in other values.
- *
- * Primarily this is intended to provide a way of supplying "logged in user" details without touching the session
- * (which can cause problems when accessed server side).
- *
- * If a value is present in the session then that value is returned, otherwise a plugin hook 'session:get', '$var' is called,
- * where $var is the variable being requested.
- *
- * Setting values will store variables in the session in the normal way.
- *
- * LIMITATIONS: You can not access multidimensional arrays
- *
- * This is EXPERIMENTAL.
- */
-class ElggSession implements ArrayAccess {
- /** Local cache of trigger retrieved variables */
- private static $__localcache;
-
- function __isset($key) {
- return $this->offsetExists($key);
- }
-
- /** Set a value, go straight to session. */
- function offsetSet($key, $value) {
- $_SESSION[$key] = $value;
- }
-
- /**
- * Get a variable from either the session, or if its not in the session attempt to get it from
- * an api call.
- */
- function offsetGet($key) {
- if (!ElggSession::$__localcache) {
- ElggSession::$__localcache = array();
- }
-
- if (isset($_SESSION[$key])) {
- return $_SESSION[$key];
- }
-
- if (isset(ElggSession::$__localcache[$key])) {
- return ElggSession::$__localcache[$key];
- }
-
- $value = NULL;
- $value = trigger_plugin_hook('session:get', $key, NULL, $value);
-
- ElggSession::$__localcache[$key] = $value;
-
- return ElggSession::$__localcache[$key];
- }
-
- /**
- * Unset a value from the cache and the session.
- */
- function offsetUnset($key) {
- unset(ElggSession::$__localcache[$key]);
- unset($_SESSION[$key]);
- }
-
- /**
- * Return whether the value is set in either the session or the cache.
- */
- function offsetExists($offset) {
- if (isset(ElggSession::$__localcache[$offset])) {
- return true;
- }
-
- if (isset($_SESSION[$offset])) {
- return true;
- }
-
- if ($this->offsetGet($offset)){
- return true;
- }
- }
-
-
- // Alias functions
- function get($key) {
- return $this->offsetGet($key);
- }
-
- function set($key, $value) {
- return $this->offsetSet($key, $value);
- }
-
- function del($key) {
- return $this->offsetUnset($key);
- }
-}
-
-
-/**
* Return the current logged in user, or NULL if no user is logged in.
*
- * If no user can be found in the current session, a plugin hook - 'session:get' 'user' to give plugin
- * authors another way to provide user details to the ACL system without touching the session.
- * @return ElggUser|NULL
+ * If no user can be found in the current session, a plugin
+ * hook - 'session:get' 'user' to give plugin authors another
+ * way to provide user details to the ACL system without touching the session.
+ *
+ * @return ElggUser
*/
-function get_loggedin_user() {
+function elgg_get_logged_in_user_entity() {
global $SESSION;
if (isset($SESSION)) {
@@ -129,11 +33,11 @@ function get_loggedin_user() {
/**
* Return the current logged in user by id.
*
- * @see get_loggedin_user()
+ * @see elgg_get_logged_in_user_entity()
* @return int
*/
-function get_loggedin_userid() {
- $user = get_loggedin_user();
+function elgg_get_logged_in_user_guid() {
+ $user = elgg_get_logged_in_user_entity();
if ($user) {
return $user->guid;
}
@@ -144,14 +48,10 @@ function get_loggedin_userid() {
/**
* Returns whether or not the user is currently logged in
*
- * @return true|false
+ * @return bool
*/
-function isloggedin() {
- if (!is_installed()) {
- return false;
- }
-
- $user = get_loggedin_user();
+function elgg_is_logged_in() {
+ $user = elgg_get_logged_in_user_entity();
if ((isset($user)) && ($user instanceof ElggUser) && ($user->guid > 0)) {
return true;
@@ -163,17 +63,12 @@ function isloggedin() {
/**
* Returns whether or not the user is currently logged in and that they are an admin user.
*
- * @uses isloggedin()
- * @return true|false
+ * @return bool
*/
-function isadminloggedin() {
- if (!is_installed()) {
- return FALSE;
- }
-
- $user = get_loggedin_user();
+function elgg_is_admin_logged_in() {
+ $user = elgg_get_logged_in_user_entity();
- if ((isloggedin()) && $user->isAdmin()) {
+ if ((elgg_is_logged_in()) && $user->isAdmin()) {
return TRUE;
}
@@ -182,13 +77,19 @@ function isadminloggedin() {
/**
* Check if the given user has full access.
+ *
* @todo: Will always return full access if the user is an admin.
*
- * @param $user_guid
+ * @param int $user_guid The user to check
+ *
* @return bool
+ * @since 1.7.1
*/
function elgg_is_admin_user($user_guid) {
global $CONFIG;
+
+ $user_guid = (int)$user_guid;
+
// cannot use magic metadata here because of recursion
// must support the old way of getting admin from metadata
@@ -227,63 +128,71 @@ function elgg_is_admin_user($user_guid) {
}
/**
- * Perform standard authentication with a given username and password.
- * Returns an ElggUser object for use with login.
+ * 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, optionally (for standard logins)
- * @param string $password The password, optionally (for standard logins)
- * @return ElggUser|false The authenticated user object, or false on failure.
+ *
+ * @param string $username The username
+ * @param string $password The password
+ *
+ * @return true|string True or an error message on failure
+ * @access private
*/
-
-function authenticate($username, $password) {
- if (pam_authenticate(array('username' => $username, 'password' => $password))) {
- return get_user_by_username($username);
+function elgg_authenticate($username, $password) {
+ $pam = new ElggPAM('user');
+ $credentials = array('username' => $username, 'password' => $password);
+ $result = $pam->authenticate($credentials);
+ if (!$result) {
+ return $pam->getFailureMessage();
}
-
- return false;
+ return true;
}
/**
* Hook into the PAM system which accepts a username and password and attempts to authenticate
* it against a known user.
*
- * @param array $credentials Associated array of credentials passed to pam_authenticate. This function expects
- * 'username' and 'password' (cleartext).
+ * @param array $credentials Associated array of credentials passed to
+ * Elgg's PAM system. This function expects
+ * 'username' and 'password' (cleartext).
+ *
+ * @return bool
+ * @throws LoginException
+ * @access private
*/
-function pam_auth_userpass($credentials = NULL) {
+function pam_auth_userpass(array $credentials = array()) {
- if (is_array($credentials) && ($credentials['username']) && ($credentials['password'])) {
- if ($user = get_user_by_username($credentials['username'])) {
-
- // Let admins log in without validating their email, but normal users must have validated their email or been admin created
- if ((!$user->isAdmin()) && (!$user->validated) && (!$user->admin_created)) {
- return false;
- }
+ if (!isset($credentials['username']) || !isset($credentials['password'])) {
+ return false;
+ }
- // User has been banned, so prevent from logging in
- if ($user->isBanned()) {
- return false;
- }
+ $user = get_user_by_username($credentials['username']);
+ if (!$user) {
+ throw new LoginException(elgg_echo('LoginException:UsernameFailure'));
+ }
- if ($user->password == generate_user_password($user, $credentials['password'])) {
- return true;
- } else {
- // Password failed, log.
- log_login_failure($user->guid);
- }
+ if (check_rate_limit_exceeded($user->guid)) {
+ throw new LoginException(elgg_echo('LoginException:AccountLocked'));
+ }
- }
+ if ($user->password !== generate_user_password($user, $credentials['password'])) {
+ log_login_failure($user->guid);
+ throw new LoginException(elgg_echo('LoginException:PasswordFailure'));
}
- return false;
+ return true;
}
/**
* Log a failed login for $user_guid
*
- * @param $user_guid
- * @return bool on success
+ * @param int $user_guid User GUID
+ *
+ * @return bool
*/
function log_login_failure($user_guid) {
$user_guid = (int)$user_guid;
@@ -304,8 +213,9 @@ function log_login_failure($user_guid) {
/**
* Resets the fail login count for $user_guid
*
- * @param $user_guid
- * @return bool on success (success = user has no logged failed attempts)
+ * @param int $user_guid User GUID
+ *
+ * @return bool true on success (success = user has no logged failed attempts)
*/
function reset_login_failure_count($user_guid) {
$user_guid = (int)$user_guid;
@@ -315,7 +225,7 @@ function reset_login_failure_count($user_guid) {
$fails = (int)$user->getPrivateSetting("login_failures");
if ($fails) {
- for ($n=1; $n <= $fails; $n++) {
+ for ($n = 1; $n <= $fails; $n++) {
$user->removePrivateSetting("login_failure_$n");
}
@@ -334,7 +244,8 @@ function reset_login_failure_count($user_guid) {
/**
* Checks if the rate limit of failed logins has been exceeded for $user_guid.
*
- * @param $user_guid
+ * @param int $user_guid User GUID
+ *
* @return bool on exceeded limit.
*/
function check_rate_limit_exceeded($user_guid) {
@@ -348,13 +259,13 @@ function check_rate_limit_exceeded($user_guid) {
if ($fails >= $limit) {
$cnt = 0;
$time = time();
- for ($n=$fails; $n>0; $n--) {
+ for ($n = $fails; $n > 0; $n--) {
$f = $user->getPrivateSetting("login_failure_$n");
- if ($f > $time - (60*5)) {
+ if ($f > $time - (60 * 5)) {
$cnt++;
}
- if ($cnt==$limit) {
+ if ($cnt == $limit) {
// Limit reached
return true;
}
@@ -367,24 +278,20 @@ function check_rate_limit_exceeded($user_guid) {
/**
* Logs in a specified ElggUser. For standard registration, use in conjunction
- * with authenticate.
+ * with elgg_authenticate.
+ *
+ * @see elgg_authenticate
*
- * @see authenticate
- * @param ElggUser $user A valid Elgg user object
- * @param boolean $persistent Should this be a persistent login?
- * @return true|false Whether login was successful
+ * @param ElggUser $user A valid Elgg user object
+ * @param boolean $persistent Should this be a persistent login?
+ *
+ * @return true or throws exception
+ * @throws LoginException
*/
function login(ElggUser $user, $persistent = false) {
- global $CONFIG;
-
// User is banned, return false.
if ($user->isBanned()) {
- return false;
- }
-
- // Check rate limit
- if (check_rate_limit_exceeded($user->guid)) {
- return false;
+ throw new LoginException(elgg_echo('LoginException:BannedUser'));
}
$_SESSION['user'] = $user;
@@ -398,18 +305,18 @@ function login(ElggUser $user, $persistent = false) {
$code = (md5($user->name . $user->username . time() . rand()));
$_SESSION['code'] = $code;
$user->code = md5($code);
- setcookie("elggperm", $code, (time()+(86400 * 30)),"/");
+ setcookie("elggperm", $code, (time() + (86400 * 30)), "/");
}
- if (!$user->save() || !trigger_elgg_event('login','user',$user)) {
+ if (!$user->save() || !elgg_trigger_event('login', 'user', $user)) {
unset($_SESSION['username']);
unset($_SESSION['name']);
unset($_SESSION['code']);
unset($_SESSION['guid']);
unset($_SESSION['id']);
unset($_SESSION['user']);
- setcookie("elggperm", "", (time()-(86400 * 30)),"/");
- return false;
+ setcookie("elggperm", "", (time() - (86400 * 30)), "/");
+ throw new LoginException(elgg_echo('LoginException:Unknown'));
}
// Users privilege has been elevated, so change the session id (prevents session fixation)
@@ -419,19 +326,23 @@ function login(ElggUser $user, $persistent = false) {
set_last_login($_SESSION['guid']);
reset_login_failure_count($user->guid); // Reset any previous failed login attempts
+ // if memcache is enabled, invalidate the user in memcache @see https://github.com/Elgg/Elgg/issues/3143
+ if (is_memcache_available()) {
+ // this needs to happen with a shutdown function because of the timing with set_last_login()
+ register_shutdown_function("_elgg_invalidate_memcache_for_entity", $_SESSION['guid']);
+ }
+
return true;
}
/**
* Log the current user out
*
- * @return true|false
+ * @return bool
*/
function logout() {
- global $CONFIG;
-
if (isset($_SESSION['user'])) {
- if (!trigger_elgg_event('logout','user',$_SESSION['user'])) {
+ if (!elgg_trigger_event('logout', 'user', $_SESSION['user'])) {
return false;
}
$_SESSION['user']->code = "";
@@ -445,7 +356,7 @@ function logout() {
unset($_SESSION['id']);
unset($_SESSION['user']);
- setcookie("elggperm", "", (time()-(86400 * 30)),"/");
+ setcookie("elggperm", "", (time() - (86400 * 30)), "/");
// pass along any messages
$old_msg = $_SESSION['msg'];
@@ -453,7 +364,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;
@@ -465,30 +376,27 @@ function logout() {
* This function looks for:
*
* 1. $_SESSION['id'] - if not present, we're logged out, and this is set to 0
- * 2. The cookie 'elggperm' - if present, checks it for an authentication token, validates it, and potentially logs the user in
+ * 2. The cookie 'elggperm' - if present, checks it for an authentication
+ * token, validates it, and potentially logs the user in
*
* @uses $_SESSION
- * @param unknown_type $event
- * @param unknown_type $object_type
- * @param unknown_type $object
+ *
+ * @return bool
+ * @access private
*/
-function session_init($event, $object_type, $object) {
+function _elgg_session_boot() {
global $DB_PREFIX, $CONFIG;
- if (!is_db_installed()) {
- return false;
- }
-
// Use database for sessions
// HACK to allow access to prefix after object destruction
$DB_PREFIX = $CONFIG->dbprefix;
if ((!isset($CONFIG->use_file_sessions))) {
- session_set_save_handler("__elgg_session_open",
- "__elgg_session_close",
- "__elgg_session_read",
- "__elgg_session_write",
- "__elgg_session_destroy",
- "__elgg_session_gc");
+ session_set_save_handler("_elgg_session_open",
+ "_elgg_session_close",
+ "_elgg_session_read",
+ "_elgg_session_write",
+ "_elgg_session_destroy",
+ "_elgg_session_gc");
}
session_name('Elgg');
@@ -496,7 +404,7 @@ function session_init($event, $object_type, $object) {
// Generate a simple token (private from potentially public session id)
if (!isset($_SESSION['__elgg_session'])) {
- $_SESSION['__elgg_session'] = md5(microtime().rand());
+ $_SESSION['__elgg_session'] = md5(microtime() . rand());
}
// test whether we have a user session
@@ -541,8 +449,8 @@ function session_init($event, $object_type, $object) {
set_last_action($_SESSION['guid']);
}
- register_action("login",true);
- register_action("logout");
+ elgg_register_action('login', '', 'public');
+ elgg_register_action('logout');
// Register a default PAM handler
register_pam_handler('pam_auth_userpass');
@@ -557,42 +465,48 @@ function session_init($event, $object_type, $object) {
return false;
}
- // Since we have loaded a new user, this user may have different language preferences
- register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/");
-
return true;
}
/**
* Used at the top of a page to mark it as logged in users only.
*
+ * @return void
*/
function gatekeeper() {
- if (!isloggedin()) {
+ if (!elgg_is_logged_in()) {
$_SESSION['last_forward_from'] = current_page_url();
register_error(elgg_echo('loggedinrequired'));
- forward();
+ forward('', 'login');
}
}
/**
* Used at the top of a page to mark it as logged in admin or siteadmin only.
*
+ * @return void
*/
function admin_gatekeeper() {
gatekeeper();
- if (!isadminloggedin()) {
+ if (!elgg_is_admin_logged_in()) {
$_SESSION['last_forward_from'] = current_page_url();
register_error(elgg_echo('adminrequired'));
- forward();
+ forward('', 'admin');
}
}
/**
- * DB Based session handling code.
+ * Handles opening a session in the DB
+ *
+ * @param string $save_path The path to save the sessions
+ * @param string $session_name The name of the session
+ *
+ * @return true
+ * @todo Document
+ * @access private
*/
-function __elgg_session_open($save_path, $session_name) {
+function _elgg_session_open($save_path, $session_name) {
global $sess_save_path;
$sess_save_path = $save_path;
@@ -600,16 +514,27 @@ function __elgg_session_open($save_path, $session_name) {
}
/**
- * DB Based session handling code.
+ * Closes a session
+ *
+ * @todo implement
+ * @todo document
+ *
+ * @return true
+ * @access private
*/
-function __elgg_session_close() {
+function _elgg_session_close() {
return true;
}
/**
- * DB Based session handling code.
+ * Read the session data from DB failing back to file.
+ *
+ * @param string $id The session ID
+ *
+ * @return string
+ * @access private
*/
-function __elgg_session_read($id) {
+function _elgg_session_read($id) {
global $DB_PREFIX;
$id = sanitise_string($id);
@@ -635,9 +560,15 @@ function __elgg_session_read($id) {
}
/**
- * DB Based session handling code.
+ * Write session data to the DB falling back to file.
+ *
+ * @param string $id The session ID
+ * @param mixed $sess_data Session data
+ *
+ * @return bool
+ * @access private
*/
-function __elgg_session_write($id, $sess_data) {
+function _elgg_session_write($id, $sess_data) {
global $DB_PREFIX;
$id = sanitise_string($id);
@@ -650,7 +581,7 @@ function __elgg_session_write($id, $sess_data) {
(session, ts, data) VALUES
('$id', '$time', '$sess_data_sanitised')";
- if (insert_data($q)!==false) {
+ if (insert_data($q) !== false) {
return true;
}
} catch (DatabaseException $e) {
@@ -670,9 +601,14 @@ function __elgg_session_write($id, $sess_data) {
}
/**
- * DB Based session handling code.
+ * Destroy a DB session, falling back to file.
+ *
+ * @param string $id Session ID
+ *
+ * @return bool
+ * @access private
*/
-function __elgg_session_destroy($id) {
+function _elgg_session_destroy($id) {
global $DB_PREFIX;
$id = sanitise_string($id);
@@ -685,24 +621,28 @@ function __elgg_session_destroy($id) {
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id";
- return(@unlink($sess_file));
+ return @unlink($sess_file);
}
-
- return false;
}
/**
- * DB Based session handling code.
+ * Perform garbage collection on session table / files
+ *
+ * @param int $maxlifetime Max age of a session
+ *
+ * @return bool
+ * @access private
*/
-function __elgg_session_gc($maxlifetime) {
+function _elgg_session_gc($maxlifetime) {
global $DB_PREFIX;
- $life = time()-$maxlifetime;
+ $life = time() - $maxlifetime;
try {
return (bool)delete_data("DELETE from {$DB_PREFIX}users_sessions where ts<'$life'");
} catch (DatabaseException $e) {
- // Fall back to file store in this case, since this likely means that the database hasn't been upgraded
+ // Fall back to file store in this case, since this likely means that the database
+ // hasn't been upgraded
global $sess_save_path;
foreach (glob("$sess_save_path/sess_*") as $filename) {
@@ -714,5 +654,3 @@ function __elgg_session_gc($maxlifetime) {
return true;
}
-
-register_elgg_event_handler("boot","system","session_init",20);
diff --git a/engine/lib/sites.php b/engine/lib/sites.php
index d67a540cc..3de0eccc2 100644
--- a/engine/lib/sites.php
+++ b/engine/lib/sites.php
@@ -3,315 +3,43 @@
* Elgg sites
* Functions to manage multiple or single sites in an Elgg install
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage DataModel.Site
*/
/**
- * ElggSite
- * Representation of a "site" in the system.
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Core
+ * Get an ElggSite entity (default is current site)
+ *
+ * @param int $site_guid Optional. Site GUID.
+ *
+ * @return ElggSite
+ * @since 1.8.0
*/
-class ElggSite extends ElggEntity {
- /**
- * Initialise the attributes array.
- * This is vital to distinguish between metadata and base parameters.
- *
- * Place your base parameters here.
- */
- protected function initialise_attributes() {
- parent::initialise_attributes();
-
- $this->attributes['type'] = "site";
- $this->attributes['name'] = "";
- $this->attributes['description'] = "";
- $this->attributes['url'] = "";
- $this->attributes['tables_split'] = 2;
- }
-
- /**
- * Construct a new site object, optionally from a given id value.
- *
- * @param mixed $guid If an int, load that GUID.
- * If a db row then will attempt to load the rest of the data.
- * @throws Exception if there was a problem creating the site.
- */
- function __construct($guid = null) {
- $this->initialise_attributes();
-
- if (!empty($guid)) {
- // Is $guid is a DB row - either a entity row, or a site table row.
- if ($guid instanceof stdClass) {
- // Load the rest
- if (!$this->load($guid->guid)) {
- throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid->guid));
- }
- }
-
- // Is $guid is an ElggSite? Use a copy constructor
- else if ($guid instanceof ElggSite) {
- elgg_deprecated_notice('This type of usage of the ElggSite constructor was deprecated. Please use the clone method.', 1.7);
-
- foreach ($guid->attributes as $key => $value) {
- $this->attributes[$key] = $value;
- }
- }
-
- // Is this is an ElggEntity but not an ElggSite = ERROR!
- else if ($guid instanceof ElggEntity) {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggSite'));
- }
-
- // See if this is a URL
- else if (strpos($guid, "http") !== false) {
- $guid = get_site_by_url($guid);
- foreach ($guid->attributes as $key => $value) {
- $this->attributes[$key] = $value;
- }
- }
-
- // We assume if we have got this far, $guid is an int
- else if (is_numeric($guid)) {
- if (!$this->load($guid)) {
- throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid));
- }
- }
-
- else {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
- }
- }
- }
-
- /**
- * Override the load function.
- * This function will ensure that all data is loaded (were possible), so
- * if only part of the ElggSite is loaded, it'll load the rest.
- *
- * @param int $guid
- */
- protected function load($guid) {
- // Test to see if we have the generic stuff
- if (!parent::load($guid)) {
- return false;
- }
-
- // Check the type
- if ($this->attributes['type']!='site') {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, get_class()));
- }
-
- // Load missing data
- $row = get_site_entity_as_row($guid);
- if (($row) && (!$this->isFullyLoaded())) {
- // If $row isn't a cached copy then increment the counter
- $this->attributes['tables_loaded'] ++;
- }
-
- // Now put these into the attributes array as core values
- $objarray = (array) $row;
- foreach($objarray as $key => $value) {
- $this->attributes[$key] = $value;
- }
-
- return true;
- }
-
- /**
- * Override the save function.
- */
- public function save() {
- // Save generic stuff
- if (!parent::save()) {
- return false;
- }
-
- // Now save specific stuff
- return create_site_entity($this->get('guid'), $this->get('name'), $this->get('description'), $this->get('url'));
- }
-
- /**
- * Delete this site.
- */
- public function delete() {
- global $CONFIG;
- if ($CONFIG->site->getGUID() == $this->guid) {
- throw new SecurityException('SecurityException:deletedisablecurrentsite');
- }
-
- return parent::delete();
- }
-
- /**
- * Disable override to add safety rail.
- *
- * @param unknown_type $reason
- */
- public function disable($reason = "") {
- global $CONFIG;
-
- if ($CONFIG->site->getGUID() == $this->guid) {
- throw new SecurityException('SecurityException:deletedisablecurrentsite');
- }
-
- return parent::disable($reason);
- }
-
- /**
- * Return a list of users using this site.
- *
- * @param int $limit
- * @param int $offset
- * @return array of ElggUsers
- */
- public function getMembers($limit = 10, $offset = 0) {
- get_site_members($this->getGUID(), $limit, $offset);
- }
-
- /**
- * Add a user to the site.
- *
- * @param int $user_guid
- */
- public function addUser($user_guid) {
- return add_site_user($this->getGUID(), $user_guid);
- }
-
- /**
- * Remove a site user.
- *
- * @param int $user_guid
- */
- public function removeUser($user_guid) {
- return remove_site_user($this->getGUID(), $user_guid);
- }
-
- /**
- * Get an array of member ElggObjects.
- *
- * @param string $subtype
- * @param int $limit
- * @param int $offset
- */
- public function getObjects($subtype="", $limit = 10, $offset = 0) {
- get_site_objects($this->getGUID(), $subtype, $limit, $offset);
- }
-
- /**
- * Add an object to the site.
- *
- * @param int $user_id
- */
- public function addObject($object_guid) {
- return add_site_object($this->getGUID(), $object_guid);
- }
-
- /**
- * Remove a site user.
- *
- * @param int $user_id
- */
- public function removeObject($object_guid) {
- return remove_site_object($this->getGUID(), $object_guid);
- }
-
- /**
- * Get the collections associated with a site.
- *
- * @param string $type
- * @param int $limit
- * @param int $offset
- * @return unknown
- */
- public function getCollections($subtype="", $limit = 10, $offset = 0) {
- get_site_collections($this->getGUID(), $subtype, $limit, $offset);
- }
+function elgg_get_site_entity($site_guid = 0) {
+ global $CONFIG;
- // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an array of fields which can be exported.
- */
- public function getExportableValues() {
- return array_merge(parent::getExportableValues(), array(
- 'name',
- 'description',
- 'url',
- ));
- }
+ $result = false;
- public function check_walled_garden() {
- global $CONFIG;
-
- if ($CONFIG->walled_garden && !isloggedin()) {
- // hook into the index system call at the highest priority
- register_plugin_hook('index', 'system', 'elgg_walled_garden_index', 1);
-
- if (!$this->is_public_page()) {
- register_error(elgg_echo('loggedinrequired'));
- forward();
- }
- }
+ if ($site_guid == 0) {
+ $site = $CONFIG->site;
+ } else {
+ $site = get_entity($site_guid);
}
- public function is_public_page($url='') {
- global $CONFIG;
-
- if (empty($url)) {
- $url = current_page_url();
-
- // do not check against URL queries
- if ($pos = strpos($url, '?')) {
- $url = substr($url, 0, $pos);
- }
- }
-
- // always allow index page
- if ($url == $CONFIG->url) {
- return TRUE;
- }
-
- // default public pages
- $defaults = array(
- 'action/login',
- 'pg/register',
- 'action/register',
- 'account/forgotten_password\.php',
- 'action/user/requestnewpassword',
- 'pg/resetpassword',
- 'upgrade\.php',
- 'xml-rpc\.php',
- 'mt/mt-xmlrpc\.cgi',
- '_css/css\.css',
- '_css/js\.php',
- );
-
- // include a hook for plugin authors to include public pages
- $plugins = trigger_plugin_hook('public_pages', 'walled_garden', NULL, array());
-
- // lookup admin-specific public pages
-
- // allow public pages
- foreach (array_merge($defaults, $plugins) as $public) {
- $pattern = "`^{$CONFIG->url}$public/*$`i";
- if (preg_match($pattern, $url)) {
- return TRUE;
- }
- }
-
- // non-public page
- return FALSE;
+ if ($site instanceof ElggSite) {
+ $result = $site;
}
+
+ return $result;
}
/**
* Return the site specific details of a site by a row.
*
- * @param int $guid
+ * @param int $guid The site GUID
+ *
+ * @return mixed
+ * @access private
*/
function get_site_entity_as_row($guid) {
global $CONFIG;
@@ -321,13 +49,16 @@ function get_site_entity_as_row($guid) {
}
/**
- * Create or update the extras table for a given site.
+ * Create or update the entities table for a given site.
* Call create_entity first.
*
- * @param int $guid
- * @param string $name
- * @param string $description
- * @param string $url
+ * @param int $guid Site GUID
+ * @param string $name Site name
+ * @param string $description Site Description
+ * @param string $url URL of the site
+ *
+ * @return bool
+ * @access private
*/
function create_site_entity($guid, $name, $description, $url) {
global $CONFIG;
@@ -341,12 +72,16 @@ function create_site_entity($guid, $name, $description, $url) {
if ($row) {
// Exists and you have access to it
- if ($exists = get_data_row("SELECT guid from {$CONFIG->dbprefix}sites_entity where guid = {$guid}")) {
- $result = update_data("UPDATE {$CONFIG->dbprefix}sites_entity set name='$name', description='$description', url='$url' where guid=$guid");
- if ($result!=false) {
+ $query = "SELECT guid from {$CONFIG->dbprefix}sites_entity where guid = {$guid}";
+ if ($exists = get_data_row($query)) {
+ $query = "UPDATE {$CONFIG->dbprefix}sites_entity
+ set name='$name', description='$description', url='$url' where guid=$guid";
+ $result = update_data($query);
+
+ if ($result != false) {
// Update succeeded, continue
$entity = get_entity($guid);
- if (trigger_elgg_event('update',$entity->type,$entity)) {
+ if (elgg_trigger_event('update', $entity->type, $entity)) {
return $guid;
} else {
$entity->delete();
@@ -355,10 +90,13 @@ function create_site_entity($guid, $name, $description, $url) {
}
} else {
// Update failed, attempt an insert.
- $result = insert_data("INSERT into {$CONFIG->dbprefix}sites_entity (guid, name, description, url) values ($guid, '$name','$description','$url')");
- if ($result!==false) {
+ $query = "INSERT into {$CONFIG->dbprefix}sites_entity
+ (guid, name, description, url) values ($guid, '$name', '$description', '$url')";
+ $result = insert_data($query);
+
+ if ($result !== false) {
$entity = get_entity($guid);
- if (trigger_elgg_event('create',$entity->type,$entity)) {
+ if (elgg_trigger_event('create', $entity->type, $entity)) {
return $guid;
} else {
$entity->delete();
@@ -372,27 +110,14 @@ function create_site_entity($guid, $name, $description, $url) {
}
/**
- * THIS FUNCTION IS DEPRECATED.
- *
- * Delete a site's extra data.
- *
- * @param int $guid
- */
-function delete_site_entity($guid) {
- system_message(sprintf(elgg_echo('deprecatedfunction'), 'delete_user_entity'));
-
- return 1; // Always return that we have deleted one row in order to not break existing code.
-}
-
-/**
* Add a user to a site.
*
- * @param int $site_guid
- * @param int $user_guid
+ * @param int $site_guid Site guid
+ * @param int $user_guid User guid
+ *
+ * @return bool
*/
function add_site_user($site_guid, $user_guid) {
- global $CONFIG;
-
$site_guid = (int)$site_guid;
$user_guid = (int)$user_guid;
@@ -402,8 +127,10 @@ function add_site_user($site_guid, $user_guid) {
/**
* Remove a user from a site.
*
- * @param int $site_guid
- * @param int $user_guid
+ * @param int $site_guid Site GUID
+ * @param int $user_guid User GUID
+ *
+ * @return bool
*/
function remove_site_user($site_guid, $user_guid) {
$site_guid = (int)$site_guid;
@@ -413,62 +140,14 @@ function remove_site_user($site_guid, $user_guid) {
}
/**
- * Get the members of a site.
- *
- * @param int $site_guid
- * @param int $limit
- * @param int $offset
- */
-function get_site_members($site_guid, $limit = 10, $offset = 0) {
- $site_guid = (int)$site_guid;
- $limit = (int)$limit;
- $offset = (int)$offset;
-
- return elgg_get_entities_from_relationship(array(
- 'relationship' => 'member_of_site',
- 'relationship_guid' => $site_guid,
- 'inverse_relationship' => TRUE,
- 'types' => 'user',
- 'limit' => $limit, 'offset' => $offset
- ));
-}
-
-/**
- * Display a list of site members
- *
- * @param int $site_guid The GUID of the site
- * @param int $limit The number of members to display on a page
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @return string A displayable list of members
- */
-function list_site_members($site_guid, $limit = 10, $fullview = true) {
- $offset = (int) get_input('offset');
- $limit = (int) $limit;
- $options = array(
- 'relationship' => 'member_of_site',
- 'relationship_guid' => $site_guid,
- 'inverse_relationship' => TRUE,
- 'types' => 'user',
- 'limit' => $limit,
- 'offset' => $offset,
- 'count' => TRUE
- );
- $count = (int) elgg_get_entities_from_relationship($options);
- $entities = get_site_members($site_guid, $limit, $offset);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview);
-
-}
-
-/**
* Add an object to a site.
*
- * @param int $site_guid
- * @param int $object_guid
+ * @param int $site_guid Site GUID
+ * @param int $object_guid Object GUID
+ *
+ * @return mixed
*/
function add_site_object($site_guid, $object_guid) {
- global $CONFIG;
-
$site_guid = (int)$site_guid;
$object_guid = (int)$object_guid;
@@ -478,8 +157,10 @@ function add_site_object($site_guid, $object_guid) {
/**
* Remove an object from a site.
*
- * @param int $site_guid
- * @param int $object_guid
+ * @param int $site_guid Site GUID
+ * @param int $object_guid Object GUID
+ *
+ * @return bool
*/
function remove_site_object($site_guid, $object_guid) {
$site_guid = (int)$site_guid;
@@ -491,77 +172,24 @@ function remove_site_object($site_guid, $object_guid) {
/**
* Get the objects belonging to a site.
*
- * @param int $site_guid
- * @param string $subtype
- * @param int $limit
- * @param int $offset
+ * @param int $site_guid Site GUID
+ * @param string $subtype Subtype
+ * @param int $limit Limit
+ * @param int $offset Offset
+ *
+ * @return mixed
*/
function get_site_objects($site_guid, $subtype = "", $limit = 10, $offset = 0) {
$site_guid = (int)$site_guid;
- $subtype = sanitise_string($subtype);
$limit = (int)$limit;
$offset = (int)$offset;
return elgg_get_entities_from_relationship(array(
'relationship' => 'member_of_site',
- 'relationship_guid' => $site_guid,
- 'inverse_relationship' => TRUE,
- 'types' => 'object',
- 'subtypes' => $subtype,
- 'limit' => $limit,
- 'offset' => $offset
- ));
-}
-
-/**
- * Add a collection to a site.
- *
- * @param int $site_guid
- * @param int $collection_guid
- */
-function add_site_collection($site_guid, $collection_guid) {
- global $CONFIG;
-
- $site_guid = (int)$site_guid;
- $collection_guid = (int)$collection_guid;
-
- return add_entity_relationship($collection_guid, "member_of_site", $site_guid);
-}
-
-/**
- * Remove a collection from a site.
- *
- * @param int $site_guid
- * @param int $collection_guid
- */
-function remove_site_collection($site_guid, $collection_guid) {
- $site_guid = (int)$site_guid;
- $collection_guid = (int)$collection_guid;
-
- return remove_entity_relationship($collection_guid, "member_of_site", $site_guid);
-}
-
-/**
- * Get the collections belonging to a site.
- *
- * @param int $site_guid
- * @param string $subtype
- * @param int $limit
- * @param int $offset
- */
-function get_site_collections($site_guid, $subtype = "", $limit = 10, $offset = 0) {
- $site_guid = (int)$site_guid;
- $subtype = sanitise_string($subtype);
- $limit = (int)$limit;
- $offset = (int)$offset;
-
- // collection isn't a valid type. This won't work.
- return elgg_get_entities_from_relationship(array(
- 'relationship' => 'member_of_site',
- 'relationship_guid' => $site_guid,
- 'inverse_relationship' => TRUE,
- 'types' => 'collection',
- 'subtypes' => $subtype,
+ 'relationship_guid' => $site_guid,
+ 'inverse_relationship' => TRUE,
+ 'type' => 'object',
+ 'subtype' => $subtype,
'limit' => $limit,
'offset' => $offset
));
@@ -569,6 +197,10 @@ function get_site_collections($site_guid, $subtype = "", $limit = 10, $offset =
/**
* Return the site via a url.
+ *
+ * @param string $url The URL of a site
+ *
+ * @return mixed
*/
function get_site_by_url($url) {
global $CONFIG;
@@ -578,63 +210,18 @@ function get_site_by_url($url) {
$row = get_data_row("SELECT * from {$CONFIG->dbprefix}sites_entity where url='$url'");
if ($row) {
- return new ElggSite($row);
+ return get_entity($row->guid);
}
return false;
}
/**
- * Searches for a site based on a complete or partial name or description or url using full text searching.
- *
- * IMPORTANT NOTE: With MySQL's default setup:
- * 1) $criteria must be 4 or more characters long
- * 2) If $criteria matches greater than 50% of results NO RESULTS ARE RETURNED!
- *
- * @param string $criteria The partial or full name or username.
- * @param int $limit Limit of the search.
- * @param int $offset Offset.
- * @param string $order_by The order.
- * @param boolean $count Whether to return the count of results or just the results.
- * @deprecated 1.7
- */
-function search_for_site($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) {
- elgg_deprecated_notice('search_for_site() was deprecated by new search plugin.', 1.7);
- global $CONFIG;
-
- $criteria = sanitise_string($criteria);
- $limit = (int)$limit;
- $offset = (int)$offset;
- $order_by = sanitise_string($order_by);
-
- $access = get_access_sql_suffix("e");
-
- if ($order_by == "") {
- $order_by = "e.time_created desc";
- }
-
- if ($count) {
- $query = "SELECT count(e.guid) as total ";
- } else {
- $query = "SELECT e.* ";
- }
- $query .= "from {$CONFIG->dbprefix}entities e join {$CONFIG->dbprefix}sites_entity s on e.guid=s.guid where match(s.name,s.description,s.url) against ('$criteria') and $access";
-
- 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;
-}
-
-/**
* Retrieve a site and return the domain portion of its url.
*
- * @param int $guid
+ * @param int $guid ElggSite GUID
+ *
+ * @return string
*/
function get_site_domain($guid) {
$guid = (int)$guid;
@@ -649,44 +236,21 @@ function get_site_domain($guid) {
}
/**
- * Initialise site handling
+ * Unit tests for sites
*
- * 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.
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of tests
+ * @param mixed $params Params
*
- * @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
+ * @return array
+ * @access private
*/
-function sites_init($event, $object_type, $object) {
- global $CONFIG;
-
- if (is_installed() && is_db_installed()) {
- $site = 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;
- }
-
- return true;
-}
-
-// Register event handlers
-register_elgg_event_handler('boot','system','sites_init',2);
-
-// Register with unit test
-register_plugin_hook('unit_test', 'system', 'sites_test');
function sites_test($hook, $type, $value, $params) {
global $CONFIG;
$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/social.php b/engine/lib/social.php
deleted file mode 100644
index 381c7ea4f..000000000
--- a/engine/lib/social.php
+++ /dev/null
@@ -1,119 +0,0 @@
-<?php
-/**
- * Elgg Social
- * Functions and objects which provide powerful social aspects within Elgg
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider
- * @link http://elgg.org/
-
-/**
- * Filters a string into an array of significant words
- *
- * @param string $string
- * @return array
- */
-function filter_string($string) {
- // Convert it to lower and trim
- $string = strtolower($string);
- $string = trim($string);
-
- // Remove links and email addresses
- // match protocol://address/path/file.extension?some=variable&another=asf%
- $string = preg_replace("/\s([a-zA-Z]+:\/\/[a-z][a-z0-9\_\.\-]*[a-z]{2,6}[a-zA-Z0-9\/\*\-\?\&\%\=]*)([\s|\.|\,])/iu"," ", $string);
- // match www.something.domain/path/file.extension?some=variable&another=asf%
- $string = preg_replace("/\s(www\.[a-z][a-z0-9\_\.\-]*[a-z]{2,6}[a-zA-Z0-9\/\*\-\?\&\%\=]*)([\s|\.|\,])/iu"," ", $string);
- // match name@address
- $string = preg_replace("/\s([a-zA-Z][a-zA-Z0-9\_\.\-]*[a-zA-Z]*\@[a-zA-Z][a-zA-Z0-9\_\.\-]*[a-zA-Z]{2,6})([\s|\.|\,])/iu"," ", $string);
-
- // Sanitise the string; remove unwanted characters
- $string = preg_replace('/\W/ui', ' ', $string);
-
- // Explode it into an array
- $terms = explode(' ',$string);
-
- // Remove any blacklist terms
- //$terms = array_filter($terms, 'remove_blacklist');
-
- return $terms;
-}
-
-/**
- * Returns true if the word in $input is considered significant
- *
- * @param string $input
- * @return true|false
- */
-function remove_blacklist($input) {
- global $CONFIG;
-
- if (!is_array($CONFIG->wordblacklist)) {
- return $input;
- }
-
- if (strlen($input) < 3 || in_array($input,$CONFIG->wordblacklist)) {
- return false;
- }
-
- return true;
-}
-
-
-/**
- * Initialise.
- *
- * Sets a blacklist of words in the current language. This is a comma separated list in word:blacklist.
- */
-function social_init() {
- global $CONFIG;
-
- $CONFIG->wordblacklist = array();
-
- $list = explode(',', elgg_echo('word:blacklist'));
- if ($list) {
- foreach ($list as $l) {
- $CONFIG->wordblacklist[] = trim($l);
- }
- } else {
- // Fallback - shouldn't happen
- $CONFIG->wordblacklist = array(
- 'and',
- 'the',
- 'then',
- 'but',
- 'she',
- 'his',
- 'her',
- 'him',
- 'one',
- 'not',
- 'also',
- 'about',
- 'now',
- 'hence',
- 'however',
- 'still',
- 'likewise',
- 'otherwise',
- 'therefore',
- 'conversely',
- 'rather',
- 'consequently',
- 'furthermore',
- 'nevertheless',
- 'instead',
- 'meanwhile',
- 'accordingly',
- 'this',
- 'seems',
- 'what',
- 'whom',
- 'whose',
- 'whoever',
- 'whomever',
- );
- }
-}
-
-register_elgg_event_handler("init","system","social_init"); \ No newline at end of file
diff --git a/engine/lib/statistics.php b/engine/lib/statistics.php
index b8bf2b012..4cb0bb0b8 100644
--- a/engine/lib/statistics.php
+++ b/engine/lib/statistics.php
@@ -1,20 +1,20 @@
<?php
/**
* Elgg statistics library.
+ *
* This file contains a number of functions for obtaining statistics about the running system.
- * These statistics are mainly used by the administration pages, and is also where the basic views for statistics
- * are added.
+ * These statistics are mainly used by the administration pages, and is also where the basic
+ * views for statistics are added.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Statistics
*/
/**
* Return an array reporting the number of various entities in the system.
*
* @param int $owner_guid Optional owner of the statistics
+ *
* @return array
*/
function get_entity_statistics($owner_guid = 0) {
@@ -23,7 +23,10 @@ function get_entity_statistics($owner_guid = 0) {
$entity_stats = array();
$owner_guid = (int)$owner_guid;
- $query = "SELECT distinct e.type,s.subtype,e.subtype as subtype_id from {$CONFIG->dbprefix}entities e left join {$CONFIG->dbprefix}entity_subtypes s on e.subtype=s.id";
+ $query = "SELECT distinct e.type,s.subtype,e.subtype as subtype_id
+ from {$CONFIG->dbprefix}entities e left
+ join {$CONFIG->dbprefix}entity_subtypes s on e.subtype=s.id";
+
$owner_query = "";
if ($owner_guid) {
$query .= " where owner_guid=$owner_guid";
@@ -39,9 +42,11 @@ function get_entity_statistics($owner_guid = 0) {
$entity_stats[$type->type] = array();
}
- $query = "SELECT count(*) as count from {$CONFIG->dbprefix}entities where type='{$type->type}' $owner_query";
+ $query = "SELECT count(*) as count
+ from {$CONFIG->dbprefix}entities where type='{$type->type}' $owner_query";
+
if ($type->subtype) {
- $query.= " and subtype={$type->subtype_id}";
+ $query .= " and subtype={$type->subtype_id}";
}
$subtype_cnt = get_data_row($query);
@@ -59,7 +64,8 @@ function get_entity_statistics($owner_guid = 0) {
/**
* Return the number of users registered in the system.
*
- * @param bool $show_deactivated
+ * @param bool $show_deactivated Count not enabled users?
+ *
* @return int
*/
function get_number_users($show_deactivated = false) {
@@ -71,7 +77,10 @@ function get_number_users($show_deactivated = false) {
$access = "and " . get_access_sql_suffix();
}
- $result = get_data_row("SELECT count(*) as count from {$CONFIG->dbprefix}entities where type='user' $access");
+ $query = "SELECT count(*) as count
+ from {$CONFIG->dbprefix}entities where type='user' $access";
+
+ $result = get_data_row($query);
if ($result) {
return $result->count;
@@ -82,28 +91,36 @@ function get_number_users($show_deactivated = false) {
/**
* Return a list of how many users are currently online, rendered as a view.
+ *
+ * @return string
*/
function get_online_users() {
- $offset = get_input('offset', 0);
- $count = count(find_active_users(600, 9999));
- $objects = find_active_users(600, 10, $offset);
+ $limit = max(0, (int) get_input("limit", 10));
+ $offset = max(0, (int) get_input("offset", 0));
+
+ $count = find_active_users(600, $limit, $offset, true);
+ $objects = find_active_users(600, $limit, $offset);
if ($objects) {
- return elgg_view_entity_list($objects, $count,$offset,10,false);
+ return elgg_view_entity_list($objects, array(
+ 'count' => $count,
+ 'limit' => $limit,
+ 'offset' => $offset
+ ));
}
+ return '';
}
/**
* Initialise the statistics admin page.
+ *
+ * @return void
+ * @access private
*/
function statistics_init() {
- extend_elgg_admin_page('admin/statistics_opt/basic', 'admin/statistics');
- extend_elgg_admin_page('admin/statistics_opt/numentities', 'admin/statistics');
- extend_elgg_admin_page('admin/statistics_opt/online', 'admin/statistics');
-
- extend_elgg_settings_page('usersettings/statistics_opt/online', 'usersettings/statistics');
- extend_elgg_settings_page('usersettings/statistics_opt/numentities', 'usersettings/statistics');
+ elgg_extend_view('core/settings/statistics', 'core/settings/statistics/online');
+ elgg_extend_view('core/settings/statistics', 'core/settings/statistics/numentities');
}
/// Register init function
-register_elgg_event_handler('init','system','statistics_init'); \ No newline at end of file
+elgg_register_event_handler('init', 'system', 'statistics_init');
diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php
index 74597ed5d..84302632e 100644
--- a/engine/lib/system_log.php
+++ b/engine/lib/system_log.php
@@ -3,110 +3,72 @@
* Elgg system log.
* Listens to events and writes crud events into the system log database.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Logging
*/
/**
- * Interface that provides an interface which must be implemented by all objects wishing to be
- * recorded in the system log (and by extension the river).
- *
- * This interface defines a set of methods that permit the system log functions to hook in and retrieve
- * the necessary information and to identify what events can actually be logged.
+ * Retrieve the system log based on a number of parameters.
*
- * To have events involving your object to be logged simply implement this interface.
+ * @todo too many args, and the first arg is too confusing
*
- * @author Curverider Ltd
+ * @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
+ * @param string $subtype The subtype.
+ * @param int $limit Maximum number of responses to return.
+ * @param int $offset Offset of where to start.
+ * @param bool $count Return count or not
+ * @param int $timebefore Lower time limit
+ * @param int $timeafter Upper time limit
+ * @param int $object_id GUID of an object
+ * @param string $ip_address The IP address.
+ * @return mixed
*/
-interface Loggable {
- /**
- * Return an identification for the object for storage in the system log.
- * This id must be an integer.
- *
- * @return int
- */
- public function getSystemLogID();
-
- /**
- * Return the class name of the object.
- * Added as a function because get_class causes errors for some reason.
- */
- public function getClassName();
-
- /**
- * Return the type of the object - eg. object, group, user, relationship, metadata, annotation etc
- */
- public function getType();
-
- /**
- * Return a subtype. For metadata & annotations this is the 'name' and for relationship this is the relationship type.
- */
- public function getSubtype();
-
- /**
- * For a given ID, return the object associated with it.
- * This is used by the river functionality primarily.
- * This is useful for checking access permissions etc on objects.
- */
- public function getObjectFromID($id);
-
- /**
- * Return the GUID of the owner of this object.
- */
- public function getObjectOwnerGUID();
-}
+function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "", $limit = 10,
+ $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0,
+ $ip_address = "") {
-/**
- * Retrieve the system log based on a number of parameters.
- *
- * @param int or array $by_user The guid(s) of the user(s) who initiated the event.
- * @param string $event The event you are searching on.
- * @param string $class The class of object it effects.
- * @param string $type The type
- * @param string $subtype The subtype.
- * @param int $limit Maximum number of responses to return.
- * @param int $offset Offset of where to start.
- * @param bool $count Return count or not
- */
-function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "", $limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0) {
global $CONFIG;
$by_user_orig = $by_user;
if (is_array($by_user) && sizeof($by_user) > 0) {
- foreach($by_user as $key => $val) {
+ foreach ($by_user as $key => $val) {
$by_user[$key] = (int) $val;
}
} 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)) {
- $where [] = "performed_by_guid in (". implode(",",$by_user) .")";
+ $where [] = "performed_by_guid in (" . implode(",", $by_user) . ")";
}
}
if ($event != "") {
$where[] = "event='$event'";
}
- if ($class!=="") {
+ if ($class !== "") {
$where[] = "object_class='$class'";
}
if ($type != "") {
$where[] = "object_type='$type'";
}
- if ($subtype!=="") {
+ if ($subtype !== "") {
$where[] = "object_subtype='$subtype'";
}
@@ -119,6 +81,9 @@ function get_system_log($by_user = "", $event = "", $class = "", $type = "", $su
if ($object_id) {
$where[] = "object_id = " . ((int) $object_id);
}
+ if ($ip_address) {
+ $where[] = "ip_address = '$ip_address'";
+ }
$select = "*";
if ($count) {
@@ -135,7 +100,8 @@ function get_system_log($by_user = "", $event = "", $class = "", $type = "", $su
}
if ($count) {
- if ($numrows = get_data_row($query)) {
+ $numrows = get_data_row($query);
+ if ($numrows) {
return $numrows->count;
}
} else {
@@ -149,6 +115,8 @@ function get_system_log($by_user = "", $event = "", $class = "", $type = "", $su
* Return a specific log entry.
*
* @param int $entry_id The log entry
+ *
+ * @return mixed
*/
function get_log_entry($entry_id) {
global $CONFIG;
@@ -162,15 +130,20 @@ function get_log_entry($entry_id) {
* Return the object referred to by a given log entry
*
* @param int $entry_id The log entry
+ *
+ * @return mixed
*/
function get_object_from_log_entry($entry_id) {
$entry = get_log_entry($entry_id);
if ($entry) {
$class = $entry->object_class;
- $tmp = new $class();
- $object = $tmp->getObjectFromID($entry->object_id);
-
+ // surround with try/catch because object could be disabled
+ try {
+ $object = new $class($entry->object_id);
+ } catch (Exception $e) {
+
+ }
if ($object) {
return $object;
}
@@ -184,16 +157,27 @@ function get_object_from_log_entry($entry_id) {
*
* This is called by the event system and should not be called directly.
*
- * @param $object The object you're talking about.
- * @param $event String The event being logged
+ * @param object $object The object you're talking about.
+ * @param string $event The event being logged
+ * @return void
*/
function system_log($object, $event) {
global $CONFIG;
- static $logcache;
+ static $log_cache;
+ static $cache_size = 0;
if ($object instanceof Loggable) {
- if (!is_array($logcache)) {
- $logcache = array();
+
+ /* @var ElggEntity|ElggExtender $object */
+ if (datalist_get('version') < 2012012000) {
+ // this is a site that doesn't have the ip_address column yet
+ return;
+ }
+
+ // reset cache if it has grown too large
+ if (!is_array($log_cache) || $cache_size > 500) {
+ $log_cache = array();
+ $cache_size = 0;
}
// Has loggable interface, extract the necessary information and store
@@ -203,7 +187,17 @@ function system_log($object, $event) {
$object_subtype = $object->getSubtype();
$event = sanitise_string($event);
$time = time();
- $performed_by = get_loggedin_userid();
+
+ if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+ $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
+ } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
+ $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_REAL_IP']));
+ } else {
+ $ip_address = $_SERVER['REMOTE_ADDR'];
+ }
+ $ip_address = sanitise_string($ip_address);
+
+ $performed_by = elgg_get_logged_in_user_guid();
if (isset($object->access_id)) {
$access_id = $object->access_id;
@@ -223,13 +217,19 @@ function system_log($object, $event) {
}
// Create log if we haven't already created it
- if (!isset($logcache[$time][$object_id][$event])) {
- insert_data("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) VALUES ('$object_id','$object_class','$object_type', '$object_subtype', '$event',$performed_by, $owner_guid, $access_id, '$enabled', '$time')");
-
- $logcache[$time][$object_id][$event] = true;
+ 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, ip_address)
+ VALUES
+ ('$object_id','$object_class','$object_type', '$object_subtype', '$event',
+ $performed_by, $owner_guid, $access_id, '$enabled', '$time', '$ip_address')";
+
+ insert_data($query);
+
+ $log_cache[$time][$object_id][$event] = true;
+ $cache_size += 1;
}
-
- return true;
}
}
@@ -237,6 +237,8 @@ function system_log($object, $event) {
* This function creates an archive copy of the system log.
*
* @param int $offset An offset in seconds from now to archive (useful for log rotation)
+ *
+ * @return bool
*/
function archive_log($offset = 0) {
global $CONFIG;
@@ -247,7 +249,10 @@ function archive_log($offset = 0) {
$ts = $now - $offset;
// create table
- if (!update_data("CREATE TABLE {$CONFIG->dbprefix}system_log_$now as SELECT * from {$CONFIG->dbprefix}system_log WHERE time_created<$ts")) {
+ $query = "CREATE TABLE {$CONFIG->dbprefix}system_log_$now as
+ SELECT * from {$CONFIG->dbprefix}system_log WHERE time_created<$ts";
+
+ if (!update_data($query)) {
return false;
}
@@ -268,10 +273,11 @@ function archive_log($offset = 0) {
/**
* Default system log handler, allows plugins to override, extend or disable logging.
*
- * @param string $event
- * @param string $object_type
- * @param Loggable $object
- * @return unknown
+ * @param string $event Event name
+ * @param string $object_type Object type
+ * @param Loggable $object Object to log
+ *
+ * @return true
*/
function system_log_default_logger($event, $object_type, $object) {
system_log($object['object'], $object['event']);
@@ -283,20 +289,23 @@ function system_log_default_logger($event, $object_type, $object) {
* System log listener.
* This function listens to all events in the system and logs anything appropriate.
*
- * @param String $event
- * @param String $object_type
- * @param Loggable $object
+ * @param String $event Event name
+ * @param String $object_type Type of object
+ * @param Loggable $object Object to log
+ *
+ * @return true
+ * @access private
*/
function system_log_listener($event, $object_type, $object) {
- if (($object_type!='systemlog') && ($event!='log')) {
- trigger_elgg_event('log', 'systemlog', array('object' => $object, 'event' => $event));
+ if (($object_type != 'systemlog') && ($event != 'log')) {
+ elgg_trigger_event('log', 'systemlog', array('object' => $object, 'event' => $event));
}
return true;
}
/** Register event to listen to all events **/
-register_elgg_event_handler('all','all','system_log_listener', 400);
+elgg_register_event_handler('all', 'all', 'system_log_listener', 400);
/** Register a default system log handler */
-register_elgg_event_handler('log','systemlog','system_log_default_logger', 999); \ No newline at end of file
+elgg_register_event_handler('log', 'systemlog', 'system_log_default_logger', 999);
diff --git a/engine/lib/tags.php b/engine/lib/tags.php
index ce1fbbf93..586a9b9e4 100644
--- a/engine/lib/tags.php
+++ b/engine/lib/tags.php
@@ -3,28 +3,33 @@
* Elgg tags
* Functions for managing tags and tag clouds.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Tags
*/
-
/**
* The algorithm working out the size of font based on the number of tags.
* This is quick and dirty.
+ *
+ * @param int $min Min size
+ * @param int $max Max size
+ * @param int $number_of_tags The number of tags
+ * @param int $buckets The number of buckets
+ *
+ * @return int
+ * @access private
*/
function calculate_tag_size($min, $max, $number_of_tags, $buckets = 6) {
- $delta = (($max - $min) / $buckets);
+ $delta = (($max - $min) / $buckets);
$thresholds = array();
- for ($n=1; $n <= $buckets; $n++) {
- $thresholds[$n-1] = ($min + $n) * $delta;
+ for ($n = 1; $n <= $buckets; $n++) {
+ $thresholds[$n - 1] = ($min + $n) * $delta;
}
// Correction
- if ($thresholds[$buckets-1]>$max) {
- $thresholds[$buckets-1] = $max;
+ if ($thresholds[$buckets - 1] > $max) {
+ $thresholds[$buckets - 1] = $max;
}
$size = 0;
@@ -40,8 +45,11 @@ function calculate_tag_size($min, $max, $number_of_tags, $buckets = 6) {
/**
* This function generates an array of tags with a weighting.
*
- * @param array $tags The array of tags.
- * @return An associated array of tags with a weighting, this can then be mapped to a display class.
+ * @param array $tags The array of tags.
+ * @param int $buckets The number of buckets
+ *
+ * @return array An associated array of tags with a weighting, this can then be mapped to a display class.
+ * @access private
*/
function generate_tag_cloud(array $tags, $buckets = 6) {
$cloud = array();
@@ -52,11 +60,11 @@ function generate_tag_cloud(array $tags, $buckets = 6) {
foreach ($tags as $tag) {
$cloud[$tag]++;
- if ($cloud[$tag]>$max) {
+ if ($cloud[$tag] > $max) {
$max = $cloud[$tag];
}
- if ($cloud[$tag]<$min) {
+ if ($cloud[$tag] < $min) {
$min = $cloud[$tag];
}
}
@@ -73,8 +81,6 @@ function generate_tag_cloud(array $tags, $buckets = 6) {
*
* Supports similar arguments as elgg_get_entities()
*
- * @since 1.7.1
- *
* @param array $options Array in format:
*
* threshold => INT minimum tag count
@@ -87,7 +93,8 @@ function generate_tag_cloud(array $tags, $buckets = 6) {
*
* subtypes => NULL|STR entity subtype (SQL: subtype = '$subtype')
*
- * type_subtype_pairs => NULL|ARR (array('type' => 'subtype')) (SQL: type = '$type' AND subtype = '$subtype') pairs
+ * type_subtype_pairs => NULL|ARR (array('type' => 'subtype'))
+ * (SQL: type = '$type' AND subtype = '$subtype') pairs
*
* owner_guids => NULL|INT entity guid
*
@@ -107,8 +114,9 @@ function generate_tag_cloud(array $tags, $buckets = 6) {
*
* joins => array() Additional joins
*
- * @return false/array - if no tags or error, false
- * otherwise, array of objects with ->tag and ->total values
+ * @return object[]|false If no tags or error, false
+ * otherwise, array of objects with ->tag and ->total values
+ * @since 1.7.1
*/
function elgg_get_tags(array $options = array()) {
global $CONFIG;
@@ -138,10 +146,9 @@ function elgg_get_tags(array $options = array()) {
$options = array_merge($defaults, $options);
- $singulars = array('type', 'subtype', 'owner_guid', 'container_guid', 'site_guid');
+ $singulars = array('type', 'subtype', 'owner_guid', 'container_guid', 'site_guid', 'tag_name');
$options = elgg_normalise_plural_options_array($options, $singulars);
-
$registered_tags = elgg_get_registered_tag_metadata_names();
if (!is_array($options['tag_names'])) {
@@ -165,22 +172,21 @@ function elgg_get_tags(array $options = array()) {
// catch for tags that were spaces
$wheres[] = "msv.string != ''";
+ $sanitised_tags = array();
foreach ($options['tag_names'] as $tag) {
$sanitised_tags[] = '"' . sanitise_string($tag) . '"';
}
$tags_in = implode(',', $sanitised_tags);
$wheres[] = "(msn.string IN ($tags_in))";
- $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'], $options['subtypes'], $options['type_subtype_pairs']);
- $wheres[] = elgg_get_entity_site_where_sql('e', $options['site_guids']);
- $wheres[] = elgg_get_entity_owner_where_sql('e', $options['owner_guids']);
- $wheres[] = elgg_get_entity_container_where_sql('e', $options['container_guids']);
+ $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'],
+ $options['subtypes'], $options['type_subtype_pairs']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']);
$wheres[] = elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
$options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
- // remove identical where clauses
- $wheres = array_unique($wheres);
-
// see if any functions failed
// remove empty strings on successful functions
foreach ($wheres as $i => $where) {
@@ -191,6 +197,8 @@ function elgg_get_tags(array $options = array()) {
}
}
+ // remove identical where clauses
+ $wheres = array_unique($wheres);
$joins = $options['joins'];
@@ -229,7 +237,7 @@ function elgg_get_tags(array $options = array()) {
$query .= get_access_sql_suffix('e');
$threshold = sanitise_int($options['threshold']);
- $query .= " GROUP BY msv.string HAVING total > {$threshold} ";
+ $query .= " GROUP BY msv.string HAVING total >= {$threshold} ";
$query .= " ORDER BY total DESC ";
$limit = sanitise_int($options['limit']);
@@ -239,75 +247,8 @@ function elgg_get_tags(array $options = array()) {
}
/**
- * Get an array of tags with weights for use with the output/tagcloud view.
- *
- * @deprecated 1.8 Use elgg_get_tags().
- *
- * @param int $threshold Get the threshold of minimum number of each tags to bother with (ie only show tags where there are more than $threshold occurances)
- * @param int $limit Number of tags to return
- * @param string $metadata_name Optionally, the name of the field you want to grab for
- * @param string $entity_type Optionally, the entity type ('object' etc)
- * @param string $entity_subtype The entity subtype, optionally
- * @param int $owner_guid The GUID of the tags owner, optionally
- * @param int $site_guid Optionally, the site to restrict to (default is the current site)
- * @param int $start_ts Optionally specify a start timestamp for tags used to generate cloud.
- * @param int $ent_ts Optionally specify an end timestamp for tags used to generate cloud.
- * @return array|false Array of objects with ->tag and ->total values, or false on failure
- */
-
-function get_tags($threshold = 1, $limit = 10, $metadata_name = "", $entity_type = "object", $entity_subtype = "", $owner_guid = "", $site_guid = -1, $start_ts = "", $end_ts = "") {
-
- elgg_deprecated_notice('get_tags() has been replaced by elgg_get_tags()', 1.8);
-
- if (is_array($metadata_name)) {
- return false;
- }
-
- $options = array();
- if ($metadata_name === '') {
- $options['tag_names'] = array();
- } else {
- $options['tag_names'] = array($metadata_name);
- }
-
- $options['threshold'] = $threshold;
- $options['limit'] = $limit;
-
- // rewrite owner_guid to container_guid to emulate old functionality
- $container_guid = $owner_guid;
- if ($container_guid) {
- $options['container_guids'] = $container_guid;
- }
-
- if ($entity_type) {
- $options['type'] = $entity_type;
- }
-
- if ($entity_subtype) {
- $options['subtype'] = $entity_subtype;
- }
-
- if ($site_guid != -1) {
- $options['site_guids'] = $site_guid;
- }
-
- if ($end_ts) {
- $options['created_time_upper'] = $end_ts;
- }
-
- if ($start_ts) {
- $options['created_time_lower'] = $start_ts;
- }
-
- $r = elgg_get_tags($options);
- return $r;
-}
-
-/**
* Returns viewable tagcloud
*
- * @since 1.7.1
- *
* @see elgg_get_tags
*
* @param array $options Any elgg_get_tags() options except:
@@ -317,7 +258,7 @@ function get_tags($threshold = 1, $limit = 10, $metadata_name = "", $entity_type
* subtype => must be single entity subtype
*
* @return string
- *
+ * @since 1.7.1
*/
function elgg_view_tagcloud(array $options = array()) {
@@ -328,40 +269,12 @@ function elgg_view_tagcloud(array $options = array()) {
if (isset($options['subtype'])) {
$subtype = $options['subtype'];
}
-
- $tag_data = elgg_get_tags($options);
- return elgg_view("output/tagcloud",array('value' => $tag_data,
- 'type' => $type,
- 'subtype' => $subtype));
-}
-
-/**
- * Loads and displays a tagcloud given particular criteria.
- *
- * @deprecated 1.8 use elgg_view_tagcloud()
- *
- * @param int $threshold Get the threshold of minimum number of each tags to bother with (ie only show tags where there are more than $threshold occurances)
- * @param int $limit Number of tags to return
- * @param string $metadata_name Optionally, the name of the field you want to grab for
- * @param string $entity_type Optionally, the entity type ('object' etc)
- * @param string $entity_subtype The entity subtype, optionally
- * @param int $owner_guid The GUID of the tags owner, optionally
- * @param int $site_guid Optionally, the site to restrict to (default is the current site)
- * @param int $start_ts Optionally specify a start timestamp for tags used to generate cloud.
- * @param int $ent_ts Optionally specify an end timestamp for tags used to generate cloud.
- * @return string The HTML (or other, depending on view type) of the tagcloud.
- */
-
-function display_tagcloud($threshold = 1, $limit = 10, $metadata_name = "", $entity_type = "object", $entity_subtype = "", $owner_guid = "", $site_guid = -1, $start_ts = "", $end_ts = "") {
-
- elgg_deprecated_notice('display_cloud() was deprecated by elgg_view_tagcloud()!', 1.8);
-
- $tags = get_tags($threshold, $limit, $metadata_name, $entity_type, $entity_subtype, $owner_guid, $site_guid, $start_ts, $end_ts);
- return elgg_view('output/tagcloud', array(
- 'value' => $tags,
- 'type' => $entity_type,
- 'subtype' => $entity_subtype,
+ $tag_data = elgg_get_tags($options);
+ return elgg_view("output/tagcloud", array(
+ 'value' => $tag_data,
+ 'type' => $type,
+ 'subtype' => $subtype,
));
}
@@ -370,10 +283,10 @@ function display_tagcloud($threshold = 1, $limit = 10, $metadata_name = "", $ent
* This is required if you are using a non-standard metadata name
* for your tags.
*
- * @since 1.7
+ * @param string $name Tag name
*
- * @param string $name
- * @return TRUE
+ * @return bool
+ * @since 1.7.0
*/
function elgg_register_tag_metadata_name($name) {
global $CONFIG;
@@ -392,31 +305,50 @@ function elgg_register_tag_metadata_name($name) {
/**
* Returns an array of valid metadata names for tags.
*
- * @since 1.7
- *
* @return array
+ * @since 1.7.0
*/
function elgg_get_registered_tag_metadata_names() {
global $CONFIG;
- $names = (isset($CONFIG->registered_tag_metadata_names)) ? $CONFIG->registered_tag_metadata_names : array();
+ $names = (isset($CONFIG->registered_tag_metadata_names))
+ ? $CONFIG->registered_tag_metadata_names : array();
+
return $names;
}
-// register the standard tags metadata name
-elgg_register_tag_metadata_name('tags');
-
-register_page_handler('tags', 'elgg_tagcloud_page_handler');
+/**
+ * Page hander for tags
+ *
+ * @param array $page Page array
+ *
+ * @return bool
+ * @access private
+ */
function elgg_tagcloud_page_handler($page) {
- global $CONFIG;
+
+ $title = elgg_view_title(elgg_echo('tags:site_cloud'));
+ $options = array(
+ 'threshold' => 0,
+ 'limit' => 100,
+ 'tag_name' => 'tags',
+ );
+ $tags = elgg_view_tagcloud($options);
+ $content = $title . $tags;
+ $body = elgg_view_layout('one_sidebar', array('content' => $content));
+
+ echo elgg_view_page(elgg_echo('tags:site_cloud'), $body);
+ return true;
+}
+
+/**
+ * @access private
+ */
+function elgg_tags_init() {
+ // register the standard tags metadata name
+ elgg_register_tag_metadata_name('tags');
- switch ($page[0]) {
- default:
- $title = elgg_view_title(elgg_echo('tags:site_cloud'));
- $tags = display_tagcloud(0, 100, 'tags');
- $body = elgg_view_layout('one_column_with_sidebar', $title . $tags);
-
- page_draw(elgg_echo('tags:site_cloud'), $body);
- break;
- }
+ elgg_register_page_handler('tags', 'elgg_tagcloud_page_handler');
}
+
+elgg_register_event_handler('init', 'system', 'elgg_tags_init'); \ No newline at end of file
diff --git a/engine/lib/upgrade.php b/engine/lib/upgrade.php
new file mode 100644
index 000000000..158ec9ec1
--- /dev/null
+++ b/engine/lib/upgrade.php
@@ -0,0 +1,365 @@
+<?php
+/**
+ * Elgg upgrade library.
+ * Contains code for handling versioning and upgrades.
+ *
+ * @package Elgg.Core
+ * @subpackage Upgrade
+ */
+
+/**
+ * Run any php upgrade scripts which are required
+ *
+ * @param int $version Version upgrading from.
+ * @param bool $quiet Suppress errors. Don't use this.
+ *
+ * @return bool
+ * @access private
+ */
+function upgrade_code($version, $quiet = FALSE) {
+ // do not remove - upgrade scripts depend on this
+ global $CONFIG;
+
+ $version = (int) $version;
+ $upgrade_path = elgg_get_config('path') . 'engine/lib/upgrades/';
+ $processed_upgrades = elgg_get_processed_upgrades();
+
+ // upgrading from 1.7 to 1.8. Need to bootstrap.
+ if (!$processed_upgrades) {
+ elgg_upgrade_bootstrap_17_to_18();
+
+ // grab accurate processed upgrades
+ $processed_upgrades = elgg_get_processed_upgrades();
+ }
+
+ $upgrade_files = elgg_get_upgrade_files($upgrade_path);
+
+ if ($upgrade_files === false) {
+ return false;
+ }
+
+ $upgrades = elgg_get_unprocessed_upgrades($upgrade_files, $processed_upgrades);
+
+ // Sort and execute
+ sort($upgrades);
+
+ foreach ($upgrades as $upgrade) {
+ $upgrade_version = elgg_get_upgrade_file_version($upgrade);
+ $success = true;
+
+ // hide all errors.
+ if ($quiet) {
+ // hide include errors as well as any exceptions that might happen
+ try {
+ if (!@include("$upgrade_path/$upgrade")) {
+ $success = false;
+ error_log("Could not include $upgrade_path/$upgrade");
+ }
+ } catch (Exception $e) {
+ $success = false;
+ error_log($e->getmessage());
+ }
+ } else {
+ if (!include("$upgrade_path/$upgrade")) {
+ $success = false;
+ error_log("Could not include $upgrade_path/$upgrade");
+ }
+ }
+
+ if ($success) {
+ // incrementally set upgrade so we know where to start if something fails.
+ $processed_upgrades[] = $upgrade;
+
+ // don't set the version to a lower number in instances where an upgrade
+ // has been merged from a lower version of Elgg
+ if ($upgrade_version > $version) {
+ datalist_set('version', $upgrade_version);
+ }
+
+ elgg_set_processed_upgrades($processed_upgrades);
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Saves the processed upgrades to a dataset.
+ *
+ * @param array $processed_upgrades An array of processed upgrade filenames
+ * (not the path, just the file)
+ * @return bool
+ * @access private
+ */
+function elgg_set_processed_upgrades(array $processed_upgrades) {
+ $processed_upgrades = array_unique($processed_upgrades);
+ return datalist_set('processed_upgrades', serialize($processed_upgrades));
+}
+
+/**
+ * Gets a list of processes upgrades
+ *
+ * @return mixed Array of processed upgrade filenames or false
+ * @access private
+ */
+function elgg_get_processed_upgrades() {
+ $upgrades = datalist_get('processed_upgrades');
+ $unserialized = unserialize($upgrades);
+ return $unserialized;
+}
+
+/**
+ * Returns the version of the upgrade filename.
+ *
+ * @param string $filename The upgrade filename. No full path.
+ * @return int|false
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_get_upgrade_file_version($filename) {
+ preg_match('/^([0-9]{10})([\.a-z0-9-_]+)?\.(php)$/i', $filename, $matches);
+
+ if (isset($matches[1])) {
+ return (int) $matches[1];
+ }
+
+ return false;
+}
+
+/**
+ * Returns a list of upgrade files relative to the $upgrade_path dir.
+ *
+ * @param string $upgrade_path The up
+ * @return array|false
+ * @access private
+ */
+function elgg_get_upgrade_files($upgrade_path = null) {
+ if (!$upgrade_path) {
+ $upgrade_path = elgg_get_config('path') . 'engine/lib/upgrades/';
+ }
+ $upgrade_path = sanitise_filepath($upgrade_path);
+ $handle = opendir($upgrade_path);
+
+ if (!$handle) {
+ return false;
+ }
+
+ $upgrade_files = array();
+
+ while ($upgrade_file = readdir($handle)) {
+ // make sure this is a wellformed upgrade.
+ if (is_dir($upgrade_path . '$upgrade_file')) {
+ continue;
+ }
+ $upgrade_version = elgg_get_upgrade_file_version($upgrade_file);
+ if (!$upgrade_version) {
+ continue;
+ }
+ $upgrade_files[] = $upgrade_file;
+ }
+
+ sort($upgrade_files);
+
+ return $upgrade_files;
+}
+
+/**
+ * Get the current Elgg version information
+ *
+ * @param bool $humanreadable Whether to return a human readable version (default: false)
+ *
+ * @return string|false Depending on success
+ */
+function get_version($humanreadable = false) {
+ global $CONFIG;
+
+ static $version, $release;
+
+ if (isset($CONFIG->path)) {
+ if (!isset($version) || !isset($release)) {
+ if (!include($CONFIG->path . "version.php")) {
+ return false;
+ }
+ }
+ return (!$humanreadable) ? $version : $release;
+ }
+
+ return false;
+}
+
+/**
+ * Checks if any upgrades need to be run.
+ *
+ * @param null|array $upgrade_files Optional upgrade files
+ * @param null|array $processed_upgrades Optional processed upgrades
+ *
+ * @return array
+ * @access private
+ */
+function elgg_get_unprocessed_upgrades($upgrade_files = null, $processed_upgrades = null) {
+ if ($upgrade_files === null) {
+ $upgrade_files = elgg_get_upgrade_files();
+ }
+
+ if ($processed_upgrades === null) {
+ $processed_upgrades = unserialize(datalist_get('processed_upgrades'));
+ if (!is_array($processed_upgrades)) {
+ $processed_upgrades = array();
+ }
+ }
+
+ $unprocessed = array_diff($upgrade_files, $processed_upgrades);
+ return $unprocessed;
+}
+
+/**
+ * Determines whether or not the database needs to be upgraded.
+ *
+ * @return bool Depending on whether or not the db version matches the code version
+ * @access private
+ */
+function version_upgrade_check() {
+ $dbversion = (int) datalist_get('version');
+ $version = get_version();
+
+ if ($version > $dbversion) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Upgrades Elgg Database and code
+ *
+ * @return bool
+ * @access private
+ */
+function version_upgrade() {
+ // It's possible large upgrades could exceed the max execution time.
+ set_time_limit(0);
+
+ $dbversion = (int) datalist_get('version');
+
+ // No version number? Oh snap...this is an upgrade from a clean installation < 1.7.
+ // Run all upgrades without error reporting and hope for the best.
+ // See https://github.com/elgg/elgg/issues/1432 for more.
+ $quiet = !$dbversion;
+
+ // Note: Database upgrades are deprecated as of 1.8. Use code upgrades. See #1433
+ if (db_upgrade($dbversion, '', $quiet)) {
+ system_message(elgg_echo('upgrade:db'));
+ }
+
+ if (upgrade_code($dbversion, $quiet)) {
+ system_message(elgg_echo('upgrade:core'));
+
+ // Now we trigger an event to give the option for plugins to do something
+ $upgrade_details = new stdClass;
+ $upgrade_details->from = $dbversion;
+ $upgrade_details->to = get_version();
+
+ elgg_trigger_event('upgrade', 'upgrade', $upgrade_details);
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Boot straps into 1.8 upgrade system from 1.7
+ *
+ * This runs all the 1.7 upgrades, then sets the processed_upgrades to all existing 1.7 upgrades.
+ * Control is then passed back to the main upgrade function which detects and runs the
+ * 1.8 upgrades, regardless of filename convention.
+ *
+ * @return bool
+ * @access private
+ */
+function elgg_upgrade_bootstrap_17_to_18() {
+ $db_version = (int) datalist_get('version');
+
+ // the 1.8 upgrades before the upgrade system change that are interspersed with 1.7 upgrades.
+ $upgrades_18 = array(
+ '2010111501.php',
+ '2010121601.php',
+ '2010121602.php',
+ '2010121701.php',
+ '2010123101.php',
+ '2011010101.php',
+ );
+
+ $upgrade_files = elgg_get_upgrade_files();
+ $processed_upgrades = array();
+
+ foreach ($upgrade_files as $upgrade_file) {
+ // ignore if not in 1.7 format or if it's a 1.8 upgrade
+ if (in_array($upgrade_file, $upgrades_18) || !preg_match("/[0-9]{10}\.php/", $upgrade_file)) {
+ continue;
+ }
+
+ $upgrade_version = elgg_get_upgrade_file_version($upgrade_file);
+
+ // this has already been run in a previous 1.7.X -> 1.7.X upgrade
+ if ($upgrade_version < $db_version) {
+ $processed_upgrades[] = $upgrade_file;
+ }
+ }
+
+ return elgg_set_processed_upgrades($processed_upgrades);
+}
+
+/**
+ * Creates a table {prefix}upgrade_lock that is used as a mutex for upgrades.
+ *
+ * @see _elgg_upgrade_lock()
+ *
+ * @return bool
+ * @access private
+ */
+function _elgg_upgrade_lock() {
+ global $CONFIG;
+
+ if (!_elgg_upgrade_is_locked()) {
+ // lock it
+ insert_data("create table {$CONFIG->dbprefix}upgrade_lock (id INT)");
+ elgg_log('Locked for upgrade.', 'NOTICE');
+ return true;
+ }
+
+ elgg_log('Cannot lock for upgrade: already locked.', 'WARNING');
+ return false;
+}
+
+/**
+ * Unlocks upgrade.
+ *
+ * @see _elgg_upgrade_lock()
+ *
+ * @access private
+ */
+function _elgg_upgrade_unlock() {
+ global $CONFIG;
+ delete_data("drop table {$CONFIG->dbprefix}upgrade_lock");
+ elgg_log('Upgrade unlocked.', 'NOTICE');
+}
+
+/**
+ * Checks if upgrade is locked
+ *
+ * @return bool
+ * @access private
+ */
+function _elgg_upgrade_is_locked() {
+ global $CONFIG;
+
+ $is_locked = count(get_data("show tables like '{$CONFIG->dbprefix}upgrade_lock'"));
+
+ // @todo why?
+ _elgg_invalidate_query_cache();
+
+ return $is_locked;
+}
diff --git a/engine/lib/upgrades/2008100701.php b/engine/lib/upgrades/2008100701.php
index cf976e84c..b8d4dfdbc 100644
--- a/engine/lib/upgrades/2008100701.php
+++ b/engine/lib/upgrades/2008100701.php
@@ -1,8 +1,7 @@
<?php
- /// Activate mail plugin
- /**
- * Because Elgg now has a plugable account activation process we need to activate
- * the email account activation plugin for existing installs.
- */
- enable_plugin('uservalidationbyemail', $CONFIG->site->guid);
-?> \ No newline at end of file
+
+/**
+ * Because Elgg now has a plugable account activation process we need to activate
+ * the email account activation plugin for existing installs.
+ */
+enable_plugin('uservalidationbyemail', $CONFIG->site->guid);
diff --git a/engine/lib/upgrades/2008101303.php b/engine/lib/upgrades/2008101303.php
index c98eace74..69e44e3a0 100644
--- a/engine/lib/upgrades/2008101303.php
+++ b/engine/lib/upgrades/2008101303.php
@@ -1,11 +1,9 @@
<?php
- // Upgrade to solve login issue
-
- if ($users = get_entities_from_metadata('validated_email', '', 'user', '', 0,9999)) {
- foreach($users as $user) {
- set_user_validation_status($user->guid, true, 'email');
- }
- }
-
-?> \ No newline at end of file
+// Upgrade to solve login issue
+
+if ($users = get_entities_from_metadata('validated_email', '', 'user', '', 0, 9999)) {
+ foreach ($users as $user) {
+ set_user_validation_status($user->guid, true, 'email');
+ }
+}
diff --git a/engine/lib/upgrades/2009022701.php b/engine/lib/upgrades/2009022701.php
index 92d540cd3..54083a34d 100644
--- a/engine/lib/upgrades/2009022701.php
+++ b/engine/lib/upgrades/2009022701.php
@@ -1,8 +1,7 @@
<?php
- global $CONFIG;
-
- /**
- * Disable update client since this has now been removed.
- */
- disable_plugin('updateclient', $CONFIG->site->guid);
-?> \ No newline at end of file
+global $CONFIG;
+
+/**
+ * Disable update client since this has now been removed.
+ */
+disable_plugin('updateclient', $CONFIG->site->guid);
diff --git a/engine/lib/upgrades/2009041701.php b/engine/lib/upgrades/2009041701.php
index 609c7e569..7b31a3bc9 100644
--- a/engine/lib/upgrades/2009041701.php
+++ b/engine/lib/upgrades/2009041701.php
@@ -1,10 +1,8 @@
<?php
- global $CONFIG;
-
- /// Activate kses
- /**
- * Elgg now has kses tag filtering built as a plugin. This needs to be enabled.
- */
- enable_plugin('kses', $CONFIG->site->guid);
-?> \ No newline at end of file
+global $CONFIG;
+
+/**
+ * Elgg now has kses tag filtering built as a plugin. This needs to be enabled.
+ */
+enable_plugin('kses', $CONFIG->site->guid);
diff --git a/engine/lib/upgrades/2009070101.php b/engine/lib/upgrades/2009070101.php
index edd8ce4b4..d0eae9b91 100644
--- a/engine/lib/upgrades/2009070101.php
+++ b/engine/lib/upgrades/2009070101.php
@@ -1,11 +1,9 @@
<?php
- global $CONFIG;
-
- /// Deprecate kses and activate htmlawed
- /**
- * Kses appears to be a dead project so we are deprecating it in favour of htmlawed.
- */
- disable_plugin('kses', $CONFIG->site->guid);
- enable_plugin('htmlawed', $CONFIG->site->guid);
-?> \ No newline at end of file
+global $CONFIG;
+
+/**
+ * Kses appears to be a dead project so we are deprecating it in favour of htmlawed.
+ */
+disable_plugin('kses', $CONFIG->site->guid);
+enable_plugin('htmlawed', $CONFIG->site->guid);
diff --git a/engine/lib/upgrades/2009102801.php b/engine/lib/upgrades/2009102801.php
index b72e0a781..3ad113fb2 100644
--- a/engine/lib/upgrades/2009102801.php
+++ b/engine/lib/upgrades/2009102801.php
@@ -1,154 +1,180 @@
<?php
-// disable timeout for large sites.
-set_time_limit(0);
+/**
+ * Move user's data directories from using username to registration date
+ */
/**
- Elgg 1.0
+ * Generates a file matrix like Elgg 1.0 did
+ *
+ * @param string $username Username of user
+ *
+ * @return string File matrix path
*/
-function file_matrix_1_0($username)
-{
+function file_matrix_1_0($username) {
$matrix = "";
-
+
$len = strlen($username);
- if ($len > 5)
+ if ($len > 5) {
$len = 5;
-
+ }
+
for ($n = 0; $n < $len; $n++) {
- if (ctype_alnum($username[$n]))
+ if (ctype_alnum($username[$n])) {
$matrix .= $username[$n] . "/";
- }
+ }
+ }
- return $matrix.$username."/";
+ return $matrix . $username . "/";
}
/**
- Elgg 1.1, 1.2 and 1.5
+ * Generate a file matrix like Elgg 1.1, 1.2 and 1.5
+ *
+ * @param string $filename The filename
+ *
+ * @return string
*/
-function file_matrix_1_1($filename)
-{
+function file_matrix_1_1($filename) {
$matrix = "";
-
+
$name = $filename;
$filename = mb_str_split($filename);
- if (!$filename) return false;
-
+ if (!$filename) {
+ return false;
+ }
+
$len = count($filename);
- if ($len > 5)
+ if ($len > 5) {
$len = 5;
-
+ }
+
for ($n = 0; $n < $len; $n++) {
$matrix .= $filename[$n] . "/";
- }
+ }
- return $matrix.$name."/";
+ return $matrix . $name . "/";
}
-function mb_str_split($string, $charset = 'UTF8')
-{
- if (is_callable('mb_substr'))
- {
+/**
+ * Handle splitting multibyte strings
+ *
+ * @param string $string String to split.
+ * @param string $charset Charset to use.
+ *
+ * @return array|false
+ */
+function mb_str_split($string, $charset = 'UTF8') {
+ if (is_callable('mb_substr')) {
$length = mb_strlen($string);
$array = array();
-
- while ($length)
- {
+
+ while ($length) {
$array[] = mb_substr($string, 0, 1, $charset);
$string = mb_substr($string, 1, $length, $charset);
-
+
$length = mb_strlen($string);
}
-
+
return $array;
- }
- else
+ } else {
return str_split($string);
-
+ }
+
return false;
}
/**
- Elgg 1.6
+ * 1.6 style file matrix
+ *
+ * @param string $filename The filename
+ *
+ * @return string
*/
-function file_matrix_1_6($filename)
-{
+function file_matrix_1_6($filename) {
$invalid_fs_chars = '*\'\\/"!$%^&*.%(){}[]#~?<>;|¬`@-+=';
-
+
$matrix = "";
-
+
$name = $filename;
$filename = mb_str_split($filename);
- if (!$filename) return false;
-
+ if (!$filename) {
+ return false;
+ }
+
$len = count($filename);
- if ($len > 5)
+ if ($len > 5) {
$len = 5;
-
+ }
+
for ($n = 0; $n < $len; $n++) {
-
+
// Prevent a matrix being formed with unsafe characters
$char = $filename[$n];
- if (strpos($invalid_fs_chars, $char)!==false)
+ if (strpos($invalid_fs_chars, $char) !== false) {
$char = '_';
-
+ }
+
$matrix .= $char . "/";
- }
+ }
- return $matrix.$name."/";
+ return $matrix . $name . "/";
}
/**
- * Scans a directory and moves any files from $from to $to
+ * Scans a directory and moves any files from $from to $to
* preserving structure and handling existing paths.
* Will no overwrite files in $to.
*
* TRAILING SLASHES REQUIRED.
*
- * @param $from From dir.
- * @param $to To dir.
- * @param $move Bool. True to move, false to copy.
- * @param $preference str to|from If file collisions, which dir has preference.
+ * @param string $from From dir.
+ * @param string $to To dir.
+ * @param bool $move True to move, false to copy.
+ * @param string $preference to|from If file collisions, which dir has preference.
+ *
+ * @return bool
*/
-function merge_directories($from, $to, $move=false, $preference='to') {
+function merge_directories($from, $to, $move = false, $preference = 'to') {
if (!$entries = scandir($from)) {
return false;
}
-
+
// character filtering needs to be elsewhere.
if (!is_dir($to)) {
mkdir($to, 0700, true);
}
-
+
if ($move === true) {
$f = 'rename';
} else {
$f = 'copy';
}
-
+
foreach ($entries as $entry) {
if ($entry == '.' || $entry == '..') {
continue;
}
-
+
$from_path = $from . $entry;
$to_path = $to . $entry;
-
+
// check to see if the path exists and is a dir, if so, recurse.
if (is_dir($from_path) && is_dir($to_path)) {
$from_path .= '/';
$to_path .= '/';
merge_directories($from_path, $to_path, $move, $preference);
-
+
// since it's a dir that already exists we don't need to move it
continue;
}
-
+
// only move if target doesn't exist or if preference is for the from dir
if (!file_exists($to_path) || $preference == 'from') {
-
+
if ($f($from_path, $to_path)) {
//elgg_dump("Moved/Copied $from_path to $to_path");
}
@@ -158,36 +184,39 @@ function merge_directories($from, $to, $move=false, $preference='to') {
}
}
+/**
+ * Create a 1.7 style user file matrix based upon date.
+ *
+ * @param int $guid Guid of owner
+ *
+ * @return string File matrix path
+ */
function user_file_matrix($guid) {
// lookup the entity
$user = get_entity($guid);
- if ($user->type != 'user')
- {
+ if ($user->type != 'user') {
// only to be used for user directories
return FALSE;
}
-
- if (!$user->time_created) {
- // fall back to deprecated method
- return $this->deprecated_file_matrix($user->username);
- }
-
+
$time_created = date('Y/m/d', $user->time_created);
return "$time_created/$user->guid/";
}
-global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE;
+global $ENTITY_CACHE, $CONFIG;
/**
- Upgrade file locations
+ * Upgrade file locations
*/
-$users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity WHERE username != ''");
+$users = mysql_query("SELECT guid, username
+ FROM {$CONFIG->dbprefix}users_entity WHERE username != ''");
while ($user = mysql_fetch_object($users)) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
$to = $CONFIG->dataroot . user_file_matrix($user->guid);
foreach (array('1_0', '1_1', '1_6') as $version) {
$function = "file_matrix_$version";
$from = $CONFIG->dataroot . $function($user->username);
- merge_directories($from, $to, $move=TRUE, $preference='from');
+ merge_directories($from, $to, $move = TRUE, $preference = 'from');
}
}
diff --git a/engine/lib/upgrades/2010033101.php b/engine/lib/upgrades/2010033101.php
index b137e0285..4779295fd 100644
--- a/engine/lib/upgrades/2010033101.php
+++ b/engine/lib/upgrades/2010033101.php
@@ -1,6 +1,7 @@
<?php
-/*
- * Conditional upgrade for UTF8 as described in http://trac.elgg.org/ticket/1928
+
+/**
+ * Conditional upgrade for UTF8 as described in https://github.com/elgg/elgg/issues/1928
*/
// get_version() returns the code version.
@@ -38,20 +39,24 @@ if ($dbversion < 2009100701) {
$qs[] = "ALTER TABLE {$CONFIG->dbprefix}groups_entity DISABLE KEYS";
$qs[] = "REPLACE INTO {$CONFIG->dbprefix}groups_entity (guid, name, description)
- SELECT guid, unhex(hex(convert(name using latin1))), unhex(hex(convert(description using latin1)))
+ SELECT guid, unhex(hex(convert(name using latin1))),
+ unhex(hex(convert(description using latin1)))
FROM {$CONFIG->dbprefix}groups_entity";
$qs[] = "ALTER TABLE {$CONFIG->dbprefix}groups_entity ENABLE KEYS";
$qs[] = "ALTER TABLE {$CONFIG->dbprefix}objects_entity DISABLE KEYS";
$qs[] = "REPLACE INTO {$CONFIG->dbprefix}objects_entity (guid, title, description)
- SELECT guid, unhex(hex(convert(title using latin1))), unhex(hex(convert(description using latin1)))
+ SELECT guid, unhex(hex(convert(title using latin1))),
+ unhex(hex(convert(description using latin1)))
FROM {$CONFIG->dbprefix}objects_entity";
$qs[] = "ALTER TABLE {$CONFIG->dbprefix}objects_entity ENABLE KEYS";
$qs[] = "ALTER TABLE {$CONFIG->dbprefix}users_entity DISABLE KEYS";
- $qs[] = "REPLACE INTO {$CONFIG->dbprefix}users_entity (guid, name, username, password, salt, email, language, code,
+ $qs[] = "REPLACE INTO {$CONFIG->dbprefix}users_entity
+ (guid, name, username, password, salt, email, language, code,
banned, last_action, prev_last_action, last_login, prev_last_login)
- SELECT guid, unhex(hex(convert(name using latin1))), username, password, salt, email, language, code,
+ SELECT guid, unhex(hex(convert(name using latin1))),
+ username, password, salt, email, language, code,
banned, last_action, prev_last_action, last_login, prev_last_login
FROM {$CONFIG->dbprefix}users_entity";
$qs[] = "ALTER TABLE {$CONFIG->dbprefix}users_entity ENABLE KEYS";
@@ -62,4 +67,4 @@ if ($dbversion < 2009100701) {
}
}
}
-} \ No newline at end of file
+}
diff --git a/engine/lib/upgrades/2010040201.php b/engine/lib/upgrades/2010040201.php
index 22eee15f8..789bf5dfc 100644
--- a/engine/lib/upgrades/2010040201.php
+++ b/engine/lib/upgrades/2010040201.php
@@ -1,4 +1,5 @@
<?php
+
/**
* Pull admin metadata setting into users_entity table column
*/
@@ -37,4 +38,4 @@ $qs[] = "DELETE FROM {$CONFIG->dbprefix}metadata
foreach ($qs as $q) {
update_data($q);
-} \ No newline at end of file
+}
diff --git a/engine/lib/upgrades/2010050701.php b/engine/lib/upgrades/2010050701.php
deleted file mode 100644
index 4a02a77c6..000000000
--- a/engine/lib/upgrades/2010050701.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-/**
- * Removes the Walled Garden plugin in favor of new system settings
- */
-
-$access = elgg_set_ignore_access(TRUE);
-
-if (is_plugin_enabled('walledgarden')) {
- disable_plugin('walledgarden');
- set_config('allow_registration', FALSE);
- set_config('walled_garden', TRUE);
-} else {
- set_config('allow_registration', TRUE);
- set_config('walled_garden', FALSE);
-}
-
-elgg_set_ignore_access($access);
diff --git a/engine/lib/upgrades/2010052601.php b/engine/lib/upgrades/2010052601.php
index 5b477910f..a9cca6dc5 100644
--- a/engine/lib/upgrades/2010052601.php
+++ b/engine/lib/upgrades/2010052601.php
@@ -9,14 +9,14 @@ $params = array('type' => 'group',
$groups = elgg_get_entities($params);
if ($groups) {
foreach ($groups as $group) {
- $group->name = html_entity_decode($group->name, ENT_COMPAT, 'UTF-8');
- $group->description = html_entity_decode($group->description, ENT_COMPAT, 'UTF-8');
- $group->briefdescription = html_entity_decode($group->briefdescription, ENT_COMPAT, 'UTF-8');
- $group->website = html_entity_decode($group->website, ENT_COMPAT, 'UTF-8');
+ $group->name = _elgg_html_decode($group->name);
+ $group->description = _elgg_html_decode($group->description);
+ $group->briefdescription = _elgg_html_decode($group->briefdescription);
+ $group->website = _elgg_html_decode($group->website);
if ($group->interests) {
$tags = $group->interests;
- foreach ($tags as $index=>$tag) {
- $tags[$index] = html_entity_decode($tag, ENT_COMPAT, 'UTF-8');
+ foreach ($tags as $index => $tag) {
+ $tags[$index] = _elgg_html_decode($tag);
}
$group->interests = $tags;
}
diff --git a/engine/lib/upgrades/2010060101.php b/engine/lib/upgrades/2010060101.php
index 7772c42eb..bb7f7c1a6 100644
--- a/engine/lib/upgrades/2010060101.php
+++ b/engine/lib/upgrades/2010060101.php
@@ -10,7 +10,7 @@ delete_data($query);
if ($CONFIG->simplecache_enabled) {
datalist_set('simplecache_enabled', 1);
- elgg_view_regenerate_simplecache();
+ elgg_regenerate_simplecache();
} else {
datalist_set('simplecache_enabled', 0);
}
diff --git a/engine/lib/upgrades/2010060401.php b/engine/lib/upgrades/2010060401.php
index 2106bdbc0..6d628b8eb 100644
--- a/engine/lib/upgrades/2010060401.php
+++ b/engine/lib/upgrades/2010060401.php
@@ -11,7 +11,8 @@ $count = 0;
$user_guids = mysql_query("SELECT guid FROM {$CONFIG->dbprefix}users_entity");
while ($user = mysql_fetch_object($user_guids)) {
- $query = "SELECT * FROM {$CONFIG->dbprefix}entity_relationships WHERE guid_one=$user->guid AND relationship LIKE 'notify%'";
+ $query = "SELECT * FROM {$CONFIG->dbprefix}entity_relationships
+ WHERE guid_one=$user->guid AND relationship LIKE 'notify%'";
$relationships = mysql_query($query);
if (mysql_num_rows($relationships) == 0) {
// no notify relationships for this user
@@ -42,11 +43,12 @@ while ($user = mysql_fetch_object($user_guids)) {
WHERE guid_one=$user->guid AND relationship='$relationship_type'
AND guid_two=$obj->guid_two";
$results = mysql_query($query);
- if (mysql_num_rows($results) == 0) {
- $query = "DELETE FROM {$CONFIG->dbprefix}entity_relationships WHERE id=$obj->id";
- mysql_query($query);
- $count++;
- }
+
+ if (mysql_num_rows($results) == 0) {
+ $query = "DELETE FROM {$CONFIG->dbprefix}entity_relationships WHERE id=$obj->id";
+ mysql_query($query);
+ $count++;
+ }
}
}
diff --git a/engine/lib/upgrades/2010061501.php b/engine/lib/upgrades/2010061501.php
index 19d6467ed..744c28fd5 100644
--- a/engine/lib/upgrades/2010061501.php
+++ b/engine/lib/upgrades/2010061501.php
@@ -1,70 +1,75 @@
<?php
/**
- * utf8 conversion and file merging for usernames with multibyte chars
- *
+ * utf8 database conversion and file merging for usernames with multibyte chars
+ *
*/
// check that we need to do the utf8 conversion
// C&P logic from 2010033101
-set_time_limit(0);
$dbversion = (int) datalist_get('version');
if ($dbversion < 2009100701) {
// start a new link to the DB to see what its defaults are.
$link = mysql_connect($CONFIG->dbhost, $CONFIG->dbuser, $CONFIG->dbpass, TRUE);
mysql_select_db($CONFIG->dbname, $link);
-
+
$q = "SHOW VARIABLES LIKE 'character_set_client'";
$r = mysql_query($q);
$client = mysql_fetch_assoc($r);
-
+
$q = "SHOW VARIABLES LIKE 'character_set_connection'";
$r = mysql_query($q);
$connection = mysql_fetch_assoc($r);
-
+
// only run upgrade if not already talking utf8
if ($client['Value'] != 'utf8' && $connection['Value'] != 'utf8') {
$qs = array();
$qs[] = "SET NAMES utf8";
-
+
$qs[] = "ALTER TABLE {$CONFIG->dbprefix}users_entity DISABLE KEYS";
- $qs[] = "REPLACE INTO {$CONFIG->dbprefix}users_entity (guid, name, username, password, salt, email, language, code,
- banned, last_action, prev_last_action, last_login, prev_last_login)
- SELECT guid, name, unhex(hex(convert(username using latin1))), password, salt, email, language, code,
- banned, last_action, prev_last_action, last_login, prev_last_login
+ $qs[] = "REPLACE INTO {$CONFIG->dbprefix}users_entity
+ (guid, name, username, password, salt, email, language, code,
+ banned, admin, last_action, prev_last_action, last_login, prev_last_login)
+
+ SELECT guid, name, unhex(hex(convert(username using latin1))),
+ password, salt, email, language, code,
+ banned, admin, last_action, prev_last_action, last_login, prev_last_login
FROM {$CONFIG->dbprefix}users_entity";
-
+
$qs[] = "ALTER TABLE {$CONFIG->dbprefix}users_entity ENABLE KEYS";
-
+
foreach ($qs as $q) {
if (!update_data($q)) {
throw new Exception('Couldn\'t execute upgrade query: ' . $q);
}
}
-
- global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE;
-
+
+ global $ENTITY_CACHE;
+
/**
Upgrade file locations
*/
// new connection to force into utf8 mode to get the old name
$link = mysql_connect($CONFIG->dbhost, $CONFIG->dbuser, $CONFIG->dbpass, TRUE);
mysql_select_db($CONFIG->dbname, $link);
-
+
// must be the first command
mysql_query("SET NAMES utf8");
-
- $users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity WHERE username != ''", $link);
+
+ $users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity
+ WHERE username != ''", $link);
while ($user = mysql_fetch_object($users)) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
-
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
+
+
$to = $CONFIG->dataroot . user_file_matrix($user->guid);
foreach (array('1_0', '1_1', '1_6') as $version) {
$function = "file_matrix_$version";
$from = $CONFIG->dataroot . $function($user->username);
- merge_directories($from, $to, $move=TRUE, $preference='from');
+ merge_directories($from, $to, $move = TRUE, $preference = 'from');
}
}
}
-} \ No newline at end of file
+}
diff --git a/engine/lib/upgrades/2010062301.php b/engine/lib/upgrades/2010062301.php
new file mode 100644
index 000000000..f679fa46d
--- /dev/null
+++ b/engine/lib/upgrades/2010062301.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * Change ownership of group ACLs to group entity
+ */
+
+elgg_set_ignore_access(TRUE);
+
+$params = array('type' => 'group',
+ 'limit' => 0);
+$groups = elgg_get_entities($params);
+if ($groups) {
+ foreach ($groups as $group) {
+ $acl = $group->group_acl;
+
+ try {
+ $query = "UPDATE {$CONFIG->dbprefix}access_collections
+ SET owner_guid = $group->guid WHERE id = $acl";
+ update_data($query);
+ } catch (Exception $e) {
+ // no acl so create one
+ $ac_name = elgg_echo('groups:group') . ": " . $group->name;
+ $group_acl = create_access_collection($ac_name, $group->guid);
+ if ($group_acl) {
+ create_metadata($group->guid, 'group_acl', $group_acl, 'integer', $group->owner_guid);
+ $object->group_acl = $group_id;
+ }
+ }
+
+ }
+}
+elgg_set_ignore_access(FALSE);
+
diff --git a/engine/lib/upgrades/2010062302.php b/engine/lib/upgrades/2010062302.php
new file mode 100644
index 000000000..fe33e12ea
--- /dev/null
+++ b/engine/lib/upgrades/2010062302.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * Make sure that everyone who belongs to a group is a member of the group's access collection
+ */
+
+
+elgg_set_ignore_access(TRUE);
+
+$params = array('type' => 'group', 'limit' => 0);
+$groups = elgg_get_entities($params);
+if ($groups) {
+ foreach ($groups as $group) {
+ $acl = $group->group_acl;
+
+ $query = "SELECT u.guid FROM {$CONFIG->dbprefix}users_entity u
+ JOIN {$CONFIG->dbprefix}entity_relationships r
+ ON u.guid = r.guid_one AND r.relationship = 'member' AND r.guid_two = $group->guid
+ LEFT JOIN {$CONFIG->dbprefix}access_collection_membership a
+ ON u.guid = a.user_guid AND a.access_collection_id = $acl
+ WHERE a.user_guid IS NULL";
+
+ $results = get_data($query);
+ if ($results != FALSE) {
+ foreach ($results as $user) {
+ $insert = "INSERT INTO {$CONFIG->dbprefix}access_collection_membership
+ (user_guid, access_collection_id) VALUES ($user->guid, $acl)";
+ insert_data($insert);
+ }
+ }
+ }
+}
+elgg_set_ignore_access(FALSE);
diff --git a/engine/lib/upgrades/2010070301.php b/engine/lib/upgrades/2010070301.php
new file mode 100644
index 000000000..af5c80419
--- /dev/null
+++ b/engine/lib/upgrades/2010070301.php
@@ -0,0 +1,9 @@
+<?php
+
+/**
+ * Group join river view has been renamed
+ */
+
+$query = "UPDATE {$CONFIG->dbprefix}river SET view='river/relationship/member/create'
+ WHERE view='river/group/create' AND action_type='join'";
+update_data($query);
diff --git a/engine/lib/upgrades/2010071001.php b/engine/lib/upgrades/2010071001.php
new file mode 100644
index 000000000..5594493a8
--- /dev/null
+++ b/engine/lib/upgrades/2010071001.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Change profile image names to use guid rather than username
+ */
+
+/**
+ * Need the same function to generate a user matrix, but can't call it
+ * the same thing as the previous update.
+ *
+ * @param int $guid User guid.
+ *
+ * @return string File matrix
+ */
+function user_file_matrix_2010071001($guid) {
+ // lookup the entity
+ $user = get_entity($guid);
+ if ($user->type != 'user') {
+ // only to be used for user directories
+ return FALSE;
+ }
+
+ if (!$user->time_created) {
+ // no idea where this user has its files
+ return FALSE;
+ }
+
+ $time_created = date('Y/m/d', $user->time_created);
+ return "$time_created/$user->guid/";
+}
+
+$sizes = array('large', 'medium', 'small', 'tiny', 'master', 'topbar');
+
+global $ENTITY_CACHE, $CONFIG;
+$users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity
+ WHERE username != ''");
+while ($user = mysql_fetch_object($users)) {
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
+
+ $user_directory = user_file_matrix_2010071001($user->guid);
+ if (!$user_directory) {
+ continue;
+ }
+ $profile_directory = $CONFIG->dataroot . $user_directory . "profile/";
+ if (!file_exists($profile_directory)) {
+ continue;
+ }
+
+ foreach ($sizes as $size) {
+ $old_filename = "$profile_directory{$user->username}{$size}.jpg";
+ $new_filename = "$profile_directory{$user->guid}{$size}.jpg";
+ if (file_exists($old_filename)) {
+ if (!rename($old_filename, $new_filename)) {
+ error_log("Failed to rename profile photo for $user->username");
+ }
+ }
+ }
+}
diff --git a/engine/lib/upgrades/2010071002.php b/engine/lib/upgrades/2010071002.php
new file mode 100644
index 000000000..52aa15ef5
--- /dev/null
+++ b/engine/lib/upgrades/2010071002.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Update the notifications based on all friends and access collections
+ */
+
+// loop through all users checking collections and notifications
+global $ENTITY_CACHE, $CONFIG;
+global $NOTIFICATION_HANDLERS;
+$users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity
+ WHERE username != ''");
+while ($user = mysql_fetch_object($users)) {
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
+
+ $user = get_entity($user->guid);
+ foreach ($NOTIFICATION_HANDLERS as $method => $foo) {
+ $notify = "notify$method";
+ $metaname = "collections_notifications_preferences_$method";
+ $collections_preferences = $user->$metaname;
+ if (!$collections_preferences) {
+ continue;
+ }
+ if (!is_array($collections_preferences)) {
+ $collections_preferences = array($collections_preferences);
+ }
+ foreach ($collections_preferences as $collection_id) {
+ // check the all friends notifications
+ if ($collection_id == -1) {
+ $options = array(
+ 'relationship' => 'friend',
+ 'relationship_guid' => $user->guid,
+ 'limit' => 0
+ );
+ $friends = elgg_get_entities_from_relationship($options);
+ foreach ($friends as $friend) {
+ if (!check_entity_relationship($user->guid, $notify, $friend->guid)) {
+ add_entity_relationship($user->guid, $notify, $friend->guid);
+ }
+ }
+ } else {
+ $members = get_members_of_access_collection($collection_id, TRUE);
+ foreach ($members as $member) {
+ if (!check_entity_relationship($user->guid, $notify, $members)) {
+ add_entity_relationship($user->guid, $notify, $member);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/engine/lib/upgrades/2010111501.php b/engine/lib/upgrades/2010111501.php
new file mode 100644
index 000000000..15e4a7d35
--- /dev/null
+++ b/engine/lib/upgrades/2010111501.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Set validation metadata on unvalidated users to false rather than
+ * not existing. This is needed because of the change in how validation is
+ * being handled.
+ */
+
+// turn off system log because of all the metadata this can create
+elgg_unregister_event_handler('all', 'all', 'system_log_listener');
+elgg_unregister_event_handler('log', 'systemlog', 'system_log_default_logger');
+
+$ia = elgg_set_ignore_access(TRUE);
+$hidden_entities = access_get_show_hidden_status();
+access_show_hidden_entities(TRUE);
+
+$validated_id = get_metastring_id('validated');
+$one_id = get_metastring_id(1);
+
+$query = "SELECT guid FROM {$CONFIG->dbprefix}entities e
+ WHERE e.type = 'user' AND e.enabled = 'no' AND
+ NOT EXISTS (
+ SELECT 1 FROM {$CONFIG->dbprefix}metadata md
+ WHERE md.entity_guid = e.guid
+ AND md.name_id = $validated_id
+ AND md.value_id = $one_id)";
+
+$user_guids = mysql_query($query);
+while ($user_guid = mysql_fetch_object($user_guids)) {
+ create_metadata($user_guid->guid, 'validated', false, '', 0, ACCESS_PUBLIC, false);
+}
+
+access_show_hidden_entities($hidden_entities);
+elgg_set_ignore_access($ia);
diff --git a/engine/lib/upgrades/2010121601.php b/engine/lib/upgrades/2010121601.php
new file mode 100644
index 000000000..ad7d26adb
--- /dev/null
+++ b/engine/lib/upgrades/2010121601.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Create friends river view has been changed
+ */
+
+$query = "UPDATE {$CONFIG->dbprefix}river
+ SET view='river/relationship/friend/create', action_type='create'
+ WHERE view='friends/river/create' AND action_type='friend'";
+update_data($query);
diff --git a/engine/lib/upgrades/2010121602.php b/engine/lib/upgrades/2010121602.php
new file mode 100644
index 000000000..5b0996b5e
--- /dev/null
+++ b/engine/lib/upgrades/2010121602.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Create comment river view has been changed
+ */
+
+$query = "UPDATE {$CONFIG->dbprefix}river
+ SET view='river/annotation/generic_comment/create'
+ WHERE view='annotation/annotate' AND action_type='comment'";
+update_data($query);
+
diff --git a/engine/lib/upgrades/2010121701.php b/engine/lib/upgrades/2010121701.php
new file mode 100644
index 000000000..375654bac
--- /dev/null
+++ b/engine/lib/upgrades/2010121701.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Create group forum topic river view has been changed
+ */
+
+$query = "UPDATE {$CONFIG->dbprefix}river
+ SET view='river/object/groupforumtopic/create'
+ WHERE view='river/forum/topic/create' AND action_type='create'";
+update_data($query);
+
diff --git a/engine/lib/upgrades/2010123101.php b/engine/lib/upgrades/2010123101.php
new file mode 100644
index 000000000..f4befd1a8
--- /dev/null
+++ b/engine/lib/upgrades/2010123101.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * Set default access for older sites
+ */
+
+$access = elgg_get_config('default_access');
+if ($access == false) {
+ elgg_save_config('default_access', ACCESS_LOGGED_IN);
+}
diff --git a/engine/lib/upgrades/2011010101.php b/engine/lib/upgrades/2011010101.php
new file mode 100644
index 000000000..f4411ee20
--- /dev/null
+++ b/engine/lib/upgrades/2011010101.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * Migrate plugins to the new system using ElggPlugin and private settings
+ */
+
+$old_ia = elgg_set_ignore_access(true);
+
+$site = get_config('site');
+$old_plugin_order = unserialize($site->pluginorder);
+$old_enabled_plugins = $site->enabled_plugins;
+
+$db_prefix = get_config('dbprefix');
+$plugin_subtype_id = get_subtype_id('object', 'plugin');
+
+// easy one first: make sure the the site owns all plugin entities.
+$q = "UPDATE {$db_prefix}entities e
+ SET owner_guid = $site->guid, container_guid = $site->guid
+ WHERE e.type = 'object' AND e.subtype = $plugin_subtype_id";
+
+$r = update_data($q);
+
+// rewrite all plugin:setting:* to ELGG_PLUGIN_USER_SETTING_PREFIX . *
+$q = "UPDATE {$db_prefix}private_settings
+ SET name = replace(name, 'plugin:settings:', '" . ELGG_PLUGIN_USER_SETTING_PREFIX . "')
+ WHERE name LIKE 'plugin:settings:%'";
+
+$r = update_data($q);
+
+// grab current plugin GUIDs to add a temp priority
+$q = "SELECT * FROM {$db_prefix}entities e
+ JOIN {$db_prefix}objects_entity oe ON e.guid = oe.guid
+ WHERE e.type = 'object' AND e.subtype = $plugin_subtype_id";
+
+$plugins = get_data($q);
+
+foreach ($plugins as $plugin) {
+ $priority = elgg_namespace_plugin_private_setting('internal', 'priority');
+ set_private_setting($plugin->guid, $priority, 0);
+}
+
+// force regenerating plugin entities
+elgg_generate_plugin_entities();
+
+// set the priorities for all plugins
+// this function rewrites it to a normal index so use the current one.
+elgg_set_plugin_priorities($old_plugin_order);
+
+// add relationships for enabled plugins
+if ($old_enabled_plugins) {
+ // they might only have one plugin enabled.
+ if (!is_array($old_enabled_plugins)) {
+ $old_enabled_plugins = array($old_enabled_plugins);
+ }
+
+ // sometimes there were problems and you'd get 1000s of enabled plugins.
+ $old_enabled_plugins = array_unique($old_enabled_plugins);
+
+ foreach ($old_enabled_plugins as $plugin_id) {
+ $plugin = elgg_get_plugin_from_id($plugin_id);
+
+ if ($plugin) {
+ $plugin->activate();
+ }
+ }
+}
+
+// invalidate caches
+elgg_invalidate_simplecache();
+elgg_reset_system_cache();
+
+// clean up.
+remove_metadata($site->guid, 'pluginorder');
+remove_metadata($site->guid, 'enabled_plugins');
+
+elgg_set_ignore_access($old_id);
+
+/**
+ * @hack
+ *
+ * We stop the upgrade at this point because plugins weren't given the chance to
+ * load due to the new plugin code introduced with Elgg 1.8. Instead, we manually
+ * set the version and start the upgrade process again.
+ *
+ * The variables from upgrade_code() are available because this script was included
+ */
+if ($upgrade_version > $version) {
+ datalist_set('version', $upgrade_version);
+}
+
+// add ourselves to the processed_upgrades.
+$processed_upgrades[] = '2011010101.php';
+
+$processed_upgrades = array_unique($processed_upgrades);
+elgg_set_processed_upgrades($processed_upgrades);
+
+_elgg_upgrade_unlock();
+
+forward('upgrade.php');
diff --git a/engine/lib/upgrades/2011021800-1.8_svn-goodbye_walled_garden-083121a656d06894.php b/engine/lib/upgrades/2011021800-1.8_svn-goodbye_walled_garden-083121a656d06894.php
new file mode 100644
index 000000000..40b2c71d5
--- /dev/null
+++ b/engine/lib/upgrades/2011021800-1.8_svn-goodbye_walled_garden-083121a656d06894.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Elgg 1.8-svn upgrade 2011021800
+ * goodbye_walled_garden
+ *
+ * Removes the Walled Garden plugin in favor of new system settings
+ */
+
+global $CONFIG;
+
+$access = elgg_set_ignore_access(TRUE);
+
+if (elgg_is_active_plugin('walledgarden')) {
+ disable_plugin('walledgarden');
+ set_config('allow_registration', FALSE);
+ set_config('walled_garden', TRUE);
+} else {
+ set_config('allow_registration', TRUE);
+ set_config('walled_garden', FALSE);
+}
+
+// this was for people who manually set the config option
+$disable_registration = elgg_get_config('disable_registration');
+if ($disable_registration !== null) {
+ $allow_registration = !$disable_registration;
+ elgg_save_config('allow_registration', $allow_registration);
+
+ $site = elgg_get_site_entity();
+ $query = "DELETE FROM {$CONFIG->dbprefix}config
+ WHERE name = 'disable_registration' AND site_guid = $site->guid";
+ delete_data($query);
+}
+
+elgg_set_ignore_access($access);
diff --git a/engine/lib/upgrades/2011022000-1.8_svn-custom_profile_fields-390ac967b0bb5665.php b/engine/lib/upgrades/2011022000-1.8_svn-custom_profile_fields-390ac967b0bb5665.php
new file mode 100644
index 000000000..7561b84ba
--- /dev/null
+++ b/engine/lib/upgrades/2011022000-1.8_svn-custom_profile_fields-390ac967b0bb5665.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Elgg 2011010401 upgrade 00
+ * custom_profile_fields
+ *
+ * Migrate 1.7 style custom profile fields to 1.8
+ */
+
+$plugin = elgg_get_plugin_from_id('profile');
+
+// plugin not installed
+if (!$plugin) {
+ return true;
+}
+
+$settings = $plugin->getAllSettings();
+// no fields to migrate
+if (!$settings['user_defined_fields']) {
+ return true;
+}
+
+$order = array();
+$remove_settings = array();
+
+// make sure we have a name and type
+foreach ($settings as $k => $v) {
+ if (!preg_match('/admin_defined_profile_([0-9]+)/i', $k, $matches)) {
+ continue;
+ }
+
+ $i = $matches[1];
+ $type_name = "admin_defined_profile_type_$i";
+ $type = elgg_extract($type_name, $settings, null);
+
+ if ($type) {
+ // field name
+ elgg_save_config($k, $v);
+ // field value
+ elgg_save_config($type_name, $type);
+
+ $order[] = $i;
+ $remove_settings[] = $k;
+ $remove_settings[] = $type_name;
+ }
+}
+
+if ($order) {
+ // these will always need to be in order, but there might be gaps
+ ksort($order);
+
+ $order_str = implode(',', $order);
+ elgg_save_config('profile_custom_fields', $order_str);
+
+ foreach ($remove_settings as $name) {
+ $plugin->unsetSetting($name);
+ }
+
+ $plugin->unsetSetting('user_defined_fields');
+} \ No newline at end of file
diff --git a/engine/lib/upgrades/2011030700-1.8_svn-blog_status_metadata-4645225d7b440876.php b/engine/lib/upgrades/2011030700-1.8_svn-blog_status_metadata-4645225d7b440876.php
new file mode 100644
index 000000000..fe2af9928
--- /dev/null
+++ b/engine/lib/upgrades/2011030700-1.8_svn-blog_status_metadata-4645225d7b440876.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Elgg 1.8-svn upgrade 2011030700
+ * blog_status_metadata
+ *
+ * Add a "status" metadata entry to every blog entity because in 1.8 you can have status = draft or
+ * status = published
+ */
+$ia = elgg_set_ignore_access(true);
+$options = array(
+ 'type' => 'object',
+ 'subtype' => 'blog',
+ 'limit' => 0,
+);
+$batch = new ElggBatch('elgg_get_entities', $options);
+
+foreach ($batch as $entity) {
+ if (!$entity->status) {
+ // create metadata owned by the original owner
+ create_metadata($entity->getGUID(), 'status', 'published', '', $entity->owner_guid,
+ $entity->access_id);
+ }
+}
+elgg_set_ignore_access($ia); \ No newline at end of file
diff --git a/engine/lib/upgrades/2011031300-1.8_svn-twitter_api-12b832a5a7a3e1bd.php b/engine/lib/upgrades/2011031300-1.8_svn-twitter_api-12b832a5a7a3e1bd.php
new file mode 100644
index 000000000..df60892a6
--- /dev/null
+++ b/engine/lib/upgrades/2011031300-1.8_svn-twitter_api-12b832a5a7a3e1bd.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Elgg 1.8-svn upgrade 2011031300
+ * twitter_api
+ *
+ * Updates the database for twitterservice to twitter_api changes.
+ */
+
+
+$ia = elgg_set_ignore_access(true);
+
+// make sure we have updated plugins
+elgg_generate_plugin_entities();
+
+$show_hidden = access_get_show_hidden_status();
+access_show_hidden_entities(true);
+
+$db_prefix = elgg_get_config('dbprefix');
+$site_guid = elgg_get_site_entity()->getGUID();
+$old = elgg_get_plugin_from_id('twitterservice');
+$new = elgg_get_plugin_from_id('twitter_api');
+$has_settings = false;
+
+// if not loaded, don't bother.
+if (!$old || !$new) {
+ return true;
+}
+
+$settings = array('consumer_key', 'consumer_secret', 'sign_on', 'new_users');
+
+foreach ($settings as $setting) {
+ $value = $old->getSetting($setting);
+ if ($value) {
+ $has_settings = true;
+ $new->setSetting($setting, $value);
+ }
+}
+
+// update the user settings
+$q = "UPDATE {$db_prefix}private_settings
+ SET name = replace(name, 'twitterservice', 'twitter_api')
+ WHERE name like '%twitterservice%'";
+
+update_data($q);
+
+// if there were settings, emit a notice to re-enable twitter_api
+if ($has_settings) {
+ elgg_add_admin_notice('twitter_api:disabled', elgg_echo('update:twitter_api:deactivated'));
+}
+
+$old->delete();
+
+access_show_hidden_entities($show_hidden);
+elgg_set_ignore_access($ia); \ No newline at end of file
diff --git a/engine/lib/upgrades/2011031600-1.8_svn-datalist_grows_up-0b8aec5a55cc1e1c.php b/engine/lib/upgrades/2011031600-1.8_svn-datalist_grows_up-0b8aec5a55cc1e1c.php
new file mode 100644
index 000000000..379244b36
--- /dev/null
+++ b/engine/lib/upgrades/2011031600-1.8_svn-datalist_grows_up-0b8aec5a55cc1e1c.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Elgg 1.8-svn upgrade 2011031600
+ * datalist_grows_up
+ *
+ * Ups the varchar to 256 for the datalist and config table.
+ *
+ * Keeping it as a varchar because of the trailing whitespace trimming it apparently does:
+ * http://dev.mysql.com/doc/refman/5.0/en/char.html
+ */
+
+$db_prefix = elgg_get_config('dbprefix');
+
+$q = "ALTER TABLE {$db_prefix}datalists CHANGE name name VARCHAR(255)";
+update_data($q);
+
+$q = "ALTER TABLE {$db_prefix}config CHANGE name name VARCHAR(255)";
+update_data($q);
diff --git a/engine/lib/upgrades/2011032000-1.8_svn-widgets_arent_plugins-61836261fa280a5c.php b/engine/lib/upgrades/2011032000-1.8_svn-widgets_arent_plugins-61836261fa280a5c.php
new file mode 100644
index 000000000..a20970d79
--- /dev/null
+++ b/engine/lib/upgrades/2011032000-1.8_svn-widgets_arent_plugins-61836261fa280a5c.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * Elgg 1.8-svn upgrade 2011031800
+ * widgets_arent_plugins
+ *
+ * At some point in Elgg's history subtype widget was registered with class ElggPlugin.
+ * Fix that.
+ */
+
+update_subtype('object', 'widget', 'ElggWidget');
diff --git a/engine/lib/upgrades/2011032200-1.8_svn-admins_like_widgets-7f19d2783c1680d3.php b/engine/lib/upgrades/2011032200-1.8_svn-admins_like_widgets-7f19d2783c1680d3.php
new file mode 100644
index 000000000..592adb403
--- /dev/null
+++ b/engine/lib/upgrades/2011032200-1.8_svn-admins_like_widgets-7f19d2783c1680d3.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * Elgg 1.8-svn upgrade 2011032200
+ * admins_like_widgets
+ *
+ * Give current admins widgets for those pre-1.8
+ */
+
+$admins = elgg_get_admins(array('limit' => 0));
+foreach ($admins as $admin) {
+ // call the admin handler for the make_admin event
+ elgg_add_admin_widgets('make_admin', 'user', $admin);
+}
diff --git a/engine/lib/upgrades/2011052801.php b/engine/lib/upgrades/2011052801.php
new file mode 100644
index 000000000..b5a8e1018
--- /dev/null
+++ b/engine/lib/upgrades/2011052801.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Make sure all users have the relationship member_of_site
+ */
+global $ENTITY_CACHE;
+$db_prefix = get_config('dbprefix');
+
+$limit = 100;
+
+$q = "SELECT e.* FROM {$db_prefix}entities e
+ WHERE e.type = 'user' AND e.guid NOT IN (
+ SELECT guid_one FROM {$db_prefix}entity_relationships
+ WHERE guid_two = 1 AND relationship = 'member_of_site'
+ )
+ LIMIT $limit";
+
+$users = get_data($q);
+
+while ($users) {
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
+
+ // do manually to not trigger any events because these aren't new users.
+ foreach ($users as $user) {
+ $rel_q = "INSERT INTO {$db_prefix}entity_relationships VALUES (
+ '',
+ '$user->guid',
+ 'member_of_site',
+ '$user->site_guid',
+ '$user->time_created'
+ )";
+
+ insert_data($rel_q);
+ }
+
+ // every time we run this query we've just reduced the rows it returns by $limit
+ // so don't pass an offset.
+ $q = "SELECT e.* FROM {$db_prefix}entities e
+ WHERE e.type = 'user' AND e.guid NOT IN (
+ SELECT guid_one FROM {$db_prefix}entity_relationships
+ WHERE guid_two = 1 AND relationship = 'member_of_site'
+ )
+ LIMIT $limit";
+
+ $users = get_data($q);
+} \ No newline at end of file
diff --git a/engine/lib/upgrades/2011061200-1.8b1-sites_need_a_site_guid-6d9dcbf46c0826cc.php b/engine/lib/upgrades/2011061200-1.8b1-sites_need_a_site_guid-6d9dcbf46c0826cc.php
new file mode 100644
index 000000000..41ab29998
--- /dev/null
+++ b/engine/lib/upgrades/2011061200-1.8b1-sites_need_a_site_guid-6d9dcbf46c0826cc.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Elgg 1.8b1 upgrade 2011061200
+ * sites_need_a_site_guid
+ *
+ * Sites did not have a site guid. This causes problems with getting
+ * metadata on site objects since we default to the current site.
+ */
+
+global $CONFIG;
+
+$ia = elgg_set_ignore_access(true);
+$access_status = access_get_show_hidden_status();
+access_show_hidden_entities(true);
+
+$options = array(
+ 'type' => 'site',
+ 'site_guid' => 0,
+ 'limit' => 0,
+);
+$batch = new ElggBatch('elgg_get_entities', $options);
+
+foreach ($batch as $entity) {
+ if (!$entity->site_guid) {
+ update_data("UPDATE {$CONFIG->dbprefix}entities SET site_guid=$entity->guid
+ WHERE guid=$entity->guid");
+ }
+}
+
+access_show_hidden_entities($access_status);
+elgg_set_ignore_access($ia);
diff --git a/engine/lib/upgrades/2011092500-1.8.0.1-forum_reply_river_view-5758ce8d86ac56ce.php b/engine/lib/upgrades/2011092500-1.8.0.1-forum_reply_river_view-5758ce8d86ac56ce.php
new file mode 100644
index 000000000..3a9200b51
--- /dev/null
+++ b/engine/lib/upgrades/2011092500-1.8.0.1-forum_reply_river_view-5758ce8d86ac56ce.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Elgg 1.8.0.1 upgrade 2011092500
+ * forum_reply_river_view
+ *
+ * The forum reply river view is in a new location in Elgg 1.8
+ */
+
+$query = "UPDATE {$CONFIG->dbprefix}river SET view='river/annotation/group_topic_post/reply',
+ action_type='reply'
+ WHERE view='river/forum/create' AND action_type='create'";
+update_data($query);
diff --git a/engine/lib/upgrades/2011123100-1.8.2-fix_friend_river-b17e7ff8345c2269.php b/engine/lib/upgrades/2011123100-1.8.2-fix_friend_river-b17e7ff8345c2269.php
new file mode 100644
index 000000000..4dc43cd32
--- /dev/null
+++ b/engine/lib/upgrades/2011123100-1.8.2-fix_friend_river-b17e7ff8345c2269.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Elgg 1.8.2 upgrade 2011123100
+ * fix_friend_river
+ *
+ * Action type was incorrect due to previoud friends river upgrade
+ */
+
+$query = "UPDATE {$CONFIG->dbprefix}river
+ SET action_type='friend'
+ WHERE view='river/relationship/friend/create' AND action_type='create'";
+update_data($query);
diff --git a/engine/lib/upgrades/2011123101-1.8.2-fix_blog_status-b14c2a0e7b9e7d55.php b/engine/lib/upgrades/2011123101-1.8.2-fix_blog_status-b14c2a0e7b9e7d55.php
new file mode 100644
index 000000000..e351c6ac9
--- /dev/null
+++ b/engine/lib/upgrades/2011123101-1.8.2-fix_blog_status-b14c2a0e7b9e7d55.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * Elgg 1.8.2 upgrade 2011123101
+ * fix_blog_status
+ *
+ * Most blog posts did not have their status properly set with 1.8 upgrade so we run
+ * the blog status upgrade again
+ */
+
+$ia = elgg_set_ignore_access(true);
+$options = array(
+ 'type' => 'object',
+ 'subtype' => 'blog',
+ 'limit' => 0,
+);
+$batch = new ElggBatch('elgg_get_entities', $options);
+
+foreach ($batch as $entity) {
+ if (!$entity->status) {
+ // create metadata owned by the original owner
+ create_metadata($entity->getGUID(), 'status', 'published', '', $entity->owner_guid,
+ $entity->access_id);
+ }
+}
+elgg_set_ignore_access($ia); \ No newline at end of file
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..780038c32
--- /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 https://github.com/elgg/elgg/issues/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/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php b/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php
new file mode 100644
index 000000000..8eccf05e2
--- /dev/null
+++ b/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Elgg 1.8.14 upgrade 2013030600
+ * update_user_location
+ *
+ * Before Elgg 1.8, a location like "London, England" would be stored as an array.
+ * This script turns that back into a string.
+ */
+
+$ia = elgg_set_ignore_access(true);
+$options = array(
+ 'type' => 'user',
+ 'limit' => 0,
+);
+$batch = new ElggBatch('elgg_get_entities', $options);
+
+foreach ($batch as $entity) {
+ _elgg_invalidate_query_cache();
+
+ if (is_array($entity->location)) {
+ $entity->location = implode(', ', $entity->location);
+ }
+}
+elgg_set_ignore_access($ia);
diff --git a/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php b/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php
new file mode 100644
index 000000000..ee99bdbc8
--- /dev/null
+++ b/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Elgg 1.8.15 upgrade 2013051700
+ * add_missing_group_index
+ *
+ * Some Elgg sites are missing the groups_entity full text index on name and
+ * description. This checks if it exists and adds it if it does not.
+ */
+
+$db_prefix = elgg_get_config('dbprefix');
+
+$full_text_index_exists = false;
+$results = get_data("SHOW INDEX FROM {$db_prefix}groups_entity");
+if ($results) {
+ foreach ($results as $result) {
+ if ($result->Index_type === 'FULLTEXT') {
+ $full_text_index_exists = true;
+ }
+ }
+}
+
+if ($full_text_index_exists == false) {
+ $query = "ALTER TABLE {$db_prefix}groups_entity
+ ADD FULLTEXT name_2 (name, description)";
+ if (!update_data($query)) {
+ elgg_log("Failed to add full text index to groups_entity table", 'ERROR');
+ }
+}
diff --git a/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php b/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php
new file mode 100644
index 000000000..d333a6cd2
--- /dev/null
+++ b/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Elgg 1.8.15 upgrade 2013052900
+ * ipv6_in_syslog
+ *
+ * Upgrade the ip column in system_log to be able to store ipv6 addresses
+ */
+
+$db_prefix = elgg_get_config('dbprefix');
+$q = "ALTER TABLE {$db_prefix}system_log MODIFY COLUMN ip_address varchar(46) NOT NULL";
+
+update_data($q); \ No newline at end of file
diff --git a/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php
new file mode 100644
index 000000000..538d74dd6
--- /dev/null
+++ b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Elgg 1.8.15 upgrade 2013060900
+ * site_secret
+ *
+ * Description
+ */
+
+$strength = _elgg_get_site_secret_strength();
+
+if ($strength !== 'strong') {
+ // a new key is needed immediately
+ register_translations(elgg_get_root_path() . 'languages/');
+
+ elgg_add_admin_notice('weak_site_key', elgg_echo("upgrade:site_secret_warning:$strength"));
+}
diff --git a/engine/lib/upgrades/create_upgrade.php b/engine/lib/upgrades/create_upgrade.php
new file mode 100644
index 000000000..b34f31b7e
--- /dev/null
+++ b/engine/lib/upgrades/create_upgrade.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Creates an upgrade file for Elgg.
+ *
+ * Run this from the command line:
+ * php create_upgrade.php upgrade_name
+ */
+
+error_reporting(E_NOTICE);
+
+// only allow from the command line.
+if (php_sapi_name() != 'cli') {
+ die('Upgrades can only be created from the command line.');
+}
+
+if (count($argv) < 2) {
+ elgg_create_upgrade_show_usage('No upgrade name.');
+}
+
+$name = $argv[1];
+
+if (strlen($name) > 24) {
+ elgg_create_upgrade_show_usage('Upgrade names cannot be longer than 24 characters.');
+}
+
+require_once '../../../version.php';
+require_once '../elgglib.php';
+$upgrade_path = dirname(__FILE__);
+
+$upgrade_name = strtolower($name);
+$upgrade_name = str_replace(array(' ', '-'), '_', $upgrade_name);
+$upgrade_release = str_replace(array(' ', '-'), '_', $release);
+$time = time();
+$upgrade_rnd = substr(md5($time), 0, 16);
+$upgrade_date = date('Ymd', $time);
+
+// determine the inc count
+$upgrade_inc = 0;
+$files = elgg_get_file_list($upgrade_path);
+sort($files);
+
+foreach ($files as $filename) {
+ $filename = basename($filename);
+ $date = (int)substr($filename, 0, 8);
+ $inc = (int)substr($filename, 8, 2);
+
+ if ($upgrade_date == $date) {
+ if ($inc >= $upgrade_inc) {
+ $upgrade_inc = $inc + 1;
+ }
+ }
+}
+
+// zero-pad
+// if there are more than 10 upgrades in a day, someone needs talking to.
+if ($upgrade_inc < 10) {
+ $upgrade_inc = "0$upgrade_inc";
+}
+
+$upgrade_version = $upgrade_date . $upgrade_inc;
+
+// make filename
+if (substr($release, 0, 3) == '1.7') {
+ // 1.7 upgrades are YYYYMMDDXX
+ $upgrade_name = $upgrade_version . '.php';
+} else {
+ // 1.8+ upgrades are YYYYMMDDXX-release-friendly_name-rnd
+ $upgrade_name = $upgrade_version . "-$upgrade_release-$name-$upgrade_rnd.php";
+}
+
+$upgrade_file = $upgrade_path . '/' . $upgrade_name;
+
+if (is_file($upgrade_file)) {
+ elgg_create_upgrade_show_usage("Upgrade file $upgrade_file already exists. This script has failed you.");
+}
+
+$upgrade_code = <<<___UPGRADE
+<?php
+/**
+ * Elgg $release upgrade $upgrade_version
+ * $name
+ *
+ * Description
+ */
+
+// upgrade code here.
+
+___UPGRADE;
+
+$h = fopen($upgrade_file, 'wb');
+
+if (!$h) {
+ die("Could not open file $upgrade_file");
+}
+
+if (!fwrite($h, $upgrade_code)) {
+ die("Could not write to $upgrade_file");
+} else {
+ elgg_set_version_dot_php_version($upgrade_version);
+ echo <<<___MSG
+
+Created upgrade file and updated version.php.
+
+Upgrade file: $upgrade_name
+Version: $upgrade_version
+
+___MSG;
+}
+
+fclose($h);
+
+
+function elgg_set_version_dot_php_version($version) {
+ $file = '../../../version.php';
+ $h = fopen($file, 'r+b');
+
+ if (!$h) {
+ return false;
+ }
+
+ $out = '';
+
+ while (($line = fgets($h)) !== false) {
+ $find = "/\\\$version[ ]?=[ ]?[0-9]{10};/";
+ $replace = "\$version = $version;";
+ $out .= preg_replace($find, $replace, $line);
+ }
+
+ rewind($h);
+
+ fwrite($h, $out);
+ fclose($h);
+ return true;
+}
+
+/**
+ * Shows the usage for the create_upgrade script and dies().
+ *
+ * @param string $msg Optional message to display
+ * @return void
+ */
+function elgg_create_upgrade_show_usage($msg = '') {
+ $text = <<<___MSG
+$msg
+
+Example:
+ php create_upgrade.php my_upgrade
+
+___MSG;
+
+ die($text);
+}
diff --git a/engine/lib/user_settings.php b/engine/lib/user_settings.php
new file mode 100644
index 000000000..0e36dc46d
--- /dev/null
+++ b/engine/lib/user_settings.php
@@ -0,0 +1,360 @@
+<?php
+/**
+ * Elgg user settings functions.
+ * Functions for adding and manipulating options on the user settings panel.
+ *
+ * @package Elgg.Core
+ * @subpackage Settings.User
+ */
+
+/**
+ * Saves user settings.
+ *
+ * @todo this assumes settings are coming in on a GET/POST request
+ *
+ * @note This is a handler for the 'usersettings:save', 'user' plugin hook
+ *
+ * @return void
+ * @access private
+ */
+function users_settings_save() {
+ elgg_set_user_language();
+ elgg_set_user_password();
+ elgg_set_user_default_access();
+ elgg_set_user_name();
+ elgg_set_user_email();
+}
+
+/**
+ * Set a user's password
+ *
+ * @return bool
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_set_user_password() {
+ $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) {
+ $user = elgg_get_logged_in_user_entity();
+ } else {
+ $user = get_entity($user_guid);
+ }
+
+ if ($user && $password) {
+ // let admin user change anyone's password without knowing it except his own.
+ if (!elgg_is_admin_logged_in() || elgg_is_admin_logged_in() && $user->guid == elgg_get_logged_in_user_guid()) {
+ $credentials = array(
+ 'username' => $user->username,
+ 'password' => $current_password
+ );
+
+ try {
+ pam_auth_userpass($credentials);
+ } catch (LoginException $e) {
+ register_error(elgg_echo('LoginException:ChangePasswordFailure'));
+ return false;
+ }
+ }
+
+ try {
+ $result = validate_password($password);
+ } catch (RegistrationException $e) {
+ register_error($e->getMessage());
+ return false;
+ }
+
+ if ($result) {
+ if ($password == $password2) {
+ $user->salt = generate_random_cleartext_password(); // Reset the salt
+ $user->password = generate_user_password($user, $password);
+ if ($user->save()) {
+ system_message(elgg_echo('user:password:success'));
+ return true;
+ } else {
+ register_error(elgg_echo('user:password:fail'));
+ }
+ } else {
+ register_error(elgg_echo('user:password:fail:notsame'));
+ }
+ } else {
+ register_error(elgg_echo('user:password:fail:tooshort'));
+ }
+ } else {
+ // no change
+ return null;
+ }
+
+ return false;
+}
+
+/**
+ * Set a user's display name
+ *
+ * @return bool
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_set_user_name() {
+ $name = strip_tags(get_input('name'));
+ $user_id = get_input('guid');
+
+ if (!$user_id) {
+ $user = elgg_get_logged_in_user_entity();
+ } else {
+ $user = get_entity($user_id);
+ }
+
+ if (elgg_strlen($name) > 50) {
+ register_error(elgg_echo('user:name:fail'));
+ return false;
+ }
+
+ if (($user) && ($user->canEdit()) && ($name)) {
+ if ($name != $user->name) {
+ $user->name = $name;
+ if ($user->save()) {
+ system_message(elgg_echo('user:name:success'));
+ return true;
+ } else {
+ register_error(elgg_echo('user:name:fail'));
+ }
+ } else {
+ // no change
+ return null;
+ }
+ } else {
+ register_error(elgg_echo('user:name:fail'));
+ }
+ return false;
+}
+
+/**
+ * Set a user's language
+ *
+ * @return bool
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_set_user_language() {
+ $language = get_input('language');
+ $user_id = get_input('guid');
+
+ if (!$user_id) {
+ $user = elgg_get_logged_in_user_entity();
+ } else {
+ $user = get_entity($user_id);
+ }
+
+ if (($user) && ($language)) {
+ if (strcmp($language, $user->language) != 0) {
+ $user->language = $language;
+ if ($user->save()) {
+ system_message(elgg_echo('user:language:success'));
+ return true;
+ } else {
+ register_error(elgg_echo('user:language:fail'));
+ }
+ } else {
+ // no change
+ return null;
+ }
+ } else {
+ register_error(elgg_echo('user:language:fail'));
+ }
+ return false;
+}
+
+/**
+ * Set a user's email address
+ *
+ * @return bool
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_set_user_email() {
+ $email = get_input('email');
+ $user_id = get_input('guid');
+
+ if (!$user_id) {
+ $user = elgg_get_logged_in_user_entity();
+ } else {
+ $user = get_entity($user_id);
+ }
+
+ if (!is_email_address($email)) {
+ register_error(elgg_echo('email:save:fail'));
+ return false;
+ }
+
+ if ($user) {
+ if (strcmp($email, $user->email) != 0) {
+ if (!get_user_by_email($email)) {
+ if ($user->email != $email) {
+
+ $user->email = $email;
+ if ($user->save()) {
+ system_message(elgg_echo('email:save:success'));
+ return true;
+ } else {
+ register_error(elgg_echo('email:save:fail'));
+ }
+ }
+ } else {
+ register_error(elgg_echo('registration:dupeemail'));
+ }
+ } else {
+ // no change
+ return null;
+ }
+ } else {
+ register_error(elgg_echo('email:save:fail'));
+ }
+ return false;
+}
+
+/**
+ * Set a user's default access level
+ *
+ * @return bool
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_set_user_default_access() {
+
+ if (!elgg_get_config('allow_user_default_access')) {
+ return false;
+ }
+
+ $default_access = get_input('default_access');
+ $user_id = get_input('guid');
+
+ if (!$user_id) {
+ $user = elgg_get_logged_in_user_entity();
+ } else {
+ $user = get_entity($user_id);
+ }
+
+ if ($user) {
+ $current_default_access = $user->getPrivateSetting('elgg_default_access');
+ if ($default_access !== $current_default_access) {
+ if ($user->setPrivateSetting('elgg_default_access', $default_access)) {
+ system_message(elgg_echo('user:default_access:success'));
+ return true;
+ } else {
+ register_error(elgg_echo('user:default_access:fail'));
+ }
+ } else {
+ // no change
+ return null;
+ }
+ } else {
+ register_error(elgg_echo('user:default_access:fail'));
+ }
+
+ return false;
+}
+
+/**
+ * Set up the menu for user settings
+ *
+ * @return void
+ * @access private
+ */
+function usersettings_pagesetup() {
+ $user = elgg_get_page_owner_entity();
+
+ if ($user && elgg_get_context() == "settings") {
+ $params = array(
+ 'name' => '1_account',
+ 'text' => elgg_echo('usersettings:user:opt:linktext'),
+ 'href' => "settings/user/{$user->username}",
+ );
+ elgg_register_menu_item('page', $params);
+ $params = array(
+ 'name' => '1_plugins',
+ 'text' => elgg_echo('usersettings:plugins:opt:linktext'),
+ 'href' => "settings/plugins/{$user->username}",
+ );
+ elgg_register_menu_item('page', $params);
+ $params = array(
+ 'name' => '1_statistics',
+ 'text' => elgg_echo('usersettings:statistics:opt:linktext'),
+ 'href' => "settings/statistics/{$user->username}",
+ );
+ elgg_register_menu_item('page', $params);
+ }
+}
+
+/**
+ * Page handler for user settings
+ *
+ * @param array $page Pages array
+ *
+ * @return bool
+ * @access private
+ */
+function usersettings_page_handler($page) {
+ global $CONFIG;
+
+ if (!isset($page[0])) {
+ $page[0] = 'user';
+ }
+
+ if (isset($page[1])) {
+ $user = get_user_by_username($page[1]);
+ elgg_set_page_owner_guid($user->guid);
+ } else {
+ $user = elgg_get_logged_in_user_entity();
+ elgg_set_page_owner_guid($user->guid);
+ }
+
+ elgg_push_breadcrumb(elgg_echo('settings'), "settings/user/$user->username");
+
+ switch ($page[0]) {
+ case 'statistics':
+ elgg_push_breadcrumb(elgg_echo('usersettings:statistics:opt:linktext'));
+ $path = $CONFIG->path . "pages/settings/statistics.php";
+ break;
+ case 'plugins':
+ elgg_push_breadcrumb(elgg_echo('usersettings:plugins:opt:linktext'));
+ $path = $CONFIG->path . "pages/settings/tools.php";
+ break;
+ case 'user':
+ $path = $CONFIG->path . "pages/settings/account.php";
+ break;
+ }
+
+ if (isset($path)) {
+ require $path;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Initialize the user settings library
+ *
+ * @return void
+ * @access private
+ */
+function usersettings_init() {
+ elgg_register_page_handler('settings', 'usersettings_page_handler');
+
+ elgg_register_plugin_hook_handler('usersettings:save', 'user', 'users_settings_save');
+
+ elgg_register_action("usersettings/save");
+
+ // extend the account settings form
+ elgg_extend_view('forms/account/settings', 'core/settings/account/name', 100);
+ elgg_extend_view('forms/account/settings', 'core/settings/account/password', 100);
+ elgg_extend_view('forms/account/settings', 'core/settings/account/email', 100);
+ elgg_extend_view('forms/account/settings', 'core/settings/account/language', 100);
+ elgg_extend_view('forms/account/settings', 'core/settings/account/default_access', 100);
+}
+
+elgg_register_event_handler('init', 'system', 'usersettings_init');
+elgg_register_event_handler('pagesetup', 'system', 'usersettings_pagesetup');
diff --git a/engine/lib/users.php b/engine/lib/users.php
index 99ef15246..a8fb9121c 100644
--- a/engine/lib/users.php
+++ b/engine/lib/users.php
@@ -3,449 +3,25 @@
* Elgg users
* Functions to manage multiple or single users in an Elgg install
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage DataModel.User
*/
/// Map a username to a cached GUID
+global $USERNAME_TO_GUID_MAP_CACHE;
$USERNAME_TO_GUID_MAP_CACHE = array();
/// Map a user code to a cached GUID
+global $CODE_TO_GUID_MAP_CACHE;
$CODE_TO_GUID_MAP_CACHE = array();
/**
- * ElggUser
- *
- * Representation of a "user" in the system.
- *
- * @package Elgg
- * @subpackage Core
- */
-class ElggUser extends ElggEntity
- implements Friendable {
- /**
- * Initialise the attributes array.
- * This is vital to distinguish between metadata and base parameters.
- *
- * Place your base parameters here.
- */
- protected function initialise_attributes() {
- parent::initialise_attributes();
-
- $this->attributes['type'] = "user";
- $this->attributes['name'] = "";
- $this->attributes['username'] = "";
- $this->attributes['password'] = "";
- $this->attributes['salt'] = "";
- $this->attributes['email'] = "";
- $this->attributes['language'] = "";
- $this->attributes['code'] = "";
- $this->attributes['banned'] = "no";
- $this->attributes['admin'] = 'no';
- $this->attributes['tables_split'] = 2;
- }
-
- /**
- * Construct a new user entity, optionally from a given id value.
- *
- * @param mixed $guid If an int, load that GUID.
- * If a db row then will attempt to load the rest of the data.
- * @throws Exception if there was a problem creating the user.
- */
- function __construct($guid = null) {
- $this->initialise_attributes();
-
- if (!empty($guid)) {
- // Is $guid is a DB row - either a entity row, or a user table row.
- if ($guid instanceof stdClass) {
- // Load the rest
- if (!$this->load($guid->guid)) {
- throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid->guid));
- }
- }
-
- // See if this is a username
- else if (is_string($guid)) {
- $guid = get_user_by_username($guid);
- foreach ($guid->attributes as $key => $value) {
- $this->attributes[$key] = $value;
- }
- }
-
- // Is $guid is an ElggUser? Use a copy constructor
- else if ($guid instanceof ElggUser) {
- elgg_deprecated_notice('This type of usage of the ElggUser constructor was deprecated. Please use the clone method.', 1.7);
-
- foreach ($guid->attributes as $key => $value) {
- $this->attributes[$key] = $value;
- }
- }
-
- // Is this is an ElggEntity but not an ElggUser = ERROR!
- else if ($guid instanceof ElggEntity) {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggUser'));
- }
-
- // We assume if we have got this far, $guid is an int
- else if (is_numeric($guid)) {
- if (!$this->load($guid)) {
- throw new IOException(sprintf(elgg_echo('IOException:FailedToLoadGUID'), get_class(), $guid));
- }
- }
-
- else {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:UnrecognisedValue'));
- }
- }
- }
-
- /**
- * Override the load function.
- * This function will ensure that all data is loaded (were possible), so
- * if only part of the ElggUser is loaded, it'll load the rest.
- *
- * @param int $guid
- * @return true|false
- */
- protected function load($guid) {
- // Test to see if we have the generic stuff
- if (!parent::load($guid)) {
- return false;
- }
-
- // Check the type
- if ($this->attributes['type']!='user') {
- throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, get_class()));
- }
-
- // Load missing data
- $row = get_user_entity_as_row($guid);
- if (($row) && (!$this->isFullyLoaded())) {
- // If $row isn't a cached copy then increment the counter
- $this->attributes['tables_loaded'] ++;
- }
-
- // Now put these into the attributes array as core values
- $objarray = (array) $row;
- foreach($objarray as $key => $value) {
- $this->attributes[$key] = $value;
- }
-
- return true;
- }
-
- /**
- * Saves this user to the database.
- * @return true|false
- */
- public function save() {
- // Save generic stuff
- if (!parent::save()) {
- return false;
- }
-
- // Now save specific stuff
- return create_user_entity($this->get('guid'), $this->get('name'), $this->get('username'), $this->get('password'), $this->get('salt'), $this->get('email'), $this->get('language'), $this->get('code'));
- }
-
- /**
- * User specific override of the entity delete method.
- *
- * @return bool
- */
- public function delete() {
- global $USERNAME_TO_GUID_MAP_CACHE, $CODE_TO_GUID_MAP_CACHE;
-
- // clear cache
- if (isset($USERNAME_TO_GUID_MAP_CACHE[$this->username])) {
- unset($USERNAME_TO_GUID_MAP_CACHE[$this->username]);
- }
- if (isset($CODE_TO_GUID_MAP_CACHE[$this->code])) {
- unset($CODE_TO_GUID_MAP_CACHE[$this->code]);
- }
-
- // Delete owned data
- clear_annotations_by_owner($this->guid);
- clear_metadata_by_owner($this->guid);
- clear_user_files($this);
-
- // Delete entity
- return parent::delete();
- }
-
- /**
- * Ban this user.
- *
- * @param string $reason Optional reason
- */
- public function ban($reason = "") {
- return ban_user($this->guid, $reason);
- }
-
- /**
- * Unban this user.
- */
- public function unban() {
- return unban_user($this->guid);
- }
-
- /**
- * Is this user banned or not?
- *
- * @return bool
- */
- public function isBanned() {
- return $this->banned == 'yes';
- }
-
- /**
- * Is this user admin?
- *
- * @return bool
- */
- public function isAdmin() {
-
- // for backward compatibility we need to pull this directly
- // from the attributes instead of using the magic methods.
- // this can be removed in 1.9
- // return $this->admin == 'yes';
- return $this->attributes['admin'] == 'yes';
- }
-
- /**
- * Make the user an admin
- *
- * @return bool
- */
- public function makeAdmin() {
- if (make_user_admin($this->guid)) {
- $this->attributes['admin'] = 'yes';
- return TRUE;
- }
- return FALSE;
- }
-
- /**
- * Remove the admin flag for user
- *
- * @return bool
- */
- public function removeAdmin() {
- if (remove_user_admin($this->guid)) {
- $this->attributes['admin'] = 'no';
- return TRUE;
- }
- return FALSE;
- }
-
- /**
- * Get sites that this user is a member of
- *
- * @param string $subtype Optionally, the subtype of result we want to limit to
- * @param int $limit The number of results to return
- * @param int $offset Any indexing offset
- */
- function getSites($subtype="", $limit = 10, $offset = 0) {
- // return get_site_users($this->getGUID(), $subtype, $limit, $offset);
- return get_user_sites($this->getGUID(), $subtype, $limit, $offset);
- }
-
- /**
- * Add this user to a particular site
- *
- * @param int $site_guid The guid of the site to add it to
- * @return true|false
- */
- function addToSite($site_guid) {
- // return add_site_user($this->getGUID(), $site_guid);
- return add_site_user($site_guid, $this->getGUID());
- }
-
- /**
- * Remove this user from a particular site
- *
- * @param int $site_guid The guid of the site to remove it from
- * @return true|false
- */
- function removeFromSite($site_guid) {
- //return remove_site_user($this->getGUID(), $site_guid);
- return remove_site_user($site_guid, $this->getGUID());
- }
-
- /**
- * Adds a user to this user's friends list
- *
- * @param int $friend_guid The GUID of the user to add
- * @return true|false Depending on success
- */
- function addFriend($friend_guid) {
- return user_add_friend($this->getGUID(), $friend_guid);
- }
-
- /**
- * Removes a user from this user's friends list
- *
- * @param int $friend_guid The GUID of the user to remove
- * @return true|false Depending on success
- */
- function removeFriend($friend_guid) {
- return user_remove_friend($this->getGUID(), $friend_guid);
- }
-
- /**
- * Determines whether or not this user is a friend of the currently logged in user
- *
- * @return true|false
- */
- function isFriend() {
- return user_is_friend(get_loggedin_userid(), $this->getGUID());
- }
-
- /**
- * Determines whether this user is friends with another user
- *
- * @param int $user_guid The GUID of the user to check is on this user's friends list
- * @return true|false
- */
- function isFriendsWith($user_guid) {
- return user_is_friend($this->getGUID(), $user_guid);
- }
-
- /**
- * Determines whether or not this user is on another user's friends list
- *
- * @param int $user_guid The GUID of the user to check against
- * @return true|false
- */
- function isFriendOf($user_guid) {
- return user_is_friend($user_guid, $this->getGUID());
- }
-
- /**
- * Retrieves a list of this user's friends
- *
- * @param string $subtype Optionally, the subtype of user to filter to (leave blank for all)
- * @param int $limit The number of users to retrieve
- * @param int $offset Indexing offset, if any
- * @return array|false Array of ElggUsers, or false, depending on success
- */
- function getFriends($subtype = "", $limit = 10, $offset = 0) {
- return get_user_friends($this->getGUID(), $subtype, $limit, $offset);
- }
-
- /**
- * Retrieves a list of people who have made this user a friend
- *
- * @param string $subtype Optionally, the subtype of user to filter to (leave blank for all)
- * @param int $limit The number of users to retrieve
- * @param int $offset Indexing offset, if any
- * @return array|false Array of ElggUsers, or false, depending on success
- */
- function getFriendsOf($subtype = "", $limit = 10, $offset = 0) {
- return get_user_friends_of($this->getGUID(), $subtype, $limit, $offset);
- }
-
- /**
- * Get an array of ElggObjects owned by this user.
- *
- * @param string $subtype The subtype of the objects, if any
- * @param int $limit Number of results to return
- * @param int $offset Any indexing offset
- */
- public function getObjects($subtype="", $limit = 10, $offset = 0) {
- return get_user_objects($this->getGUID(), $subtype, $limit, $offset);
- }
-
- /**
- * Get an array of ElggObjects owned by this user's friends.
- *
- * @param string $subtype The subtype of the objects, if any
- * @param int $limit Number of results to return
- * @param int $offset Any indexing offset
- */
- public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) {
- return get_user_friends_objects($this->getGUID(), $subtype, $limit, $offset);
- }
-
- /**
- * Counts the number of ElggObjects owned by this user
- *
- * @param string $subtype The subtypes of the objects, if any
- * @return int The number of ElggObjects
- */
- public function countObjects($subtype = "") {
- return count_user_objects($this->getGUID(), $subtype);
- }
-
- /**
- * Get the collections associated with a user.
- *
- * @param string $subtype Optionally, the subtype of result we want to limit to
- * @param int $limit The number of results to return
- * @param int $offset Any indexing offset
- * @return unknown
- */
- public function getCollections($subtype="", $limit = 10, $offset = 0) {
- return get_user_collections($this->getGUID(), $subtype, $limit, $offset);
- }
-
- /**
- * If a user's owner is blank, return its own GUID as the owner
- *
- * @return int User GUID
- */
- function getOwner() {
- if ($this->owner_guid == 0) {
- return $this->getGUID();
- }
-
- return $this->owner_guid;
- }
-
- // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
-
- /**
- * Return an array of fields which can be exported.
- */
- public function getExportableValues() {
- return array_merge(parent::getExportableValues(), array(
- 'name',
- 'username',
- 'language',
- ));
- }
-
- // backward compatibility with admin flag
- // remove for 1.9
- public function __set($name, $value) {
- if ($name == 'admin' || $name == 'siteadmin') {
- elgg_deprecated_notice('The admin/siteadmin metadata are not longer used. Use ElggUser->makeAdmin() and ElggUser->removeAdmin().', '1.7.1');
-
- if ($value == 'yes' || $value == '1') {
- $this->makeAdmin();
- } else {
- $this->removeAdmin();
- }
- }
- return parent::__set($name, $value);
- }
-
- public function __get($name) {
- if ($name == 'admin' || $name == 'siteadmin') {
- elgg_deprecated_notice('The admin/siteadmin metadata are not longer used. Use ElggUser->isAdmin().', '1.7.1');
- return $this->isAdmin();
- }
-
- return parent::__get($name);
- }
-}
-
-/**
* Return the user specific details of a user by a row.
*
- * @param int $guid
+ * @param int $guid The ElggUser guid
+ *
+ * @return mixed
+ * @access private
*/
function get_user_entity_as_row($guid) {
global $CONFIG;
@@ -455,13 +31,20 @@ function get_user_entity_as_row($guid) {
}
/**
- * Create or update the extras table for a given user.
+ * Create or update the entities table for a given user.
* Call create_entity first.
*
- * @param int $guid
- * @param string $name
- * @param string $description
- * @param string $url
+ * @param int $guid The user's GUID
+ * @param string $name The user's display name
+ * @param string $username The username
+ * @param string $password The password
+ * @param string $salt A salt for the password
+ * @param string $email The user's email address
+ * @param string $language The user's default language
+ * @param string $code A code
+ *
+ * @return bool
+ * @access private
*/
function create_user_entity($guid, $name, $username, $password, $salt, $email, $language, $code) {
global $CONFIG;
@@ -478,27 +61,36 @@ function create_user_entity($guid, $name, $username, $password, $salt, $email, $
$row = get_entity_as_row($guid);
if ($row) {
// Exists and you have access to it
-
- if ($exists = get_data_row("SELECT guid from {$CONFIG->dbprefix}users_entity where guid = {$guid}")) {
- $result = update_data("UPDATE {$CONFIG->dbprefix}users_entity set name='$name', username='$username', password='$password', salt='$salt', email='$email', language='$language', code='$code', last_action = ". time() ." where guid = {$guid}");
+ $query = "SELECT guid from {$CONFIG->dbprefix}users_entity where guid = {$guid}";
+ if ($exists = get_data_row($query)) {
+ $query = "UPDATE {$CONFIG->dbprefix}users_entity
+ SET name='$name', username='$username', password='$password', salt='$salt',
+ email='$email', language='$language', code='$code'
+ WHERE guid = $guid";
+
+ $result = update_data($query);
if ($result != false) {
// Update succeeded, continue
$entity = get_entity($guid);
- if (trigger_elgg_event('update',$entity->type,$entity)) {
+ if (elgg_trigger_event('update', $entity->type, $entity)) {
return $guid;
} else {
$entity->delete();
}
}
} else {
- // Update failed, attempt an insert.
- $result = insert_data("INSERT into {$CONFIG->dbprefix}users_entity (guid, name, username, password, salt, email, language, code) values ($guid, '$name', '$username', '$password', '$salt', '$email', '$language', '$code')");
- if ($result!==false) {
+ // Exists query failed, attempt an insert.
+ $query = "INSERT into {$CONFIG->dbprefix}users_entity
+ (guid, name, username, password, salt, email, language, code)
+ values ($guid, '$name', '$username', '$password', '$salt', '$email', '$language', '$code')";
+
+ $result = insert_data($query);
+ if ($result !== false) {
$entity = get_entity($guid);
- if (trigger_elgg_event('create',$entity->type,$entity)) {
+ if (elgg_trigger_event('create', $entity->type, $entity)) {
return $guid;
} else {
- $entity->delete(); //delete_entity($guid);
+ $entity->delete();
}
}
}
@@ -511,15 +103,20 @@ function create_user_entity($guid, $name, $username, $password, $salt, $email, $
* Disables all of a user's entities
*
* @param int $owner_guid The owner GUID
- * @return true|false Depending on success
+ *
+ * @return bool Depending on success
*/
function disable_user_entities($owner_guid) {
global $CONFIG;
$owner_guid = (int) $owner_guid;
if ($entity = get_entity($owner_guid)) {
- if (trigger_elgg_event('disable',$entity->type,$entity)) {
+ if (elgg_trigger_event('disable', $entity->type, $entity)) {
if ($entity->canEdit()) {
- $res = update_data("UPDATE {$CONFIG->dbprefix}entities set enabled='no' where owner_guid={$owner_guid} or container_guid = {$owner_guid}");
+ $query = "UPDATE {$CONFIG->dbprefix}entities
+ set enabled='no' where owner_guid={$owner_guid}
+ or container_guid = {$owner_guid}";
+
+ $res = update_data($query);
return $res;
}
}
@@ -531,22 +128,23 @@ function disable_user_entities($owner_guid) {
/**
* Ban a user
*
- * @param int $user_guid The user guid
- * @param string $reason A reason
+ * @param int $user_guid The user guid
+ * @param string $reason A reason
+ *
+ * @return bool
*/
function ban_user($user_guid, $reason = "") {
global $CONFIG;
$user_guid = (int)$user_guid;
- $reason = sanitise_string($reason);
$user = get_entity($user_guid);
if (($user) && ($user->canEdit()) && ($user instanceof ElggUser)) {
- if (trigger_elgg_event('ban', 'user', $user)) {
+ if (elgg_trigger_event('ban', 'user', $user)) {
// Add reason
if ($reason) {
- create_metadata($user_guid, 'ban_reason', $reason,'', 0, ACCESS_PUBLIC);
+ create_metadata($user_guid, 'ban_reason', $reason, '', 0, ACCESS_PUBLIC);
}
// clear "remember me" cookie code so user cannot login in using it
@@ -564,7 +162,8 @@ function ban_user($user_guid, $reason = "") {
}
// Set ban flag
- return update_data("UPDATE {$CONFIG->dbprefix}users_entity set banned='yes' where guid=$user_guid");
+ $query = "UPDATE {$CONFIG->dbprefix}users_entity set banned='yes' where guid=$user_guid";
+ return update_data($query);
}
return FALSE;
@@ -577,6 +176,8 @@ function ban_user($user_guid, $reason = "") {
* Unban a user.
*
* @param int $user_guid Unban a user.
+ *
+ * @return bool
*/
function unban_user($user_guid) {
global $CONFIG;
@@ -586,8 +187,8 @@ function unban_user($user_guid) {
$user = get_entity($user_guid);
if (($user) && ($user->canEdit()) && ($user instanceof ElggUser)) {
- if (trigger_elgg_event('unban', 'user', $user)) {
- create_metadata($user_guid, 'ban_reason', '','', 0, ACCESS_PUBLIC);
+ if (elgg_trigger_event('unban', 'user', $user)) {
+ create_metadata($user_guid, 'ban_reason', '', '', 0, ACCESS_PUBLIC);
// invalidate memcache for this user
static $newentity_cache;
@@ -599,7 +200,9 @@ function unban_user($user_guid) {
$newentity_cache->delete($user_guid);
}
- return update_data("UPDATE {$CONFIG->dbprefix}users_entity set banned='no' where guid=$user_guid");
+
+ $query = "UPDATE {$CONFIG->dbprefix}users_entity set banned='no' where guid=$user_guid";
+ return update_data($query);
}
return FALSE;
@@ -611,7 +214,8 @@ function unban_user($user_guid) {
/**
* Makes user $guid an admin.
*
- * @param int $guid
+ * @param int $user_guid User guid
+ *
* @return bool
*/
function make_user_admin($user_guid) {
@@ -620,7 +224,7 @@ function make_user_admin($user_guid) {
$user = get_entity((int)$user_guid);
if (($user) && ($user instanceof ElggUser) && ($user->canEdit())) {
- if (trigger_elgg_event('make_admin', 'user', $user)) {
+ if (elgg_trigger_event('make_admin', 'user', $user)) {
// invalidate memcache for this user
static $newentity_cache;
@@ -633,7 +237,7 @@ function make_user_admin($user_guid) {
}
$r = update_data("UPDATE {$CONFIG->dbprefix}users_entity set admin='yes' where guid=$user_guid");
- invalidate_cache_for_entity($user_guid);
+ _elgg_invalidate_cache_for_entity($user_guid);
return $r;
}
@@ -646,7 +250,8 @@ function make_user_admin($user_guid) {
/**
* Removes user $guid's admin flag.
*
- * @param int $guid
+ * @param int $user_guid User GUID
+ *
* @return bool
*/
function remove_user_admin($user_guid) {
@@ -655,7 +260,7 @@ function remove_user_admin($user_guid) {
$user = get_entity((int)$user_guid);
if (($user) && ($user instanceof ElggUser) && ($user->canEdit())) {
- if (trigger_elgg_event('remove_admin', 'user', $user)) {
+ if (elgg_trigger_event('remove_admin', 'user', $user)) {
// invalidate memcache for this user
static $newentity_cache;
@@ -668,7 +273,7 @@ function remove_user_admin($user_guid) {
}
$r = update_data("UPDATE {$CONFIG->dbprefix}users_entity set admin='no' where guid=$user_guid");
- invalidate_cache_for_entity($user_guid);
+ _elgg_invalidate_cache_for_entity($user_guid);
return $r;
}
@@ -679,25 +284,13 @@ function remove_user_admin($user_guid) {
}
/**
- * THIS FUNCTION IS DEPRECATED.
- *
- * Delete a user's extra data.
- *
- * @param int $guid
- */
-function delete_user_entity($guid) {
- system_message(sprintf(elgg_echo('deprecatedfunction'), 'delete_user_entity'));
-
- return 1; // Always return that we have deleted one row in order to not break existing code.
-}
-
-/**
* Get the sites this user is part of
*
* @param int $user_guid The user's GUID
- * @param int $limit Number of results to return
- * @param int $offset Any indexing offset
- * @return false|array On success, an array of ElggSites
+ * @param int $limit Number of results to return
+ * @param int $offset Any indexing offset
+ *
+ * @return ElggSite[]|false On success, an array of ElggSites
*/
function get_user_sites($user_guid, $limit = 10, $offset = 0) {
$user_guid = (int)$user_guid;
@@ -705,21 +298,23 @@ function get_user_sites($user_guid, $limit = 10, $offset = 0) {
$offset = (int)$offset;
return elgg_get_entities_from_relationship(array(
+ 'site_guids' => ELGG_ENTITIES_ANY_VALUE,
'relationship' => 'member_of_site',
'relationship_guid' => $user_guid,
'inverse_relationship' => FALSE,
- 'types' => 'site',
+ 'type' => 'site',
'limit' => $limit,
- 'offset' => $offset)
- );
+ 'offset' => $offset,
+ ));
}
/**
* Adds a user to another user's friends list.
*
- * @param int $user_guid The GUID of the friending user
+ * @param int $user_guid The GUID of the friending user
* @param int $friend_guid The GUID of the user to friend
- * @return true|false Depending on success
+ *
+ * @return bool Depending on success
*/
function user_add_friend($user_guid, $friend_guid) {
$user_guid = (int) $user_guid;
@@ -742,13 +337,12 @@ function user_add_friend($user_guid, $friend_guid) {
/**
* Removes a user from another user's friends list.
*
- * @param int $user_guid The GUID of the friending user
+ * @param int $user_guid The GUID of the friending user
* @param int $friend_guid The GUID of the user on the friends list
- * @return true|false Depending on success
+ *
+ * @return bool Depending on success
*/
function user_remove_friend($user_guid, $friend_guid) {
- global $CONFIG;
-
$user_guid = (int) $user_guid;
$friend_guid = (int) $friend_guid;
@@ -766,29 +360,33 @@ function user_remove_friend($user_guid, $friend_guid) {
/**
* Determines whether or not a user is another user's friend.
*
- * @param int $user_guid The GUID of the user
+ * @param int $user_guid The GUID of the user
* @param int $friend_guid The GUID of the friend
- * @return true|false
+ *
+ * @return bool
*/
function user_is_friend($user_guid, $friend_guid) {
- return check_entity_relationship($user_guid, "friend", $friend_guid);
+ return check_entity_relationship($user_guid, "friend", $friend_guid) !== false;
}
/**
* Obtains a given user's friends
*
- * @param int $user_guid The user's GUID
- * @param string $subtype The subtype of users, if any
- * @param int $limit Number of results to return (default 10)
- * @param int $offset Indexing offset, if any
- * @return false|array Either an array of ElggUsers or false, depending on success
+ * @param int $user_guid The user's GUID
+ * @param string $subtype The subtype of users, if any
+ * @param int $limit Number of results to return (default 10)
+ * @param int $offset Indexing offset, if any
+ *
+ * @return ElggUser[]|false Either an array of ElggUsers or false, depending on success
*/
-function get_user_friends($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $offset = 0) {
+function get_user_friends($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10,
+$offset = 0) {
+
return elgg_get_entities_from_relationship(array(
'relationship' => 'friend',
'relationship_guid' => $user_guid,
- 'types' => 'user',
- 'subtypes' => $subtype,
+ 'type' => 'user',
+ 'subtype' => $subtype,
'limit' => $limit,
'offset' => $offset
));
@@ -797,110 +395,45 @@ function get_user_friends($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit
/**
* Obtains the people who have made a given user a friend
*
- * @param int $user_guid The user's GUID
- * @param string $subtype The subtype of users, if any
- * @param int $limit Number of results to return (default 10)
- * @param int $offset Indexing offset, if any
- * @return false|array Either an array of ElggUsers or false, depending on success
+ * @param int $user_guid The user's GUID
+ * @param string $subtype The subtype of users, if any
+ * @param int $limit Number of results to return (default 10)
+ * @param int $offset Indexing offset, if any
+ *
+ * @return ElggUser[]|false Either an array of ElggUsers or false, depending on success
*/
-function get_user_friends_of($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $offset = 0) {
+function get_user_friends_of($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10,
+$offset = 0) {
+
return elgg_get_entities_from_relationship(array(
'relationship' => 'friend',
'relationship_guid' => $user_guid,
'inverse_relationship' => TRUE,
- 'types' => 'user',
- 'subtypes' => $subtype,
- 'limit' => $limit,
- 'offset' => $offset
- ));
-}
-
-/**
- * Obtains a list of objects owned by a user
- *
- * @param int $user_guid The GUID of the owning user
- * @param string $subtype Optionally, the subtype of objects
- * @param int $limit The number of results to return (default 10)
- * @param int $offset Indexing offset, if any
- * @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 false|array An array of ElggObjects or false, depending on success
- */
-function get_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $offset = 0, $timelower = 0, $timeupper = 0) {
- $ntt = elgg_get_entities(array(
- 'type' => 'object',
+ 'type' => 'user',
'subtype' => $subtype,
- 'owner_guid' => $user_guid,
'limit' => $limit,
- 'offset' => $offset,
- 'container_guid' => $user_guid,
- 'created_time_lower' => $timelower,
- 'created_time_upper' => $timeupper
- ));
- return $ntt;
-}
-
-/**
- * Counts the objects (optionally of a particular subtype) owned by a user
- *
- * @param int $user_guid The GUID of the owning user
- * @param string $subtype Optionally, the subtype of objects
- * @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 int The number of objects the user owns (of this subtype)
- */
-function count_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $timelower = 0, $timeupper = 0) {
- $total = elgg_get_entities(array(
- 'type' => 'object',
- 'subtype' => $subtype,
- 'owner_guid' => $user_guid,
- 'count' => TRUE,
- 'container_guid' => $user_guid,
- 'created_time_lower' => $timelower,
- 'created_time_upper' => $timeupper
+ 'offset' => $offset
));
- return $total;
}
/**
- * Displays a list of user objects of a particular subtype, with navigation.
+ * Obtains a list of objects owned by a user's friends
*
- * @see elgg_view_entity_list
+ * @param int $user_guid The GUID of the user to get the friends of
+ * @param string $subtype Optionally, the subtype of objects
+ * @param int $limit The number of results to return (default 10)
+ * @param int $offset Indexing offset, if any
+ * @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
*
- * @param int $user_guid The GUID of the user
- * @param string $subtype The object subtype
- * @param int $limit The number of entities to display on a page
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow gallery view (default: true)
- * @param true|false $pagination Whether to display pagination (default: true)
- * @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 ElggObject[]|false An array of ElggObjects or false, depending on success
*/
-function list_user_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $fullview = true, $viewtypetoggle = true, $pagination = true, $timelower = 0, $timeupper = 0) {
- $offset = (int) get_input('offset');
- $limit = (int) $limit;
- $count = (int) count_user_objects($user_guid, $subtype,$timelower,$timeupper);
- $entities = get_user_objects($user_guid, $subtype, $limit, $offset, $timelower, $timeupper);
+function get_user_friends_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10,
+$offset = 0, $timelower = 0, $timeupper = 0) {
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
-}
-
-/**
- * Obtains a list of objects owned by a user's friends
- *
- * @param int $user_guid The GUID of the user to get the friends of
- * @param string $subtype Optionally, the subtype of objects
- * @param int $limit The number of results to return (default 10)
- * @param int $offset Indexing offset, if any
- * @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 false|array An array of ElggObjects or false, depending on success
- */
-function get_user_friends_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $offset = 0, $timelower = 0, $timeupper = 0) {
if ($friends = get_user_friends($user_guid, "", 999999, 0)) {
$friendguids = array();
- foreach($friends as $friend) {
+ foreach ($friends as $friend) {
$friendguids[] = $friend->getGUID();
}
return elgg_get_entities(array(
@@ -920,16 +453,19 @@ function get_user_friends_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE
/**
* Counts the number of objects owned by a user's friends
*
- * @param int $user_guid The GUID of the user to get the friends of
- * @param string $subtype Optionally, the subtype of objects
- * @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
+ * @param int $user_guid The GUID of the user to get the friends of
+ * @param string $subtype Optionally, the subtype of objects
+ * @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 int The number of objects
*/
-function count_user_friends_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $timelower = 0, $timeupper = 0) {
+function count_user_friends_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE,
+$timelower = 0, $timeupper = 0) {
+
if ($friends = get_user_friends($user_guid, "", 999999, 0)) {
$friendguids = array();
- foreach($friends as $friend) {
+ foreach ($friends as $friend) {
$friendguids[] = $friend->getGUID();
}
return elgg_get_entities(array(
@@ -950,44 +486,44 @@ function count_user_friends_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VAL
*
* @see elgg_view_entity_list
*
- * @param int $user_guid The GUID of the user
- * @param string $subtype The object subtype
- * @param int $limit The number of entities to display on a page
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow you to flip to gallery mode (default: true)
- * @param true|false $pagination Whether to display pagination (default: true)
- * @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
+ * @param int $user_guid The GUID of the user
+ * @param string $subtype The object subtype
+ * @param int $limit The number of entities to display on a page
+ * @param bool $full_view Whether or not to display the full view (default: true)
+ * @param bool $listtypetoggle Whether or not to allow you to flip to gallery mode (default: true)
+ * @param bool $pagination Whether to display pagination (default: true)
+ * @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
*/
-function list_user_friends_objects($user_guid, $subtype = "", $limit = 10, $fullview = true, $viewtypetoggle = 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);
- $entities = get_user_friends_objects($user_guid, $subtype, $limit, $offset, $timelower, $timeupper);
+function list_user_friends_objects($user_guid, $subtype = "", $limit = 10, $full_view = true,
+$listtypetoggle = true, $pagination = true, $timelower = 0, $timeupper = 0) {
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, $viewtypetoggle, $pagination);
-}
+ $offset = (int)get_input('offset');
+ $limit = (int)$limit;
+ $count = (int)count_user_friends_objects($user_guid, $subtype, $timelower, $timeupper);
-/**
- * Get user objects by an array of metadata
- *
- * @param int $user_guid The GUID of the owning user
- * @param string $subtype Optionally, the subtype of objects
- * @paran array $metadata An array of metadata
- * @param int $limit The number of results to return (default 10)
- * @param int $offset Indexing offset, if any
- * @return false|array An array of ElggObjects or false, depending on success
- */
-function get_user_objects_by_metadata($user_guid, $subtype = "", $metadata = array(), $limit = 0, $offset = 0) {
- return get_entities_from_metadata_multi($metadata,"object",$subtype,$user_guid,$limit,$offset);
+ $entities = get_user_friends_objects($user_guid, $subtype, $limit, $offset,
+ $timelower, $timeupper);
+
+ return elgg_view_entity_list($entities, array(
+ 'count' => $count,
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'full_view' => $full_view,
+ 'list_type_toggle' => $listtypetoggle,
+ 'pagination' => $pagination,
+ ));
}
/**
* Get a user object from a GUID.
*
* This function returns an ElggUser from a given GUID.
+ *
* @param int $guid The GUID
+ *
* @return ElggUser|false
*/
function get_user($guid) {
@@ -997,7 +533,6 @@ function get_user($guid) {
}
if ((!empty($result)) && (!($result instanceof ElggUser))) {
- //throw new InvalidClassException(sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), $guid, 'ElggUser'));
return false;
}
@@ -1012,32 +547,45 @@ function get_user($guid) {
* Get user by username
*
* @param string $username The user's username
+ *
* @return ElggUser|false Depending on success
*/
function get_user_by_username($username) {
global $CONFIG, $USERNAME_TO_GUID_MAP_CACHE;
+ // Fixes #6052. Username is frequently sniffed from the path info, which,
+ // unlike $_GET, is not URL decoded. If the username was not URL encoded,
+ // this is harmless.
+ $username = rawurldecode($username);
+
$username = sanitise_string($username);
$access = get_access_sql_suffix('e');
// Caching
- if ( (isset($USERNAME_TO_GUID_MAP_CACHE[$username])) && (retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username])) ) {
- return retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]);
+ if ((isset($USERNAME_TO_GUID_MAP_CACHE[$username]))
+ && (_elgg_retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]))) {
+ return _elgg_retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]);
}
- $row = get_data_row("SELECT e.* from {$CONFIG->dbprefix}users_entity u join {$CONFIG->dbprefix}entities e on e.guid=u.guid where u.username='$username' and $access ");
- if ($row) {
- $USERNAME_TO_GUID_MAP_CACHE[$username] = $row->guid;
- return new ElggUser($row);
+ $query = "SELECT e.* from {$CONFIG->dbprefix}users_entity u
+ join {$CONFIG->dbprefix}entities e on e.guid=u.guid
+ where u.username='$username' and $access ";
+
+ $entity = get_data_row($query, 'entity_row_to_elggstar');
+ if ($entity) {
+ $USERNAME_TO_GUID_MAP_CACHE[$username] = $entity->guid;
+ } else {
+ $entity = false;
}
- return false;
+ return $entity;
}
/**
* Get user by session code
*
* @param string $code The session code
+ *
* @return ElggUser|false Depending on success
*/
function get_user_by_code($code) {
@@ -1048,24 +596,30 @@ function get_user_by_code($code) {
$access = get_access_sql_suffix('e');
// Caching
- if ( (isset($CODE_TO_GUID_MAP_CACHE[$code])) && (retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code])) ) {
- return retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]);
+ if ((isset($CODE_TO_GUID_MAP_CACHE[$code]))
+ && (_elgg_retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]))) {
+
+ return _elgg_retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]);
}
- $row = get_data_row("SELECT e.* from {$CONFIG->dbprefix}users_entity u join {$CONFIG->dbprefix}entities e on e.guid=u.guid where u.code='$code' and $access");
- if ($row) {
- $CODE_TO_GUID_MAP_CACHE[$code] = $row->guid;
- return new ElggUser($row);
+ $query = "SELECT e.* from {$CONFIG->dbprefix}users_entity u
+ join {$CONFIG->dbprefix}entities e on e.guid=u.guid
+ where u.code='$code' and $access";
+
+ $entity = get_data_row($query, 'entity_row_to_elggstar');
+ if ($entity) {
+ $CODE_TO_GUID_MAP_CACHE[$code] = $entity->guid;
}
- return false;
+ return $entity;
}
/**
- * Get an array of users from their
+ * 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;
@@ -1074,125 +628,72 @@ function get_user_by_email($email) {
$access = get_access_sql_suffix('e');
- $query = "SELECT e.* from {$CONFIG->dbprefix}entities e join {$CONFIG->dbprefix}users_entity u on e.guid=u.guid where email='$email' and $access";
+ $query = "SELECT e.* from {$CONFIG->dbprefix}entities e
+ join {$CONFIG->dbprefix}users_entity u on e.guid=u.guid
+ where email='$email' and $access";
return get_data($query, 'entity_row_to_elggstar');
}
/**
- * Searches for a user based on a complete or partial name or username.
- *
- * @param string $criteria The partial or full name or username.
- * @param int $limit Limit of the search.
- * @param int $offset Offset.
- * @param string $order_by The order.
- * @param boolean $count Whether to return the count of results or just the results.
- * @deprecated 1.7
- */
-function search_for_user($criteria, $limit = 10, $offset = 0, $order_by = "", $count = false) {
- elgg_deprecated_notice('search_for_user() was deprecated by new search.', 1.7);
- global $CONFIG;
-
- $criteria = sanitise_string($criteria);
- $limit = (int)$limit;
- $offset = (int)$offset;
- $order_by = sanitise_string($order_by);
-
- $access = get_access_sql_suffix("e");
-
- if ($order_by == "") {
- $order_by = "e.time_created desc";
- }
-
- if ($count) {
- $query = "SELECT count(e.guid) as total ";
- } else {
- $query = "SELECT e.* ";
- }
- $query .= "from {$CONFIG->dbprefix}entities e join {$CONFIG->dbprefix}users_entity u on e.guid=u.guid where ";
- // $query .= " match(u.name,u.username) against ('$criteria') ";
- $query .= "(u.name like \"%{$criteria}%\" or u.username like \"%{$criteria}%\")";
- $query .= " and $access";
-
- 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;
-}
-
-/**
- * Displays a list of user objects that have been searched for.
- *
- * @see elgg_view_entity_list
- *
- * @param string $tag Search criteria
- * @param int $limit The number of entities to display on a page
- * @return string The list in a form suitable to display
- * @deprecated 1.7
- */
-function list_user_search($tag, $limit = 10) {
- elgg_deprecated_notice('list_user_search() deprecated by new search', 1.7);
- $offset = (int) get_input('offset');
- $limit = (int) $limit;
- $count = (int) search_for_user($tag, 10, 0, '', true);
- $entities = search_for_user($tag, $limit, $offset);
-
- return elgg_view_entity_list($entities, $count, $offset, $limit, $fullview, false);
-}
-
-/**
* A function that returns a maximum of $limit users who have done something within the last
- * $seconds seconds.
+ * $seconds seconds or the total count of active users.
+ *
+ * @param int $seconds Number of seconds (default 600 = 10min)
+ * @param int $limit Limit, default 10.
+ * @param int $offset Offset, default 0.
+ * @param bool $count Count, default false.
*
- * @param int $seconds Number of seconds (default 600 = 10min)
- * @param int $limit Limit, default 10.
- * @param int $offset Offset, defualt 0.
+ * @return mixed
*/
-function find_active_users($seconds = 600, $limit = 10, $offset = 0) {
- global $CONFIG;
-
+function find_active_users($seconds = 600, $limit = 10, $offset = 0, $count = false) {
$seconds = (int)$seconds;
$limit = (int)$limit;
$offset = (int)$offset;
+ $params = array('seconds' => $seconds, 'limit' => $limit, 'offset' => $offset, 'count' => $count);
+ $data = elgg_trigger_plugin_hook('find_active_users', 'system', $params, NULL);
+ if (!$data) {
+ global $CONFIG;
- $time = time() - $seconds;
-
- $access = get_access_sql_suffix("e");
-
- $query = "SELECT distinct e.* from {$CONFIG->dbprefix}entities e join {$CONFIG->dbprefix}users_entity u on e.guid = u.guid where u.last_action >= {$time} and $access order by u.last_action desc limit {$offset},{$limit}";
+ $time = time() - $seconds;
- return get_data($query, "entity_row_to_elggstar");
+ $data = elgg_get_entities(array(
+ 'type' => 'user',
+ 'limit' => $limit,
+ 'offset' => $offset,
+ 'count' => $count,
+ 'joins' => array("join {$CONFIG->dbprefix}users_entity u on e.guid = u.guid"),
+ 'wheres' => array("u.last_action >= {$time}"),
+ 'order_by' => "u.last_action desc"
+ ));
+ }
+ return $data;
}
/**
* Generate and send a password request email to a given user's registered email address.
*
- * @param int $user_guid
+ * @param int $user_guid User GUID
+ *
+ * @return bool
*/
function send_new_password_request($user_guid) {
- global $CONFIG;
-
$user_guid = (int)$user_guid;
$user = get_entity($user_guid);
- if ($user) {
+ if ($user instanceof ElggUser) {
// generate code
$code = generate_random_cleartext_password();
- //create_metadata($user_guid, 'conf_code', $code,'', 0, ACCESS_PRIVATE);
- set_private_setting($user_guid, 'passwd_conf_code', $code);
+ $user->setPrivateSetting('passwd_conf_code', $code);
// generate link
- $link = $CONFIG->site->url . "pg/resetpassword?u=$user_guid&c=$code";
+ $link = elgg_get_site_url() . "resetpassword?u=$user_guid&c=$code";
// generate email
- $email = sprintf(elgg_echo('email:resetreq:body'), $user->name, $_SERVER['REMOTE_ADDR'], $link);
+ $email = elgg_echo('email:resetreq:body', array($user->name, $_SERVER['REMOTE_ADDR'], $link));
- return notify_user($user->guid, $CONFIG->site->guid, elgg_echo('email:resetreq:subject'), $email, NULL, 'email');
+ return notify_user($user->guid, elgg_get_site_entity()->guid,
+ elgg_echo('email:resetreq:subject'), $email, array(), 'email');
}
return false;
@@ -1203,23 +704,24 @@ function send_new_password_request($user_guid) {
*
* This can only be called from execute_new_password_request().
*
- * @param int $user_guid The user.
- * @param string $password password text (which will then be converted into a hash and stored)
+ * @param int $user_guid The user.
+ * @param string $password Text (which will then be converted into a hash and stored)
+ *
+ * @return bool
*/
function force_user_password_reset($user_guid, $password) {
- global $CONFIG;
-
- if (call_gatekeeper('execute_new_password_request', __FILE__)) {
- $user = get_entity($user_guid);
+ $user = get_entity($user_guid);
+ if ($user instanceof ElggUser) {
+ $ia = elgg_set_ignore_access();
- if ($user) {
- $salt = generate_random_cleartext_password(); // Reset the salt
- $user->salt = $salt;
+ $user->salt = generate_random_cleartext_password();
+ $hash = generate_user_password($user, $password);
+ $user->password = $hash;
+ $result = (bool)$user->save();
- $hash = generate_user_password($user, $password);
+ elgg_set_ignore_access($ia);
- return update_data("UPDATE {$CONFIG->dbprefix}users_entity set password='$hash', salt='$salt' where guid=$user_guid");
- }
+ return $result;
}
return false;
@@ -1228,8 +730,10 @@ function force_user_password_reset($user_guid, $password) {
/**
* Validate and execute a password reset for a user.
*
- * @param int $user_guid The user id
+ * @param int $user_guid The user id
* @param string $conf_code Confirmation code as sent in the request email.
+ *
+ * @return mixed
*/
function execute_new_password_request($user_guid, $conf_code) {
global $CONFIG;
@@ -1237,17 +741,22 @@ function execute_new_password_request($user_guid, $conf_code) {
$user_guid = (int)$user_guid;
$user = get_entity($user_guid);
- $saved_code = get_private_setting($user_guid, 'passwd_conf_code');
-
- if ($user && $saved_code && $saved_code == $conf_code) {
- $password = generate_random_cleartext_password();
+ if ($user instanceof ElggUser) {
+ $saved_code = $user->getPrivateSetting('passwd_conf_code');
- if (force_user_password_reset($user_guid, $password)) {
- remove_private_setting($user_guid, 'passwd_conf_code');
+ if ($saved_code && $saved_code == $conf_code) {
+ $password = generate_random_cleartext_password();
- $email = sprintf(elgg_echo('email:resetpassword:body'), $user->name, $password);
+ if (force_user_password_reset($user_guid, $password)) {
+ remove_private_setting($user_guid, 'passwd_conf_code');
+ // clean the logins failures
+ reset_login_failure_count($user_guid);
+
+ $email = elgg_echo('email:resetpassword:body', array($user->name, $password));
- return notify_user($user->guid, $CONFIG->site->guid, elgg_echo('email:resetpassword:subject'), $email, NULL, 'email');
+ return notify_user($user->guid, $CONFIG->site->guid,
+ elgg_echo('email:resetpassword:subject'), $email, array(), 'email');
+ }
}
}
@@ -1255,130 +764,11 @@ function execute_new_password_request($user_guid, $conf_code) {
}
/**
- * Handles pages for password reset requests.
- *
- * @param unknown_type $page
- * @return unknown_type
- */
-function elgg_user_resetpassword_page_handler($page) {
- global $CONFIG;
-
- $user_guid = get_input('u');
- $code = get_input('c');
-
- $user = get_entity($user_guid);
-
- // don't check code here to avoid automated attacks
- if (!$user instanceof ElggUser) {
- register_error(elgg_echo('user:passwordreset:unknown_user'));
- forward();
- }
-
- $form_body = elgg_echo('user:resetpassword:reset_password_confirm') . "<br />";
-
- $form_body .= elgg_view('input/hidden', array(
- 'internalname' => 'u',
- 'value' => $user_guid
- ));
-
- $form_body .= elgg_view('input/hidden', array(
- 'internalname' => 'c',
- 'value' => $code
- ));
-
- $form_body .= elgg_view('input/submit', array(
- 'value' => elgg_echo('resetpassword')
- ));
-
- $form .= elgg_view('input/form', array(
- 'body' => $form_body,
- 'action' => $CONFIG->site->url . 'action/user/passwordreset'
- ));
-
- $title = elgg_echo('resetpassword');
- $content = elgg_view_title(elgg_echo('resetpassword')) . $form;
-
- page_draw($title, elgg_view_layout('one_column', $content));
-}
-
-/**
- * Set the validation status for a user.
- *
- * @param bool $status Validated (true) or false
- * @param string $method Optional method to say how a user was validated
- * @return bool
- */
-function set_user_validation_status($user_guid, $status, $method = '') {
- if (!$status) {
- $method = '';
- }
-
- if ($status) {
- if (
- (create_metadata($user_guid, 'validated', $status,'', 0, ACCESS_PUBLIC)) &&
- (create_metadata($user_guid, 'validated_method', $method,'', 0, ACCESS_PUBLIC))
- ) {
- return true;
- }
- } else {
- $validated = get_metadata_byname($user_guid, 'validated');
- $validated_method = get_metadata_byname($user_guid, 'validated_method');
-
- if (
- ($validated) &&
- ($validated_method) &&
- (delete_metadata($validated->id)) &&
- (delete_metadata($validated_method->id))
- )
- return true;
- }
-
- return false;
-}
-
-/**
- * Trigger an event requesting that a user guid be validated somehow - either by email address or some other way.
- *
- * This event invalidates any existing values and returns
- *
- * @param unknown_type $user_guid
- */
-function request_user_validation($user_guid) {
- $user = get_entity($user_guid);
-
- if (($user) && ($user instanceof ElggUser)) {
- // invalidate any existing validations
- set_user_validation_status($user_guid, false);
-
- // request validation
- trigger_elgg_event('validate', 'user', $user);
- }
-}
-
-/**
- * Validates an email address.
- *
- * @param string $address Email address.
- * @return bool
- */
-function is_email_address($address) {
- // TODO: Make this better!
-
- if (strpos($address, '@')=== false) {
- return false;
- }
-
- if (strpos($address, '.')=== false) {
- return false;
- }
-
- return true;
-}
-
-/**
- * Simple function that will generate a random clear text password suitable for feeding into generate_user_password().
+ * Simple function that will generate a random clear text password
+ * suitable for feeding into generate_user_password().
*
* @see generate_user_password
+ *
* @return string
*/
function generate_random_cleartext_password() {
@@ -1388,10 +778,10 @@ function generate_random_cleartext_password() {
/**
* Generate a password for a user, currently uses MD5.
*
- * Later may introduce salting etc.
+ * @param ElggUser $user The user this is being generated for.
+ * @param string $password Password in clear text
*
- * @param ElggUser $user The user this is being generated for.
- * @param string $password Password in clear text
+ * @return string
*/
function generate_user_password(ElggUser $user, $password) {
return md5($password . $user->salt);
@@ -1402,7 +792,9 @@ function generate_user_password(ElggUser $user, $password) {
*
* This should only permit chars that are valid on the file system as well.
*
- * @param string $username
+ * @param string $username Username
+ *
+ * @return bool
* @throws RegistrationException on invalid
*/
function validate_username($username) {
@@ -1414,57 +806,80 @@ function validate_username($username) {
}
if (strlen($username) < $CONFIG->minusername) {
- throw new RegistrationException(elgg_echo('registration:usernametooshort'));
+ $msg = elgg_echo('registration:usernametooshort', array($CONFIG->minusername));
+ throw new RegistrationException($msg);
+ }
+
+ // username in the database has a limit of 128 characters
+ if (strlen($username) > 128) {
+ $msg = elgg_echo('registration:usernametoolong', array(128));
+ throw new RegistrationException($msg);
}
// Blacklist for bad characters (partially nicked from mediawiki)
-
$blacklist = '/[' .
- '\x{0080}-\x{009f}' . # iso-8859-1 control chars
- '\x{00a0}' . # non-breaking space
- '\x{2000}-\x{200f}' . # various whitespace
- '\x{2028}-\x{202f}' . # breaks and control chars
- '\x{3000}' . # ideographic space
- '\x{e000}-\x{f8ff}' . # private use
+ '\x{0080}-\x{009f}' . // iso-8859-1 control chars
+ '\x{00a0}' . // non-breaking space
+ '\x{2000}-\x{200f}' . // various whitespace
+ '\x{2028}-\x{202f}' . // breaks and control chars
+ '\x{3000}' . // ideographic space
+ '\x{e000}-\x{f8ff}' . // private use
']/u';
if (
preg_match($blacklist, $username)
) {
+ // @todo error message needs work
throw new RegistrationException(elgg_echo('registration:invalidchars'));
}
- // Belts and braces TODO: Tidy into main unicode
+ // Belts and braces
+ // @todo Tidy into main unicode
$blacklist2 = '\'/\\"*& ?#%^(){}[]~?<>;|¬`@-+=';
- for ($n=0; $n < strlen($blacklist2); $n++) {
- if (strpos($username, $blacklist2[$n])!==false) {
- throw new RegistrationException(sprintf(elgg_echo('registration:invalidchars'), $blacklist2[$n], $blacklist2));
+
+ for ($n = 0; $n < strlen($blacklist2); $n++) {
+ if (strpos($username, $blacklist2[$n]) !== false) {
+ $msg = elgg_echo('registration:invalidchars', array($blacklist2[$n], $blacklist2));
+ $msg = htmlspecialchars($msg, ENT_QUOTES, 'UTF-8');
+ throw new RegistrationException($msg);
}
}
$result = true;
- return trigger_plugin_hook('registeruser:validate:username', 'all', array('username' => $username), $result);
+ return elgg_trigger_plugin_hook('registeruser:validate:username', 'all',
+ array('username' => $username), $result);
}
/**
* Simple validation of a password.
*
- * @param string $password
+ * @param string $password Clear text password
+ *
+ * @return bool
* @throws RegistrationException on invalid
*/
function validate_password($password) {
- if (strlen($password) < 6) {
- throw new RegistrationException(elgg_echo('registration:passwordtooshort'));
+ global $CONFIG;
+
+ if (!isset($CONFIG->min_password_length)) {
+ $CONFIG->min_password_length = 6;
+ }
+
+ if (strlen($password) < $CONFIG->min_password_length) {
+ $msg = elgg_echo('registration:passwordtooshort', array($CONFIG->min_password_length));
+ throw new RegistrationException($msg);
}
$result = true;
- return trigger_plugin_hook('registeruser:validate:password', 'all', array('password' => $password), $result);
+ return elgg_trigger_plugin_hook('registeruser:validate:password', 'all',
+ array('password' => $password), $result);
}
/**
* Simple validation of a email.
*
- * @param string $address
+ * @param string $address Email address
+ *
* @throws RegistrationException on invalid
* @return bool
*/
@@ -1475,27 +890,30 @@ function validate_email_address($address) {
// Got here, so lets try a hook (defaulting to ok)
$result = true;
- return trigger_plugin_hook('registeruser:validate:email', 'all', array('email' => $address), $result);
+ return elgg_trigger_plugin_hook('registeruser:validate:email', 'all',
+ array('email' => $address), $result);
}
/**
* Registers a user, returning false if the username already exists
*
- * @param string $username The username of the new user
- * @param string $password The password
- * @param string $name The user's display name
- * @param string $email Their email address
- * @param bool $allow_multiple_emails Allow the same email address to be registered multiple times?
- * @param int $friend_guid Optionally, GUID of a user this user will friend once fully registered
+ * @param string $username The username of the new user
+ * @param string $password The password
+ * @param string $name The user's display name
+ * @param string $email Their email address
+ * @param bool $allow_multiple_emails Allow the same email address to be
+ * registered multiple times?
+ * @param int $friend_guid GUID of a user to friend once fully registered
+ * @param string $invitecode An invite code from a friend
+ *
* @return int|false The new user's GUID; false on failure
+ * @throws RegistrationException
*/
-function register_user($username, $password, $name, $email, $allow_multiple_emails = false, $friend_guid = 0, $invitecode = '') {
- // Load the configuration
- global $CONFIG;
+function register_user($username, $password, $name, $email,
+$allow_multiple_emails = false, $friend_guid = 0, $invitecode = '') {
- $username = trim($username);
// no need to trim password.
- $password = $password;
+ $username = trim($username);
$name = trim(strip_tags($name));
$email = trim($email);
@@ -1507,39 +925,33 @@ function register_user($username, $password, $name, $email, $allow_multiple_emai
return false;
}
- // See if it exists and is disabled
+ // Make sure a user with conflicting details hasn't registered and been disabled
$access_status = access_get_show_hidden_status();
access_show_hidden_entities(true);
- // Validate email address
if (!validate_email_address($email)) {
throw new RegistrationException(elgg_echo('registration:emailnotvalid'));
}
- // Validate password
if (!validate_password($password)) {
throw new RegistrationException(elgg_echo('registration:passwordnotvalid'));
}
- // Validate the username
if (!validate_username($username)) {
throw new RegistrationException(elgg_echo('registration:usernamenotvalid'));
}
- // Check to see if $username exists already
if ($user = get_user_by_username($username)) {
- //return false;
throw new RegistrationException(elgg_echo('registration:userexists'));
}
- // If we're not allowed multiple emails then see if this address has been used before
if ((!$allow_multiple_emails) && (get_user_by_email($email))) {
throw new RegistrationException(elgg_echo('registration:dupeemail'));
}
access_show_hidden_entities($access_status);
- // Otherwise ...
+ // Create user
$user = new ElggUser();
$user->username = $username;
$user->email = $email;
@@ -1549,6 +961,7 @@ function register_user($username, $password, $name, $email, $allow_multiple_emai
$user->password = generate_user_password($user, $password);
$user->owner_guid = 0; // Users aren't owned by anyone, even if they are admin created.
$user->container_guid = 0; // Users aren't contained by anyone, even if they are admin created.
+ $user->language = get_current_language();
$user->save();
// If $friend_guid has been set, make mutual friends
@@ -1559,26 +972,12 @@ function register_user($username, $password, $name, $email, $allow_multiple_emai
$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());
}
}
}
- // Check to see if we've registered the first admin yet.
- // If not, this is the first admin user!
- $have_admin = datalist_get('admin_registered');
- global $registering_admin;
-
- if (!$have_admin) {
- $user->makeAdmin();
- set_user_validation_status($user->getGUID(), TRUE, 'first_run');
- datalist_set('admin_registered', 1);
- $registering_admin = true;
- } else {
- $registering_admin = false;
- }
-
// Turn on email notifications by default
set_user_notification_setting($user->getGUID(), 'email', true);
@@ -1589,6 +988,7 @@ function register_user($username, $password, $name, $email, $allow_multiple_emai
* Generates a unique invite code for a user
*
* @param string $username The username of the user sending the invitation
+ *
* @return string Invite code
*/
function generate_invite_code($username) {
@@ -1597,289 +997,607 @@ function generate_invite_code($username) {
}
/**
- * Adds collection submenu items
+ * Set the validation status for a user.
*
+ * @param int $user_guid The user's GUID
+ * @param bool $status Validated (true) or unvalidated (false)
+ * @param string $method Optional method to say how a user was validated
+ * @return bool
+ * @since 1.8.0
*/
-function collections_submenu_items() {
- global $CONFIG;
- $user = get_loggedin_user();
- add_submenu_item(elgg_echo('friends:collections'), $CONFIG->wwwroot . "pg/collections/" . $user->username);
- add_submenu_item(elgg_echo('friends:collections:add'), $CONFIG->wwwroot . "pg/collections/add");
+function elgg_set_user_validation_status($user_guid, $status, $method = '') {
+ $result1 = create_metadata($user_guid, 'validated', $status, '', 0, ACCESS_PUBLIC, false);
+ $result2 = create_metadata($user_guid, 'validated_method', $method, '', 0, ACCESS_PUBLIC, false);
+ if ($result1 && $result2) {
+ return true;
+ } else {
+ return false;
+ }
}
/**
- * Page handler for friends
+ * Gets the validation status of a user.
*
+ * @param int $user_guid The user's GUID
+ * @return bool|null Null means status was not set for this user.
+ * @since 1.8.0
*/
-function friends_page_handler($page_elements) {
- if (isset($page_elements[0]) && $user = get_user_by_username($page_elements[0])) {
- set_page_owner($user->getGUID());
+function elgg_get_user_validation_status($user_guid) {
+ $md = elgg_get_metadata(array(
+ 'guid' => $user_guid,
+ 'metadata_name' => 'validated'
+ ));
+ if ($md == false) {
+ return null;
}
- if (get_loggedin_userid() == page_owner()) {
- // collections_submenu_items(); disabled for now as we no longer use friends collections (replaced by shared access)
+
+ if ($md[0]->value) {
+ return true;
}
- require_once(dirname(dirname(dirname(__FILE__))) . "/friends/index.php");
+
+ return false;
}
/**
- * Page handler for friends of
+ * Adds collection submenu items
*
+ * @return void
+ * @access private
*/
-function friends_of_page_handler($page_elements) {
- if (isset($page_elements[0]) && $user = get_user_by_username($page_elements[0])) {
- set_page_owner($user->getGUID());
- }
- if (get_loggedin_userid() == page_owner()) {
- // collections_submenu_items(); disabled for now as we no longer use friends collections (replaced by shared access)
+function collections_submenu_items() {
+
+ $user = elgg_get_logged_in_user_entity();
+
+ elgg_register_menu_item('page', array(
+ 'name' => 'friends:view:collections',
+ 'text' => elgg_echo('friends:collections'),
+ 'href' => "collections/$user->username",
+ ));
+}
+
+/**
+ * Page handler for friends-related pages
+ *
+ * @param array $segments URL segments
+ * @param string $handler The first segment in URL used for routing
+ *
+ * @return bool
+ * @access private
+ */
+function friends_page_handler($segments, $handler) {
+ elgg_set_context('friends');
+
+ if (isset($segments[0]) && $user = get_user_by_username($segments[0])) {
+ elgg_set_page_owner_guid($user->getGUID());
+ }
+ if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid()) {
+ collections_submenu_items();
+ }
+
+ switch ($handler) {
+ case 'friends':
+ require_once(dirname(dirname(dirname(__FILE__))) . "/pages/friends/index.php");
+ break;
+ case 'friendsof':
+ require_once(dirname(dirname(dirname(__FILE__))) . "/pages/friends/of.php");
+ break;
+ default:
+ return false;
}
- require_once(dirname(dirname(dirname(__FILE__))) . "/friends/of.php");
+ return true;
}
/**
* Page handler for friends collections
*
+ * @param array $page_elements Page elements
+ *
+ * @return bool
+ * @access private
*/
function collections_page_handler($page_elements) {
+ gatekeeper();
+ elgg_set_context('friends');
+ $base = elgg_get_config('path');
if (isset($page_elements[0])) {
if ($page_elements[0] == "add") {
- set_page_owner(get_loggedin_userid());
+ elgg_set_page_owner_guid(elgg_get_logged_in_user_guid());
collections_submenu_items();
- require_once(dirname(dirname(dirname(__FILE__))) . "/friends/add.php");
+ require_once "{$base}pages/friends/collections/add.php";
+ return true;
} else {
- if ($user = get_user_by_username($page_elements[0])) {
- set_page_owner($user->getGUID());
- if (get_loggedin_userid() == page_owner()) {
+ $user = get_user_by_username($page_elements[0]);
+ if ($user) {
+ elgg_set_page_owner_guid($user->getGUID());
+ if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid()) {
collections_submenu_items();
}
- require_once(dirname(dirname(dirname(__FILE__))) . "/friends/collections.php");
+ require_once "{$base}pages/friends/collections/view.php";
+ return true;
}
}
}
+ return false;
}
/**
- * Page handler for dashboard
- */
-function dashboard_page_handler($page_elements) {
- require_once(dirname(dirname(dirname(__FILE__))) . "/dashboard/index.php");
-}
-
-
-/**
- * Page handler for registration
- */
-function registration_page_handler($page_elements) {
- require_once(dirname(dirname(dirname(__FILE__))) . "/account/register.php");
-}
-
-/**
- * Display a login box.
+ * Page handler for account related pages
+ *
+ * @param array $page_elements Page elements
+ * @param string $handler The handler string
*
- * This is a fallback for non-JS users who click on the
- * dropdown login link.
+ * @return bool
+ * @access private
*/
-function elgg_user_login_page_handler() {
- $content = elgg_view_layout('one_column', elgg_view('account/forms/login'));
- $content = '
- <div id="elgg_content" class="clearfloat">
- ' . elgg_view('account/forms/login') . '
- </div>
- ';
- page_draw('test', $content);
+function elgg_user_account_page_handler($page_elements, $handler) {
+
+ $base_dir = elgg_get_root_path() . 'pages/account';
+ switch ($handler) {
+ case 'login':
+ require_once("$base_dir/login.php");
+ break;
+ case 'forgotpassword':
+ require_once("$base_dir/forgotten_password.php");
+ break;
+ case 'resetpassword':
+ require_once("$base_dir/reset_password.php");
+ break;
+ case 'register':
+ require_once("$base_dir/register.php");
+ break;
+ default:
+ return false;
+ }
+ return true;
}
/**
* Sets the last action time of the given user to right now.
*
* @param int $user_guid The user GUID
+ *
+ * @return void
*/
function set_last_action($user_guid) {
$user_guid = (int) $user_guid;
global $CONFIG;
$time = time();
- execute_delayed_write_query("UPDATE {$CONFIG->dbprefix}users_entity set prev_last_action = last_action, last_action = {$time} where guid = {$user_guid}");
+ $query = "UPDATE {$CONFIG->dbprefix}users_entity
+ set prev_last_action = last_action,
+ last_action = {$time} where guid = {$user_guid}";
+
+ execute_delayed_write_query($query);
}
/**
* Sets the last logon time of the given user to right now.
*
* @param int $user_guid The user GUID
+ *
+ * @return void
*/
function set_last_login($user_guid) {
$user_guid = (int) $user_guid;
global $CONFIG;
$time = time();
- execute_delayed_write_query("UPDATE {$CONFIG->dbprefix}users_entity set prev_last_login = last_login, last_login = {$time} where guid = {$user_guid}");
+ $query = "UPDATE {$CONFIG->dbprefix}users_entity
+ set prev_last_login = last_login, last_login = {$time} where guid = {$user_guid}";
+
+ execute_delayed_write_query($query);
}
/**
- * A permissions plugin hook that grants access to users if they are newly created - allows
- * for email activation.
+ * Creates a relationship between this site and the user.
*
- * TODO: Do this in a better way!
+ * @param string $event create
+ * @param string $object_type user
+ * @param ElggUser $object User object
*
- * @param unknown_type $hook
- * @param unknown_type $entity_type
- * @param unknown_type $returnvalue
- * @param unknown_type $params
+ * @return void
+ * @access private
*/
-function new_user_enable_permissions_check($hook, $entity_type, $returnvalue, $params) {
- $entity = $params['entity'];
- $user = $params['user'];
- if (($entity) && ($entity instanceof ElggUser)) {
- if (
- (($entity->disable_reason == 'new_user') || (
- // if this isn't set at all they're a "new user"
- !$entity->validated
- ))
- && (!isloggedin())) {
- return true;
+function user_create_hook_add_site_relationship($event, $object_type, $object) {
+ add_entity_relationship($object->getGUID(), 'member_of_site', elgg_get_site_entity()->guid);
+}
+
+/**
+ * Serves the user's avatar
+ *
+ * @param string $hook
+ * @param string $entity_type
+ * @param string $returnvalue
+ * @param array $params
+ * @return string
+ * @access private
+ */
+function user_avatar_hook($hook, $entity_type, $returnvalue, $params) {
+ $user = $params['entity'];
+ $size = $params['size'];
+
+ if (isset($user->icontime)) {
+ return "avatar/view/$user->username/$size/$user->icontime";
+ } else {
+ return "_graphics/icons/user/default{$size}.gif";
+ }
+}
+
+/**
+ * Setup the default user hover menu
+ * @access private
+ */
+function elgg_user_hover_menu($hook, $type, $return, $params) {
+ $user = $params['entity'];
+ /* @var ElggUser $user */
+
+ if (elgg_is_logged_in()) {
+ if (elgg_get_logged_in_user_guid() != $user->guid) {
+ if ($user->isFriend()) {
+ $url = "action/friends/remove?friend={$user->guid}";
+ $text = elgg_echo('friend:remove');
+ $name = 'remove_friend';
+ } else {
+ $url = "action/friends/add?friend={$user->guid}";
+ $text = elgg_echo('friend:add');
+ $name = 'add_friend';
+ }
+ $url = elgg_add_action_tokens_to_url($url);
+ $item = new ElggMenuItem($name, $text, $url);
+ $item->setSection('action');
+ $return[] = $item;
+ } else {
+ $url = "profile/$user->username/edit";
+ $item = new ElggMenuItem('profile:edit', elgg_echo('profile:edit'), $url);
+ $item->setSection('action');
+ $return[] = $item;
+
+ $url = "avatar/edit/$user->username";
+ $item = new ElggMenuItem('avatar:edit', elgg_echo('avatar:edit'), $url);
+ $item->setSection('action');
+ $return[] = $item;
+ }
+ }
+
+ // prevent admins from banning or deleting themselves
+ if (elgg_get_logged_in_user_guid() == $user->guid) {
+ return $return;
+ }
+
+ if (elgg_is_admin_logged_in()) {
+ $actions = array();
+ if (!$user->isBanned()) {
+ $actions[] = 'ban';
+ } else {
+ $actions[] = 'unban';
+ }
+ $actions[] = 'delete';
+ $actions[] = 'resetpassword';
+ if (!$user->isAdmin()) {
+ $actions[] = 'makeadmin';
+ } else {
+ $actions[] = 'removeadmin';
+ }
+
+ foreach ($actions as $action) {
+ $url = "action/admin/user/$action?guid={$user->guid}";
+ $url = elgg_add_action_tokens_to_url($url);
+ $item = new ElggMenuItem($action, elgg_echo($action), $url);
+ $item->setSection('admin');
+ $item->setLinkClass('elgg-requires-confirmation');
+
+ $return[] = $item;
}
+
+ $url = "profile/$user->username/edit";
+ $item = new ElggMenuItem('profile:edit', elgg_echo('profile:edit'), $url);
+ $item->setSection('admin');
+ $return[] = $item;
+
+ $url = "settings/user/$user->username";
+ $item = new ElggMenuItem('settings:edit', elgg_echo('settings:edit'), $url);
+ $item->setSection('admin');
+ $return[] = $item;
}
- return $returnvalue;
+ return $return;
}
/**
- * Creates a relationship between this site and the user.
+ * Setup the menu shown with an entity
*
- * @param $event
- * @param $object_type
- * @param $object
- * @return bool
+ * @param string $hook
+ * @param string $type
+ * @param array $return
+ * @param array $params
+ * @return array
+ *
+ * @access private
*/
-function user_create_hook_add_site_relationship($event, $object_type, $object) {
- global $CONFIG;
+function elgg_users_setup_entity_menu($hook, $type, $return, $params) {
+ if (elgg_in_context('widgets')) {
+ return $return;
+ }
- add_entity_relationship($object->getGUID(), 'member_of_site', $CONFIG->site->getGUID());
+ $entity = $params['entity'];
+ if (!elgg_instanceof($entity, 'user')) {
+ return $return;
+ }
+ /* @var ElggUser $entity */
+
+ if ($entity->isBanned()) {
+ $banned = elgg_echo('banned');
+ $options = array(
+ 'name' => 'banned',
+ 'text' => "<span>$banned</span>",
+ 'href' => false,
+ 'priority' => 0,
+ );
+ $return = array(ElggMenuItem::factory($options));
+ } else {
+ $return = array();
+ if (isset($entity->location)) {
+ $location = htmlspecialchars($entity->location, ENT_QUOTES, 'UTF-8', false);
+ $options = array(
+ 'name' => 'location',
+ 'text' => "<span>$location</span>",
+ 'href' => false,
+ 'priority' => 150,
+ );
+ $return[] = ElggMenuItem::factory($options);
+ }
+ }
+
+ return $return;
}
/**
- * Sets up user-related menu items
+ * This function loads a set of default fields into the profile, then triggers a hook letting other plugins to edit
+ * add and delete fields.
*
+ * Note: This is a secondary system:init call and is run at a super low priority to guarantee that it is called after all
+ * other plugins have initialised.
+ * @access private
*/
-function users_pagesetup() {
- // Load config
+function elgg_profile_fields_setup() {
global $CONFIG;
- //add submenu options
- if (get_context() == "friends" || get_context() == "friendsof") { // || get_context() == "collections") { - disabled as we no longer use collections
- add_submenu_item(elgg_echo('friends'),$CONFIG->wwwroot."pg/friends/" . page_owner_entity()->username);
- add_submenu_item(elgg_echo('friends:of'),$CONFIG->wwwroot."pg/friendsof/" . page_owner_entity()->username);
- if(is_plugin_enabled('members'))
- add_submenu_item(elgg_echo('members:browse'), $CONFIG->wwwroot . "mod/members/index.php");
+ $profile_defaults = array (
+ 'description' => 'longtext',
+ 'briefdescription' => 'text',
+ 'location' => 'location',
+ 'interests' => 'tags',
+ 'skills' => 'tags',
+ 'contactemail' => 'email',
+ 'phone' => 'text',
+ 'mobile' => 'text',
+ 'website' => 'url',
+ 'twitter' => 'text'
+ );
+
+ $loaded_defaults = array();
+ if ($fieldlist = elgg_get_config('profile_custom_fields')) {
+ if (!empty($fieldlist)) {
+ $fieldlistarray = explode(',', $fieldlist);
+ foreach ($fieldlistarray as $listitem) {
+ if ($translation = elgg_get_config("admin_defined_profile_{$listitem}")) {
+ $type = elgg_get_config("admin_defined_profile_type_{$listitem}");
+ $loaded_defaults["admin_defined_profile_{$listitem}"] = $type;
+ add_translation(get_current_language(), array("profile:admin_defined_profile_{$listitem}" => $translation));
+ }
+ }
+ }
+ }
+
+ if (count($loaded_defaults)) {
+ $CONFIG->profile_using_custom = true;
+ $profile_defaults = $loaded_defaults;
+ }
+
+ $CONFIG->profile_fields = elgg_trigger_plugin_hook('profile:fields', 'profile', NULL, $profile_defaults);
+
+ // register any tag metadata names
+ foreach ($CONFIG->profile_fields as $name => $type) {
+ if ($type == 'tags' || $type == 'location' || $type == 'tag') {
+ elgg_register_tag_metadata_name($name);
+ // register a tag name translation
+ add_translation(get_current_language(), array("tag_names:$name" => elgg_echo("profile:$name")));
+ }
}
}
/**
- * Users initialisation function, which establishes the page handler
+ * Avatar page handler
*
+ * /avatar/edit/<username>
+ * /avatar/view/<username>/<size>/<icontime>
+ *
+ * @param array $page
+ * @return bool
+ * @access private
*/
-function users_init() {
- // Load config
+function elgg_avatar_page_handler($page) {
global $CONFIG;
- // add Friends to tools menu - if profile mod is running
- // now added to toolbar
- /*
- if ( isloggedin() && is_plugin_enabled('profile') ) {
- $user = get_loggedin_user();
- add_menu(elgg_echo('friends'), $CONFIG->wwwroot . "pg/friends/" . $user->username, array(), 'core:friends');
+ $user = get_user_by_username($page[1]);
+ if ($user) {
+ elgg_set_page_owner_guid($user->getGUID());
}
- */
-
- register_page_handler('friends', 'friends_page_handler');
- register_page_handler('friendsof', 'friends_of_page_handler');
- register_page_handler('dashboard', 'dashboard_page_handler');
- register_page_handler('register', 'registration_page_handler');
- register_page_handler('resetpassword', 'elgg_user_resetpassword_page_handler');
- register_page_handler('login', 'elgg_user_login_page_handler');
-
- register_action("register", true);
- register_action("useradd", true);
- register_action("friends/add");
- register_action("friends/remove");
- //register_action('friends/addcollection');
- //register_action('friends/deletecollection');
- //register_action('friends/editcollection');
- //register_action("user/spotlight");
- register_action("usersettings/save");
-
- register_action("user/passwordreset", TRUE);
- register_action("user/requestnewpassword", TRUE);
+ if ($page[0] == 'edit') {
+ require_once("{$CONFIG->path}pages/avatar/edit.php");
+ return true;
+ } else {
+ set_input('size', $page[2]);
+ require_once("{$CONFIG->path}pages/avatar/view.php");
+ return true;
+ }
+ return false;
+}
- // User name change
- extend_elgg_settings_page('user/settings/name', 'usersettings/user', 1);
- //register_action("user/name");
+/**
+ * Profile page handler
+ *
+ * @param array $page
+ * @return bool
+ * @access private
+ */
+function elgg_profile_page_handler($page) {
+ global $CONFIG;
- // User password change
- extend_elgg_settings_page('user/settings/password', 'usersettings/user', 1);
- //register_action("user/password");
+ $user = get_user_by_username($page[0]);
+ elgg_set_page_owner_guid($user->guid);
- // Add email settings
- extend_elgg_settings_page('user/settings/email', 'usersettings/user', 1);
- //register_action("email/save");
+ if ($page[1] == 'edit') {
+ require_once("{$CONFIG->path}pages/profile/edit.php");
+ return true;
+ }
+ return false;
+}
- // Add language settings
- extend_elgg_settings_page('user/settings/language', 'usersettings/user', 1);
+/**
+ * Sets up user-related menu items
+ *
+ * @return void
+ * @access private
+ */
+function users_pagesetup() {
- // Add default access settings
- extend_elgg_settings_page('user/settings/default_access', 'usersettings/user', 1);
+ $owner = elgg_get_page_owner_entity();
+ $viewer = elgg_get_logged_in_user_entity();
+
+ if ($owner) {
+ $params = array(
+ 'name' => 'friends',
+ 'text' => elgg_echo('friends'),
+ 'href' => 'friends/' . $owner->username,
+ 'contexts' => array('friends')
+ );
+ elgg_register_menu_item('page', $params);
+
+ $params = array(
+ 'name' => 'friends:of',
+ 'text' => elgg_echo('friends:of'),
+ 'href' => 'friendsof/' . $owner->username,
+ 'contexts' => array('friends')
+ );
+ elgg_register_menu_item('page', $params);
+
+ elgg_register_menu_item('page', array(
+ 'name' => 'edit_avatar',
+ 'href' => "avatar/edit/{$owner->username}",
+ 'text' => elgg_echo('avatar:edit'),
+ 'contexts' => array('profile_edit'),
+ ));
- //register_action("user/language");
+ elgg_register_menu_item('page', array(
+ 'name' => 'edit_profile',
+ 'href' => "profile/{$owner->username}/edit",
+ 'text' => elgg_echo('profile:edit'),
+ 'contexts' => array('profile_edit'),
+ ));
+ }
- // Register the user type
- register_entity_type('user','');
+ // topbar
+ if ($viewer) {
+ elgg_register_menu_item('topbar', array(
+ 'name' => 'profile',
+ 'href' => $viewer->getURL(),
+ 'text' => elgg_view('output/img', array(
+ 'src' => $viewer->getIconURL('topbar'),
+ 'alt' => $viewer->name,
+ 'title' => elgg_echo('profile'),
+ 'class' => 'elgg-border-plain elgg-transition',
+ )),
+ 'priority' => 100,
+ 'link_class' => 'elgg-topbar-avatar',
+ ));
- register_plugin_hook('usersettings:save','user','users_settings_save');
+ elgg_register_menu_item('topbar', array(
+ 'name' => 'friends',
+ 'href' => "friends/{$viewer->username}",
+ 'text' => elgg_view_icon('users'),
+ 'title' => elgg_echo('friends'),
+ 'priority' => 300,
+ ));
- register_elgg_event_handler('create', 'user', 'user_create_hook_add_site_relationship');
+ elgg_register_menu_item('topbar', array(
+ 'name' => 'usersettings',
+ 'href' => "settings/user/{$viewer->username}",
+ 'text' => elgg_view_icon('settings') . elgg_echo('settings'),
+ 'priority' => 500,
+ 'section' => 'alt',
+ ));
- // Handle a special case for newly created users when the user is not logged in
- // TODO: handle this better!
- register_plugin_hook('permissions_check','all','new_user_enable_permissions_check');
+ elgg_register_menu_item('topbar', array(
+ 'name' => 'logout',
+ 'href' => "action/logout",
+ 'text' => elgg_echo('logout'),
+ 'is_action' => TRUE,
+ 'priority' => 1000,
+ 'section' => 'alt',
+ ));
+ }
}
/**
- * Returns a formatted list of users suitable for injecting into search.
- * @deprecated 1.7
+ * Users initialisation function, which establishes the page handler
+ *
+ * @return void
+ * @access private
*/
-function search_list_users_by_name($hook, $user, $returnvalue, $tag) {
- elgg_deprecated_notice('search_list_users_by_name() was deprecated by new search', 1.7);
- // Change this to set the number of users that display on the search page
- $threshold = 4;
+function users_init() {
- $object = get_input('object');
+ elgg_register_page_handler('friends', 'friends_page_handler');
+ elgg_register_page_handler('friendsof', 'friends_page_handler');
+ elgg_register_page_handler('register', 'elgg_user_account_page_handler');
+ elgg_register_page_handler('forgotpassword', 'elgg_user_account_page_handler');
+ elgg_register_page_handler('resetpassword', 'elgg_user_account_page_handler');
+ elgg_register_page_handler('login', 'elgg_user_account_page_handler');
+ elgg_register_page_handler('avatar', 'elgg_avatar_page_handler');
+ elgg_register_page_handler('profile', 'elgg_profile_page_handler');
+ elgg_register_page_handler('collections', 'collections_page_handler');
- if (!get_input('offset') && (empty($object) || $object == 'user')) {
- if ($users = search_for_user($tag,$threshold)) {
- $countusers = search_for_user($tag,0,0,"",true);
+ elgg_register_plugin_hook_handler('register', 'menu:user_hover', 'elgg_user_hover_menu');
- $return = elgg_view('user/search/startblurb',array('count' => $countusers, 'tag' => $tag));
- foreach($users as $user) {
- $return .= elgg_view_entity($user);
- }
- $return .= elgg_view('user/search/finishblurb',array('count' => $countusers, 'threshold' => $threshold, 'tag' => $tag));
- return $return;
+ elgg_register_action('register', '', 'public');
+ elgg_register_action('useradd', '', 'admin');
+ elgg_register_action('friends/add');
+ elgg_register_action('friends/remove');
+ elgg_register_action('avatar/upload');
+ elgg_register_action('avatar/crop');
+ elgg_register_action('avatar/remove');
+ elgg_register_action('profile/edit');
- }
- }
-}
+ elgg_register_action('friends/collections/add');
+ elgg_register_action('friends/collections/delete');
+ elgg_register_action('friends/collections/edit');
-function users_settings_save() {
- global $CONFIG;
- include($CONFIG->path . "actions/user/name.php");
- include($CONFIG->path . "actions/user/password.php");
- include($CONFIG->path . "actions/email/save.php");
- include($CONFIG->path . "actions/user/language.php");
- include($CONFIG->path . "actions/user/default_access.php");
+ elgg_register_plugin_hook_handler('entity:icon:url', 'user', 'user_avatar_hook');
+
+ elgg_register_action('user/passwordreset', '', 'public');
+ elgg_register_action('user/requestnewpassword', '', 'public');
+
+ elgg_register_widget_type('friends', elgg_echo('friends'), elgg_echo('friends:widget:description'));
+
+ // Register the user type
+ elgg_register_entity_type('user', '');
+
+ elgg_register_plugin_hook_handler('register', 'menu:entity', 'elgg_users_setup_entity_menu', 501);
+
+ elgg_register_event_handler('create', 'user', 'user_create_hook_add_site_relationship');
}
/**
* Runs unit tests for ElggObject
+ *
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of tests
+ * @param mixed $params Params
+ *
+ * @return array
+ * @access private
*/
function users_test($hook, $type, $value, $params) {
global $CONFIG;
@@ -1887,7 +1605,7 @@ function users_test($hook, $type, $value, $params) {
return $value;
}
-//register actions *************************************************************
-register_elgg_event_handler('init','system','users_init',0);
-register_elgg_event_handler('pagesetup','system','users_pagesetup',0);
-register_plugin_hook('unit_test', 'system', 'users_test'); \ No newline at end of file
+elgg_register_event_handler('init', 'system', 'users_init', 0);
+elgg_register_event_handler('init', 'system', 'elgg_profile_fields_setup', 10000); // Ensure this runs after other plugins
+elgg_register_event_handler('pagesetup', 'system', 'users_pagesetup', 0);
+elgg_register_plugin_hook_handler('unit_test', 'system', 'users_test');
diff --git a/engine/lib/usersettings.php b/engine/lib/usersettings.php
deleted file mode 100644
index baaf41562..000000000
--- a/engine/lib/usersettings.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-/**
- * Elgg user settings functions.
- * Functions for adding and manipulating options on the user settings panel.
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-
-/**
- * Register a user settings page with the admin panel.
- * This function extends the view "usersettings/main" with the provided view. This view should provide a description
- * and either a control or a link to.
- *
- * Usage:
- * - To add a control to the main admin panel then extend usersettings/main
- * - To add a control to a new page create a page which renders a view usersettings/subpage (where subpage is your new page -
- * nb. some pages already exist that you can extend), extend the main view to point to it, and add controls to your
- * new view.
- *
- * At the moment this is essentially a wrapper around elgg_extend_view().
- *
- * @param string $new_settings_view The view associated with the control you're adding
- * @param string $view The view to extend, by default this is 'usersettings/main'.
- * @param int $priority Optional priority to govern the appearance in the list.
- */
-function extend_elgg_settings_page( $new_settings_view, $view = 'usersettings/main', $priority = 500) {
- return elgg_extend_view($view, $new_settings_view, $priority);
-}
-
-function usersettings_pagesetup() {
- // Get config
- global $CONFIG;
-
- // Menu options
- if (get_context() == "settings") {
- $user = get_loggedin_user();
- add_submenu_item(elgg_echo('usersettings:user:opt:linktext'),$CONFIG->wwwroot . "pg/settings/user/{$user->username}/");
- add_submenu_item(elgg_echo('usersettings:plugins:opt:linktext'),$CONFIG->wwwroot . "pg/settings/plugins/{$user->username}/");
- add_submenu_item(elgg_echo('usersettings:statistics:opt:linktext'),$CONFIG->wwwroot . "pg/settings/statistics/{$user->username}/");
- }
-}
-
-function usersettings_page_handler($page) {
- global $CONFIG;
-
- $path = $CONFIG->path . "settings/index.php";
-
- if ($page[0]) {
- switch ($page[0]) {
- case 'user' : $path = $CONFIG->path . "settings/user.php"; break;
- case 'statistics' : $path = $CONFIG->path . "settings/statistics.php"; break;
- case 'plugins' : $path = $CONFIG->path . "settings/plugins.php"; break;
- }
- }
-
- if ($page[1]) {
- set_input('username', $page[1]);
- }
-
- include($path);
-}
-
-/**
- * Initialise the admin page.
- */
-function usersettings_init() {
- // Page handler
- register_page_handler('settings','usersettings_page_handler');
-}
-
-/// Register init function
-register_elgg_event_handler('init','system','usersettings_init');
-register_elgg_event_handler('pagesetup','system','usersettings_pagesetup'); \ No newline at end of file
diff --git a/engine/lib/version.php b/engine/lib/version.php
deleted file mode 100644
index 5322e5afa..000000000
--- a/engine/lib/version.php
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-/**
- * Elgg version library.
- * Contains code for handling versioning and upgrades.
- *
- * @package Elgg
- * @subpackage Core
- * @link http://elgg.org/
- */
-
-/**
- * Run any php upgrade scripts which are required
- *
- * @param int $version Version upgrading from.
- * @param bool $quiet Suppress errors. Don't use this.
- */
-function upgrade_code($version, $quiet = FALSE) {
- global $CONFIG;
-
- // Elgg and its database must be installed to upgrade it!
- if (!is_db_installed() || !is_installed()) {
- return FALSE;
- }
-
- $version = (int) $version;
-
- if ($handle = opendir($CONFIG->path . 'engine/lib/upgrades/')) {
- $upgrades = array();
-
- while ($updatefile = readdir($handle)) {
- // Look for upgrades and add to upgrades list
- if (!is_dir($CONFIG->path . 'engine/lib/upgrades/' . $updatefile)) {
- if (preg_match('/^([0-9]{10})\.(php)$/', $updatefile, $matches)) {
- $core_version = (int) $matches[1];
- if ($core_version > $version) {
- $upgrades[] = $updatefile;
- }
- }
- }
- }
-
- // Sort and execute
- asort($upgrades);
-
- if (sizeof($upgrades) > 0) {
- foreach($upgrades as $upgrade) {
- // hide all errors.
- if ($quiet) {
- // hide include errors as well as any exceptions that might happen
- try {
- if (!@include($CONFIG->path . 'engine/lib/upgrades/' . $upgrade)) {
- error_log($e->getmessage());
- }
- } catch (Exception $e) {
- error_log($e->getmessage());
- }
- } else {
- include($CONFIG->path . 'engine/lib/upgrades/' . $upgrade);
- }
- }
- }
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-/**
- * Get the current version information
- *
- * @param true|false $humanreadable Whether to return a human readable version (default: false)
- * @return string|false Depending on success
- */
-function get_version($humanreadable = false) {
- global $CONFIG;
-
- if (include($CONFIG->path . "version.php")) {
- return (!$humanreadable) ? $version : $release;
- }
-
- return FALSE;
-}
-
-/**
- * Determines whether or not the database needs to be upgraded.
- *
- * @return true|false Depending on whether or not the db version matches the code version
- */
-function version_upgrade_check() {
- $dbversion = (int) datalist_get('version');
- $version = get_version();
-
- if ($version > $dbversion) {
- return TRUE;
- }
-
- return FALSE;
-}
-
-/**
- * Upgrades Elgg
- *
- */
-function version_upgrade() {
- $dbversion = (int) datalist_get('version');
-
- // No version number? Oh snap...this is an upgrade from a clean installation < 1.7.
- // Run all upgrades without error reporting and hope for the best.
- // See http://trac.elgg.org/elgg/ticket/1432 for more.
- $quiet = !$dbversion;
-
- // Upgrade database
- if (db_upgrade($dbversion, '', $quiet)) {
- system_message(elgg_echo('upgrade:db'));
- }
-
- // Upgrade core
- if (upgrade_code($dbversion, $quiet)) {
- system_message(elgg_echo('upgrade:core'));
- }
-
- // Now we trigger an event to give the option for plugins to do something
- $upgrade_details = new stdClass;
- $upgrade_details->from = $dbversion;
- $upgrade_details->to = get_version();
-
- trigger_elgg_event('upgrade', 'upgrade', $upgrade_details);
-
- // Update the version
- datalist_set('version', get_version());
-}
diff --git a/engine/lib/views.php b/engine/lib/views.php
index c1c07024a..1142461fe 100644
--- a/engine/lib/views.php
+++ b/engine/lib/views.php
@@ -1,20 +1,74 @@
-<?php
+<?php
/**
- * Provides interfaces for Elgg's views system
- *
- * @package Elgg
+ * Elgg's view system.
+ *
+ * The view system is the primary templating engine in Elgg and renders
+ * all output. Views are short, parameterised PHP scripts for displaying
+ * output that can be regsitered, overridden, or extended. The view type
+ * determines the output format and location of the files that renders the view.
+ *
+ * Elgg uses a two step process to render full output: first
+ * content-specific elements are rendered, then the resulting
+ * content is inserted into a layout and displayed. This makes it
+ * easy to maintain a consistent look on all pages.
+ *
+ * A view corresponds to a single file on the filesystem and the views
+ * name is its directory structure. A file in
+ * <code>mod/plugins/views/default/myplugin/example.php</code>
+ * is called by saying (with the default viewtype):
+ * <code>echo elgg_view('myplugin/example');</code>
+ *
+ * View names that are registered later override those that are
+ * registered earlier. For plugins this corresponds directly
+ * to their load order: views in plugins lower in the list override
+ * those higher in the list.
+ *
+ * Plugin views belong in the views/ directory under an appropriate
+ * viewtype. Views are automatically registered.
+ *
+ * Views can be embedded-you can call a view from within a view.
+ * Views can also be prepended or extended by any other view.
+ *
+ * Any view can extend any other view if registered with
+ * {@link elgg_extend_view()}.
+ *
+ * View types are set by passing $_REQUEST['view']. The view type
+ * 'default' is a standard HTML view. Types can be defined on the fly
+ * and you can get the current view type with {@link get_current_view()}.
+ *
+ * @internal Plugin views are autoregistered before their init functions
+ * are called, so the init order doesn't affect views.
+ *
+ * @internal The file that determines the output of the view is the last
+ * registered by {@link elgg_set_view_location()}.
+ *
+ * @package Elgg.Core
+ * @subpackage Views
+ * @link http://docs.elgg.org/Views
*/
+/**
+ * The view type override.
+ *
+ * @global string $CURRENT_SYSTEM_VIEWTYPE
+ * @see elgg_set_viewtype()
+ */
+global $CURRENT_SYSTEM_VIEWTYPE;
$CURRENT_SYSTEM_VIEWTYPE = "";
/**
- * Override the view mode detection for the elgg view system.
+ * Manually set the viewtype.
+ *
+ * View types are detected automatically. This function allows
+ * you to force subsequent views to use a different viewtype.
*
- * This function will force any further views to be rendered using $viewtype. Remember to call elgg_set_viewtype() with
- * no parameters to reset.
+ * @tip Call elgg_set_viewtype() with no parameter to reset.
*
* @param string $viewtype The view type, e.g. 'rss', or 'default'.
+ *
* @return bool
+ * @link http://docs.elgg.org/Views/Viewtype
+ * @example views/viewtype.php
*/
function elgg_set_viewtype($viewtype = "") {
global $CURRENT_SYSTEM_VIEWTYPE;
@@ -25,48 +79,93 @@ function elgg_set_viewtype($viewtype = "") {
}
/**
- * Return the current view type used by the elgg view system.
+ * Return the current view type.
+ *
+ * View types are automatically detected and can be set with $_REQUEST['view']
+ * or {@link elgg_set_viewtype()}.
*
- * By default, this function will return a value based on the default for your system or from the command line
- * view parameter. However, you may force a given view type by calling elgg_set_viewtype()
+ * @internal View type is determined in this order:
+ * - $CURRENT_SYSTEM_VIEWTYPE Any overrides by {@link elgg_set_viewtype()}
+ * - $CONFIG->view The default view as saved in the DB.
+ * - $_SESSION['view']
*
* @return string The view.
+ * @see elgg_set_viewtype()
+ * @link http://docs.elgg.org/Views
+ * @todo This function's sessions stuff needs rewritten, removed, or explained.
*/
function elgg_get_viewtype() {
global $CURRENT_SYSTEM_VIEWTYPE, $CONFIG;
- $viewtype = NULL;
-
if ($CURRENT_SYSTEM_VIEWTYPE != "") {
return $CURRENT_SYSTEM_VIEWTYPE;
}
- if ((empty($_SESSION['view'])) || ( (trim($CONFIG->view!="")) && ($_SESSION['view']!=$CONFIG->view) )) {
- $_SESSION['view'] = "default";
- // If we have a config default view for this site then use that instead of 'default'
- if (/*(is_installed()) && */(!empty($CONFIG->view)) && (trim($CONFIG->view)!="")) {
- $_SESSION['view'] = $CONFIG->view;
+ $viewtype = get_input('view', '', false);
+ if (is_string($viewtype) && $viewtype !== '') {
+ // only word characters allowed.
+ if (!preg_match('/\W/', $viewtype)) {
+ return $viewtype;
}
}
- if (empty($viewtype) && is_callable('get_input')) {
- $viewtype = get_input('view');
+ if (!empty($CONFIG->view)) {
+ return $CONFIG->view;
}
- if (empty($viewtype)) {
- $viewtype = $_SESSION['view'];
+ return 'default';
+}
+
+/**
+ * Register a view type as valid.
+ *
+ * @param string $view_type The view type to register
+ * @return bool
+ */
+function elgg_register_viewtype($view_type) {
+ global $CONFIG;
+
+ if (!isset($CONFIG->view_types) || !is_array($CONFIG->view_types)) {
+ $CONFIG->view_types = array();
}
- return $viewtype;
+ if (!in_array($view_type, $CONFIG->view_types)) {
+ $CONFIG->view_types[] = $view_type;
+ }
+
+ return true;
}
/**
- * Register a viewtype to fall back to a default view if view does not exist in
- * that viewtype.
+ * Checks if $view_type is valid on this installation.
*
- * This is useful for alternate html viewtypes (such as for mobile devices)
+ * @param string $view_type View type
+ *
+ * @return bool
+ * @since 1.7.2
+ * @access private
+ */
+function elgg_is_valid_view_type($view_type) {
+ global $CONFIG;
+
+ if (!isset($CONFIG->view_types) || !is_array($CONFIG->view_types)) {
+ return FALSE;
+ }
+
+ return in_array($view_type, $CONFIG->view_types);
+}
+
+/**
+ * Register a viewtype to fall back to a default view if a view isn't
+ * found for that viewtype.
+ *
+ * @tip This is useful for alternate html viewtypes (such as for mobile devices).
*
* @param string $viewtype The viewtype to register
+ *
+ * @return void
+ * @since 1.7.2
+ * @example views/viewtype_fallback.php Fallback from mobile to default.
*/
function elgg_register_viewtype_fallback($viewtype) {
global $CONFIG;
@@ -83,10 +182,12 @@ function elgg_register_viewtype_fallback($viewtype) {
}
/**
- * Checks if this viewtype falls back to default
+ * Checks if a viewtype falls back to default.
+ *
+ * @param string $viewtype Viewtype
*
- * @param string $viewtype
* @return boolean
+ * @since 1.7.2
*/
function elgg_does_viewtype_fallback($viewtype) {
global $CONFIG;
@@ -94,16 +195,52 @@ function elgg_does_viewtype_fallback($viewtype) {
if (isset($CONFIG->viewtype) && isset($CONFIG->viewtype->fallback)) {
return in_array($viewtype, $CONFIG->viewtype->fallback);
}
-
+
return FALSE;
}
+/**
+ * Register a view to be available for ajax calls
+ *
+ * @param string $view The view name
+ * @return void
+ * @since 1.8.3
+ */
+function elgg_register_ajax_view($view) {
+ global $CONFIG;
+
+ if (!isset($CONFIG->allowed_ajax_views)) {
+ $CONFIG->allowed_ajax_views = array();
+ }
+
+ $CONFIG->allowed_ajax_views[$view] = true;
+}
+
+/**
+ * Unregister a view for ajax calls
+ *
+ * @param string $view The view name
+ * @return void
+ * @since 1.8.3
+ */
+function elgg_unregister_ajax_view($view) {
+ global $CONFIG;
+
+ if (isset($CONFIG->allowed_ajax_views[$view])) {
+ unset($CONFIG->allowed_ajax_views[$view]);
+ }
+}
/**
- * Return the location of a given view.
+ * Returns the file location for a view.
*
- * @param string $view The view.
+ * @warning This doesn't check if the file exists, but only
+ * constructs (or extracts) the path and returns it.
+ *
+ * @param string $view The view.
* @param string $viewtype The viewtype
+ *
+ * @return string
*/
function elgg_get_view_location($view, $viewtype = '') {
global $CONFIG;
@@ -121,130 +258,246 @@ function elgg_get_view_location($view, $viewtype = '') {
} else {
return $CONFIG->views->locations[$viewtype][$view];
}
+}
- return false;
+/**
+ * Set an alternative base location for a view.
+ *
+ * Views are expected to be in plugin_name/views/. This function can
+ * be used to change that location.
+ *
+ * @internal Core view locations are stored in $CONFIG->viewpath.
+ *
+ * @tip This is useful to optionally register views in a plugin.
+ *
+ * @param string $view The name of the view
+ * @param string $location The base location path
+ * @param string $viewtype The view type
+ *
+ * @return void
+ */
+function elgg_set_view_location($view, $location, $viewtype = '') {
+ global $CONFIG;
+
+ if (empty($viewtype)) {
+ $viewtype = 'default';
+ }
+
+ if (!isset($CONFIG->views)) {
+ $CONFIG->views = new stdClass;
+ }
+
+ if (!isset($CONFIG->views->locations)) {
+ $CONFIG->views->locations = array($viewtype => array($view => $location));
+
+ } else if (!isset($CONFIG->views->locations[$viewtype])) {
+ $CONFIG->views->locations[$viewtype] = array($view => $location);
+
+ } else {
+ $CONFIG->views->locations[$viewtype][$view] = $location;
+ }
}
/**
- * Handles templating views
+ * Returns whether the specified view exists
*
- * @see set_template_handler
+ * @note If $recurse is true, also checks if a view exists only as an extension.
+ *
+ * @param string $view The view name
+ * @param string $viewtype If set, forces the viewtype
+ * @param bool $recurse If false, do not check extensions
*
- * @param string $view The name and location of the view to use
- * @param array $vars Any variables that the view requires, passed as an array
- * @param boolean $bypass If set to true, elgg_view will bypass any specified alternative template handler; by default, it will hand off to this if requested (see set_template_handler)
- * @param boolean $debug If set to true, the viewer will complain if it can't find a view
- * @param string $viewtype If set, forces the viewtype for the elgg_view call to be this value (default: standard detection)
- * @return string The HTML content
+ * @return bool
*/
-function elgg_view($view, $vars = array(), $bypass = false, $debug = false, $viewtype = '') {
+function elgg_view_exists($view, $viewtype = '', $recurse = true) {
global $CONFIG;
- static $usercache;
- $view = (string)$view;
+ // Detect view type
+ if (empty($viewtype)) {
+ $viewtype = elgg_get_viewtype();
+ }
- // basic checking for bad paths
- if (strpos($view, '..') !== false) {
- return false;
+ if (!isset($CONFIG->views->locations[$viewtype][$view])) {
+ if (!isset($CONFIG->viewpath)) {
+ $location = dirname(dirname(dirname(__FILE__))) . "/views/";
+ } else {
+ $location = $CONFIG->viewpath;
+ }
+ } else {
+ $location = $CONFIG->views->locations[$viewtype][$view];
}
- $view_orig = $view;
+ if (file_exists("{$location}{$viewtype}/{$view}.php")) {
+ return true;
+ }
- // Trigger the pagesetup event
- if (!isset($CONFIG->pagesetupdone)) {
- trigger_elgg_event('pagesetup','system');
- $CONFIG->pagesetupdone = true;
+ // If we got here then check whether this exists as an extension
+ // We optionally recursively check whether the extended view exists also for the viewtype
+ if ($recurse && isset($CONFIG->views->extensions[$view])) {
+ foreach ($CONFIG->views->extensions[$view] as $view_extension) {
+ // do not recursively check to stay away from infinite loops
+ if (elgg_view_exists($view_extension, $viewtype, false)) {
+ return true;
+ }
+ }
}
- if (!is_array($usercache)) {
- $usercache = array();
+ // Now check if the default view exists if the view is registered as a fallback
+ if ($viewtype != 'default' && elgg_does_viewtype_fallback($viewtype)) {
+ return elgg_view_exists($view, 'default');
}
- if (!is_array($vars)) {
- elgg_log('Vars in views must be an array!', 'ERROR');
- $vars = array();
+ return false;
+}
+
+/**
+ * Return a parsed view.
+ *
+ * Views are rendered by a template handler and returned as strings.
+ *
+ * Views are called with a special $vars variable set,
+ * which includes any variables passed as the second parameter.
+ * For backward compatbility, the following variables are also set but we
+ * recommend that you do not use them:
+ * - $vars['config'] The $CONFIG global. (Use {@link elgg_get_config()} instead).
+ * - $vars['url'] The site URL. (use {@link elgg_get_site_url()} instead).
+ * - $vars['user'] The logged in user. (use {@link elgg_get_logged_in_user_entity()} instead).
+ *
+ * Custom template handlers can be set with {@link set_template_handler()}.
+ *
+ * The output of views can be intercepted by registering for the
+ * view, $view_name plugin hook.
+ *
+ * @warning Any variables in $_SESSION will override passed vars
+ * upon name collision. See https://github.com/Elgg/Elgg/issues/2124
+ *
+ * @param string $view The name and location of the view to use
+ * @param array $vars Variables to pass to the view.
+ * @param boolean $bypass If set to true, elgg_view will bypass any specified
+ * alternative template handler; by default, it will
+ * hand off to this if requested (see set_template_handler)
+ * @param boolean $ignored This argument is ignored and will be removed eventually
+ * @param string $viewtype If set, forces the viewtype for the elgg_view call to be
+ * this value (default: standard detection)
+ *
+ * @return string The parsed view
+ * @see set_template_handler()
+ * @example views/elgg_view.php
+ * @link http://docs.elgg.org/View
+ */
+function elgg_view($view, $vars = array(), $bypass = false, $ignored = false, $viewtype = '') {
+ global $CONFIG;
+
+ if (!is_string($view) || !is_string($viewtype)) {
+ elgg_log("View and Viewtype in views must be a strings: $view", 'NOTICE');
+ return '';
+ }
+ // basic checking for bad paths
+ if (strpos($view, '..') !== false) {
+ return '';
}
- if (empty($vars)) {
+ if (!is_array($vars)) {
+ elgg_log("Vars in views must be an array: $view", 'ERROR');
$vars = array();
}
- // Load session and configuration variables into $vars
- // $_SESSION will always be an array if it is set
- if (isset($_SESSION) /*&& is_array($_SESSION)*/ ) {
- //= array_merge($vars, $_SESSION);
- $vars += $_SESSION;
+ // Get the current viewtype
+ if ($viewtype === '') {
+ $viewtype = elgg_get_viewtype();
+ } elseif (preg_match('/\W/', $viewtype)) {
+ // Viewtypes can only be alphanumeric
+ return '';
}
- $vars['config'] = array();
+ $view_orig = $view;
- if (!empty($CONFIG)) {
- $vars['config'] = $CONFIG;
+ // Trigger the pagesetup event
+ if (!isset($CONFIG->pagesetupdone) && $CONFIG->boot_complete) {
+ $CONFIG->pagesetupdone = true;
+ elgg_trigger_event('pagesetup', 'system');
}
- $vars['url'] = $CONFIG->url;
+ // @warning - plugin authors: do not expect user, config, and url to be
+ // set by elgg_view() in the future. Instead, use elgg_get_logged_in_user_entity(),
+ // elgg_get_config(), and elgg_get_site_url() in your views.
+ if (!isset($vars['user'])) {
+ $vars['user'] = elgg_get_logged_in_user_entity();
+ }
+ if (!isset($vars['config'])) {
+ $vars['config'] = $CONFIG;
+ }
+ if (!isset($vars['url'])) {
+ $vars['url'] = elgg_get_site_url();
+ }
- // Load page owner variables into $vars
- if (is_callable('page_owner')) {
- $vars['page_owner'] = page_owner();
- } else {
- $vars['page_owner'] = -1;
+ // full_view is the new preferred key for full view on entities @see elgg_view_entity()
+ // check if full_view is set because that means we've already rewritten it and this is
+ // coming from another view passing $vars directly.
+ if (isset($vars['full']) && !isset($vars['full_view'])) {
+ elgg_deprecated_notice("Use \$vars['full_view'] instead of \$vars['full']", 1.8, 2);
+ $vars['full_view'] = $vars['full'];
+ }
+ if (isset($vars['full_view'])) {
+ $vars['full'] = $vars['full_view'];
}
- if (($vars['page_owner'] != -1) && (is_installed())) {
- if (!isset($usercache[$vars['page_owner']])) {
- $vars['page_owner_user'] = get_entity($vars['page_owner']);
- $usercache[$vars['page_owner']] = $vars['page_owner_user'];
- } else {
- $vars['page_owner_user'] = $usercache[$vars['page_owner']];
+ // internalname => name (1.8)
+ if (isset($vars['internalname']) && !isset($vars['__ignoreInternalname']) && !isset($vars['name'])) {
+ elgg_deprecated_notice('You should pass $vars[\'name\'] now instead of $vars[\'internalname\']', 1.8, 2);
+ $vars['name'] = $vars['internalname'];
+ } elseif (isset($vars['name'])) {
+ if (!isset($vars['internalname'])) {
+ $vars['__ignoreInternalname'] = '';
}
+ $vars['internalname'] = $vars['name'];
}
- if (!isset($vars['js'])) {
- $vars['js'] = "";
+ // internalid => id (1.8)
+ if (isset($vars['internalid']) && !isset($vars['__ignoreInternalid']) && !isset($vars['name'])) {
+ elgg_deprecated_notice('You should pass $vars[\'id\'] now instead of $vars[\'internalid\']', 1.8, 2);
+ $vars['id'] = $vars['internalid'];
+ } elseif (isset($vars['id'])) {
+ if (!isset($vars['internalid'])) {
+ $vars['__ignoreInternalid'] = '';
+ }
+ $vars['internalid'] = $vars['id'];
}
// If it's been requested, pass off to a template handler instead
if ($bypass == false && isset($CONFIG->template_handler) && !empty($CONFIG->template_handler)) {
$template_handler = $CONFIG->template_handler;
if (is_callable($template_handler)) {
- return $template_handler($view, $vars);
+ return call_user_func($template_handler, $view, $vars);
}
}
- // Get the current viewtype
- if (empty($viewtype)) {
- $viewtype = elgg_get_viewtype();
- }
-
- // Viewtypes can only be alphanumeric
- if (preg_match('[\W]', $viewtype)) {
- return '';
- }
-
// Set up any extensions to the requested view
if (isset($CONFIG->views->extensions[$view])) {
$viewlist = $CONFIG->views->extensions[$view];
} else {
$viewlist = array(500 => $view);
}
+
// Start the output buffer, find the requested view file, and execute it
ob_start();
- foreach($viewlist as $priority => $view) {
+ foreach ($viewlist as $priority => $view) {
+
$view_location = elgg_get_view_location($view, $viewtype);
$view_file = "$view_location$viewtype/$view.php";
-
- $default_location = elgg_get_view_location($view, 'default');
- $default_view_file = "{$default_location}default/$view.php";
-
+
// try to include view
if (!file_exists($view_file) || !include($view_file)) {
// requested view does not exist
$error = "$viewtype/$view view does not exist.";
// attempt to load default view
- if ($viewtype != 'default' && elgg_does_viewtype_fallback($viewtype)) {
+ if ($viewtype !== 'default' && elgg_does_viewtype_fallback($viewtype)) {
+
+ $default_location = elgg_get_view_location($view, 'default');
+ $default_view_file = "{$default_location}default/$view.php";
+
if (file_exists($default_view_file) && include($default_view_file)) {
// default view found
$error .= " Using default/$view instead.";
@@ -263,337 +516,350 @@ function elgg_view($view, $vars = array(), $bypass = false, $debug = false, $vie
$content = ob_get_clean();
// Plugin hook
- $content = trigger_plugin_hook('view', $view_orig,
- array('view' => $view_orig, 'vars' => $vars), $content);
+ $params = array('view' => $view_orig, 'vars' => $vars, 'viewtype' => $viewtype);
+ $content = elgg_trigger_plugin_hook('view', $view_orig, $params, $content);
- // backward compatibility with less grandular hook will be gone in 2.0
- $content_tmp = trigger_plugin_hook('display', 'view', array('view' => $view_orig, 'vars' => $vars), $content);
+ // backward compatibility with less granular hook will be gone in 2.0
+ $content_tmp = elgg_trigger_plugin_hook('display', 'view', $params, $content);
- if ($content_tmp != $content) {
+ if ($content_tmp !== $content) {
$content = $content_tmp;
- elgg_deprecated_notice('The display:view plugin hook is deprecated by view:view_name or view:all', 1.8);
+ elgg_deprecated_notice('The display:view plugin hook is deprecated by view:view_name', 1.8);
}
- // Return $content
return $content;
}
/**
- * Returns whether the specified view exists
+ * Extends a view with another view.
*
- * @param string $view The view name
- * @param string $viewtype If set, forces the viewtype
- * @param bool $recurse If false, do not recursively check extensions
- * @return true|false Depending on success
+ * The output of any view can be prepended or appended to any other view.
+ *
+ * The default action is to append a view. If the priority is less than 500,
+ * the output of the extended view will be appended to the original view.
+ *
+ * Priority can be specified and affects the order in which extensions
+ * are appended or prepended.
+ *
+ * @internal View extensions are stored in
+ * $CONFIG->views->extensions[$view][$priority] = $view_extension
+ *
+ * @param string $view The view to extend.
+ * @param string $view_extension This view is added to $view
+ * @param int $priority The priority, from 0 to 1000,
+ * to add at (lowest numbers displayed first)
+ *
+ * @return void
+ * @since 1.7.0
+ * @link http://docs.elgg.org/Views/Extend
+ * @example views/extend.php
*/
-function elgg_view_exists($view, $viewtype = '', $recurse = true) {
+function elgg_extend_view($view, $view_extension, $priority = 501) {
global $CONFIG;
- // Detect view type
- if (empty($viewtype)) {
- $viewtype = elgg_get_viewtype();
- }
-
- if (!isset($CONFIG->views->locations[$viewtype][$view])) {
- if (!isset($CONFIG->viewpath)) {
- $location = dirname(dirname(dirname(__FILE__))) . "/views/";
- } else {
- $location = $CONFIG->viewpath;
- }
+ if (!isset($CONFIG->views)) {
+ $CONFIG->views = (object) array(
+ 'extensions' => array(),
+ );
+ $CONFIG->views->extensions[$view][500] = (string)$view;
} else {
- $location = $CONFIG->views->locations[$viewtype][$view];
- }
-
- if (file_exists($location . "{$viewtype}/{$view}.php")) {
- return true;
+ if (!isset($CONFIG->views->extensions[$view])) {
+ $CONFIG->views->extensions[$view][500] = (string)$view;
+ }
}
- // If we got here then check whether this exists as an extension
- // We optionally recursively check whether the extended view exists also for the viewtype
- if ($recurse && isset($CONFIG->views->extensions[$view])) {
- foreach( $CONFIG->views->extensions[$view] as $view_extension ) {
- // do not recursively check to stay away from infinite loops
- if (elgg_view_exists($view_extension, $viewtype, false)) {
- return true;
- }
- }
+ // raise priority until it doesn't match one already registered
+ while (isset($CONFIG->views->extensions[$view][$priority])) {
+ $priority++;
}
- return false;
+ $CONFIG->views->extensions[$view][$priority] = (string)$view_extension;
+ ksort($CONFIG->views->extensions[$view]);
}
/**
- * Registers a view to be simply cached
- *
- * Views cached in this manner must take no parameters and be login agnostic -
- * that is to say, they look the same no matter who is logged in (or logged out).
+ * Unextends a view.
*
- * CSS and the basic jS views are automatically cached like this.
+ * @param string $view The view that was extended.
+ * @param string $view_extension This view that was added to $view
*
- * @param string $viewname View name
+ * @return bool
+ * @since 1.7.2
*/
-function elgg_view_register_simplecache($viewname) {
+function elgg_unextend_view($view, $view_extension) {
global $CONFIG;
- if (!isset($CONFIG->views)) {
- $CONFIG->views = new stdClass;
+ if (!isset($CONFIG->views->extensions[$view])) {
+ return FALSE;
}
- if (!isset($CONFIG->views->simplecache)) {
- $CONFIG->views->simplecache = array();
+ $priority = array_search($view_extension, $CONFIG->views->extensions[$view]);
+ if ($priority === FALSE) {
+ return FALSE;
}
- //if (elgg_view_exists($viewname))
- $CONFIG->views->simplecache[] = $viewname;
+ unset($CONFIG->views->extensions[$view][$priority]);
+
+ return TRUE;
}
/**
- * Regenerates the simple cache.
+ * Assembles and outputs a full page.
+ *
+ * A "page" in Elgg is determined by the current view type and
+ * can be HTML for a browser, RSS for a feed reader, or
+ * Javascript, PHP and a number of other formats.
*
- * @param string $viewtype Optional viewtype to regenerate
- * @see elgg_view_register_simplecache
+ * @param string $title Title
+ * @param string $body Body
+ * @param string $page_shell Optional page shell to use. See page/shells view directory
+ * @param array $vars Optional vars array to pass to the page
+ * shell. Automatically adds title, body, and sysmessages
*
+ * @return string The contents of the page
+ * @since 1.8
*/
-function elgg_view_regenerate_simplecache($viewtype = NULL) {
- global $CONFIG;
-
- if (!isset($CONFIG->views->simplecache) || !is_array($CONFIG->views->simplecache)) {
- return;
- }
-
- $lastcached = time();
-
- // @todo elgg_view() checks if the page set is done (isset($CONFIG->pagesetupdone)) and
- // triggers an event if it's not. Calling elgg_view() here breaks submenus
- // (at least) because the page setup hook is called before any
- // contexts can be correctly set (since this is called before page_handler()).
- // To avoid this, lie about $CONFIG->pagehandlerdone to force
- // the trigger correctly when the first view is actually being output.
- $CONFIG->pagesetupdone = TRUE;
-
- if (!file_exists($CONFIG->dataroot . 'views_simplecache')) {
- mkdir($CONFIG->dataroot . 'views_simplecache');
+function elgg_view_page($title, $body, $page_shell = 'default', $vars = array()) {
+
+ $messages = null;
+ if (count_messages()) {
+ // get messages - try for errors first
+ $messages = system_messages(NULL, "error");
+ if (count($messages["error"]) == 0) {
+ // no errors so grab rest of messages
+ $messages = system_messages(null, "");
+ } else {
+ // we have errors - clear out remaining messages
+ system_messages(null, "");
+ }
}
- if (isset($viewtype)) {
- $viewtypes = array($viewtype);
- } else {
- $viewtypes = $CONFIG->view_types;
- }
+ $vars['title'] = $title;
+ $vars['body'] = $body;
+ $vars['sysmessages'] = $messages;
- $original_viewtype = elgg_get_viewtype();
+ $vars = elgg_trigger_plugin_hook('output:before', 'page', null, $vars);
- foreach ($viewtypes as $viewtype) {
- elgg_set_viewtype($viewtype);
- foreach ($CONFIG->views->simplecache as $view) {
- $viewcontents = elgg_view($view);
- $viewname = md5(elgg_get_viewtype() . $view);
- if ($handle = fopen($CONFIG->dataroot . 'views_simplecache/' . $viewname, 'w')) {
- fwrite($handle, $viewcontents);
- fclose($handle);
- }
- }
-
- datalist_set("simplecache_lastupdate_$viewtype", $lastcached);
- datalist_set("simplecache_lastcached_$viewtype", $lastcached);
+ // check for deprecated view
+ if ($page_shell == 'default' && elgg_view_exists('pageshells/pageshell')) {
+ elgg_deprecated_notice("pageshells/pageshell is deprecated by page/$page_shell", 1.8);
+ $output = elgg_view('pageshells/pageshell', $vars);
+ } else {
+ $output = elgg_view("page/$page_shell", $vars);
}
- elgg_set_viewtype($original_viewtype);
-
- // needs to be set for links in html head
- $CONFIG->lastcache = $lastcached;
+ $vars['page_shell'] = $page_shell;
- unset($CONFIG->pagesetupdone);
+ // Allow plugins to mod output
+ return elgg_trigger_plugin_hook('output', 'page', $vars, $output);
}
/**
- * Enables the simple cache.
+ * Displays a layout with optional parameters.
*
- * @see elgg_view_register_simplecache
+ * Layouts provide consistent organization of pages and other blocks of content.
+ * There are a few default layouts in core:
+ * - admin A special layout for the admin area.
+ * - one_column A single content column.
+ * - one_sidebar A content column with sidebar.
+ * - two_sidebar A content column with two sidebars.
+ * - widgets A widget canvas.
*
- */
-
-function elgg_view_enable_simplecache() {
- global $CONFIG;
-
- datalist_set('simplecache_enabled',1);
- $CONFIG->simplecache_enabled = 1;
- elgg_view_regenerate_simplecache();
-}
-
-/**
- * Disables the simple cache.
+ * The layout views take the form page/layouts/$layout_name
+ * See the individual layouts for what options are supported. The three most
+ * common layouts have these parameters:
+ * one_column
+ * content => string
+ * one_sidebar
+ * content => string
+ * sidebar => string (optional)
+ * content
+ * content => string
+ * sidebar => string (optional)
+ * buttons => string (override the default add button)
+ * title => string (override the default title)
+ * filter_context => string (selected content filter)
+ * See the content layout view for more parameters
*
- * @see elgg_view_register_simplecache
+ * @param string $layout_name The name of the view in page/layouts/.
+ * @param array $vars Associative array of parameters for the layout view
*
+ * @return string The layout
*/
-function elgg_view_disable_simplecache() {
- global $CONFIG;
- if ($CONFIG->simplecache_enabled) {
- datalist_set('simplecache_enabled',0);
- $CONFIG->simplecache_enabled = 0;
-
- // purge simple cache
- if ($handle = opendir($CONFIG->dataroot.'views_simplecache')) {
- while (false !== ($file = readdir($handle))) {
- if ($file != "." && $file != "..") {
- unlink($CONFIG->dataroot.'views_simplecache/'.$file);
- }
- }
- closedir($handle);
+function elgg_view_layout($layout_name, $vars = array()) {
+
+ if (is_string($vars) || $vars === null) {
+ elgg_deprecated_notice("The use of unlimited optional string arguments in elgg_view_layout() was deprecated in favor of an options array", 1.8);
+ $arg = 1;
+ $param_array = array();
+ while ($arg < func_num_args()) {
+ $param_array['area' . $arg] = func_get_arg($arg);
+ $arg++;
}
+ } else {
+ $param_array = $vars;
}
-}
+ $params = elgg_trigger_plugin_hook('output:before', 'layout', null, $param_array);
-/**
- * Internal function for retrieving views used by elgg_view_tree
- *
- * @param unknown_type $dir
- * @param unknown_type $base
- * @return unknown
- */
-function elgg_get_views($dir, $base) {
- $return = array();
- if (file_exists($dir) && is_dir($dir)) {
- if ($handle = opendir($dir)) {
- while ($view = readdir($handle)) {
- if (!in_array($view, array('.','..','.svn','CVS'))) {
- if (is_dir($dir . '/' . $view)) {
- if ($val = elgg_get_views($dir . '/' . $view, $base . '/' . $view)) {
- $return = array_merge($return, $val);
- }
- } else {
- $view = str_replace('.php','',$view);
- $return[] = $base . '/' . $view;
- }
- }
- }
- }
+ // check deprecated location
+ if (elgg_view_exists("canvas/layouts/$layout_name")) {
+ elgg_deprecated_notice("canvas/layouts/$layout_name is deprecated by page/layouts/$layout_name", 1.8);
+ $output = elgg_view("canvas/layouts/$layout_name", $params);
+ } elseif (elgg_view_exists("page/layouts/$layout_name")) {
+ $output = elgg_view("page/layouts/$layout_name", $params);
+ } else {
+ $output = elgg_view("page/layouts/default", $params);
}
- return $return;
-}
-/**
- * @deprecated 1.7. Use elgg_get_views().
- * @param $dir
- * @param $base
- */
-function get_views($dir, $base) {
- elgg_deprecated_notice('get_views() was deprecated by elgg_get_views()!', 1.7);
- elgg_get_views($dir, $base);
+ return elgg_trigger_plugin_hook('output:after', 'layout', $params, $output);
}
/**
- * When given a partial view root (eg 'js' or 'page_elements'), returns an array of views underneath it
+ * Render a menu
*
- * @param string $view_root The root view
- * @param string $viewtype Optionally specify a view type other than the current one.
- * @return array A list of view names underneath that root view
+ * @see elgg_register_menu_item() for documentation on adding menu items and
+ * navigation.php for information on the different menus available.
+ *
+ * This function triggers a 'register', 'menu:<menu name>' plugin hook that enables
+ * plugins to add menu items just before a menu is rendered. This is used by
+ * dynamic menus (menus that change based on some input such as the user hover
+ * menu). Using elgg_register_menu_item() in response to the hook can cause
+ * incorrect links to show up. See the blog plugin's blog_owner_block_menu()
+ * for an example of using this plugin hook.
+ *
+ * An additional hook is the 'prepare', 'menu:<menu name>' which enables plugins
+ * to modify the structure of the menu (sort it, remove items, set variables on
+ * the menu items).
+ *
+ * elgg_view_menu() uses views in navigation/menu
+ *
+ * @param string $menu_name The name of the menu
+ * @param array $vars An associative array of display options for the menu.
+ * Options include:
+ * sort_by => string or php callback
+ * string options: 'name', 'priority', 'title' (default),
+ * 'register' (registration order) or a
+ * php callback (a compare function for usort)
+ * handler: string the page handler to build action URLs
+ * entity: ElggEntity to use to build action URLs
+ * class: string the class for the entire menu.
+ * show_section_headers: bool show headers before menu sections.
+ *
+ * @return string
+ * @since 1.8.0
*/
-function elgg_view_tree($view_root, $viewtype = "") {
+function elgg_view_menu($menu_name, array $vars = array()) {
global $CONFIG;
- static $treecache;
- // Get viewtype
- if (!$viewtype) {
- $viewtype = elgg_get_viewtype();
- }
+ $vars['name'] = $menu_name;
- // Has the treecache been initialised?
- if (!isset($treecache)) {
- $treecache = array();
- }
- // A little light internal caching
- if (!empty($treecache[$view_root])) {
- return $treecache[$view_root];
- }
+ $sort_by = elgg_extract('sort_by', $vars, 'text');
- // Examine $CONFIG->views->locations
- if (isset($CONFIG->views->locations[$viewtype])) {
- foreach($CONFIG->views->locations[$viewtype] as $view => $path) {
- $pos = strpos($view,$view_root);
- if ($pos === 0) {
- $treecache[$view_root][] = $view;
- }
- }
+ if (isset($CONFIG->menus[$menu_name])) {
+ $menu = $CONFIG->menus[$menu_name];
+ } else {
+ $menu = array();
}
- // Now examine core
- $location = $CONFIG->viewpath;
- $viewtype = elgg_get_viewtype();
- $root = $location . $viewtype . '/' . $view_root;
+ // Give plugins a chance to add menu items just before creation.
+ // This supports dynamic menus (example: user_hover).
+ $menu = elgg_trigger_plugin_hook('register', "menu:$menu_name", $vars, $menu);
- if (file_exists($root) && is_dir($root)) {
- $val = elgg_get_views($root, $view_root);
- if (!is_array($treecache[$view_root])) {
- $treecache[$view_root] = array();
- }
- $treecache[$view_root] = array_merge($treecache[$view_root], $val);
- }
+ $builder = new ElggMenuBuilder($menu);
+ $vars['menu'] = $builder->getMenu($sort_by);
+ $vars['selected_item'] = $builder->getSelected();
- return $treecache[$view_root];
+ // Let plugins modify the menu
+ $vars['menu'] = elgg_trigger_plugin_hook('prepare', "menu:$menu_name", $vars, $vars['menu']);
+
+ if (elgg_view_exists("navigation/menu/$menu_name")) {
+ return elgg_view("navigation/menu/$menu_name", $vars);
+ } else {
+ return elgg_view("navigation/menu/default", $vars);
+ }
}
/**
- * When given an entity, views it intelligently.
+ * Returns a string of a rendered entity.
*
- * Expects a view to exist called entity-type/subtype, or for the entity to have a parameter
- * 'view' which lists a different view to display. In both cases, elgg_view will be called with
- * array('entity' => $entity, 'full' => $full) as its parameters, and therefore this is what
- * the view should expect to receive.
+ * Entity views are either determined by setting the view property on the entity
+ * or by having a view named after the entity $type/$subtype. Entities that have
+ * neither a view property nor a defined $type/$subtype view will fall back to
+ * using the $type/default view.
+ *
+ * The entity view is called with the following in $vars:
+ * - ElggEntity 'entity' The entity being viewed
+ *
+ * Other common view $vars paramters:
+ * - bool 'full_view' Whether to show a full or condensed view.
+ *
+ * @tip This function can automatically appends annotations to entities if in full
+ * view and a handler is registered for the entity:annotate. See https://github.com/Elgg/Elgg/issues/964 and
+ * {@link elgg_view_entity_annotations()}.
*
* @param ElggEntity $entity The entity to display
- * @param boolean $full Determines whether or not to display the full version of an object, or a smaller version for use in aggregators etc
- * @param boolean $bypass If set to true, elgg_view will bypass any specified alternative template handler; by default, it will hand off to this if requested (see set_template_handler)
- * @param boolean $debug If set to true, the viewer will complain if it can't find a view
+ * @param array $vars Array of variables to pass to the entity view.
+ * In Elgg 1.7 and earlier it was the boolean $full_view
+ * @param boolean $bypass If false, will not pass to a custom template handler.
+ * {@see set_template_handler()}
+ * @param boolean $debug Complain if views are missing
+ *
* @return string HTML to display or false
+ * @link http://docs.elgg.org/Views/Entity
+ * @link http://docs.elgg.org/Entities
+ * @todo The annotation hook might be better as a generic plugin hook to append content.
*/
-function elgg_view_entity(ElggEntity $entity, $full = false, $bypass = true, $debug = false) {
- global $autofeed;
- $autofeed = true;
+function elgg_view_entity(ElggEntity $entity, $vars = array(), $bypass = true, $debug = false) {
// No point continuing if entity is null
- if (!$entity) {
- return '';
+ if (!$entity || !($entity instanceof ElggEntity)) {
+ return false;
}
- if (!($entity instanceof ElggEntity)) {
- return false;
+ global $autofeed;
+ $autofeed = true;
+
+ $defaults = array(
+ 'full_view' => false,
+ );
+
+ if (is_array($vars)) {
+ $vars = array_merge($defaults, $vars);
+ } else {
+ elgg_deprecated_notice("Update your use of elgg_view_entity()", 1.8);
+ $vars = array(
+ 'full_view' => $vars,
+ );
}
+ $vars['entity'] = $entity;
+
+
// if this entity has a view defined, use it
$view = $entity->view;
if (is_string($view)) {
- return elgg_view($view,
- array('entity' => $entity, 'full' => $full),
- $bypass,
- $debug);
+ return elgg_view($view, $vars, $bypass, $debug);
}
$entity_type = $entity->getType();
$subtype = $entity->getSubtype();
if (empty($subtype)) {
- $subtype = $entity_type;
+ $subtype = 'default';
}
$contents = '';
- if (elgg_view_exists("{$entity_type}/{$subtype}")) {
- $contents = elgg_view("{$entity_type}/{$subtype}", array(
- 'entity' => $entity,
- 'full' => $full
- ), $bypass, $debug);
+ if (elgg_view_exists("$entity_type/$subtype")) {
+ $contents = elgg_view("$entity_type/$subtype", $vars, $bypass, $debug);
}
if (empty($contents)) {
- $contents = elgg_view("{$entity_type}/default",array(
- 'entity' => $entity,
- 'full' => $full
- ), $bypass, $debug);
+ $contents = elgg_view("$entity_type/default", $vars, $bypass, $debug);
}
+
// Marcus Povey 20090616 : Speculative and low impact approach for fixing #964
- if ($full) {
- $annotations = elgg_view_entity_annotations($entity, $full);
+ if ($vars['full_view']) {
+ $annotations = elgg_view_entity_annotations($entity, $vars['full_view']);
if ($annotations) {
$contents .= $annotations;
@@ -603,163 +869,242 @@ function elgg_view_entity(ElggEntity $entity, $full = false, $bypass = true, $de
}
/**
- * When given an annotation, views it intelligently.
+ * View the icon of an entity
+ *
+ * Entity views are determined by having a view named after the entity $type/$subtype.
+ * Entities that do not have a defined icon/$type/$subtype view will fall back to using
+ * the icon/$type/default view.
*
- * This function expects annotation views to be of the form annotation/name, where name
- * is the type of annotation.
+ * @param ElggEntity $entity The entity to display
+ * @param string $size The size: tiny, small, medium, large
+ * @param array $vars An array of variables to pass to the view. Some possible
+ * variables are img_class and link_class. See the
+ * specific icon view for more parameters.
+ *
+ * @return string HTML to display or false
+ */
+function elgg_view_entity_icon(ElggEntity $entity, $size = 'medium', $vars = array()) {
+
+ // No point continuing if entity is null
+ if (!$entity || !($entity instanceof ElggEntity)) {
+ return false;
+ }
+
+ $vars['entity'] = $entity;
+ $vars['size'] = $size;
+
+ $entity_type = $entity->getType();
+
+ $subtype = $entity->getSubtype();
+ if (empty($subtype)) {
+ $subtype = 'default';
+ }
+
+ $contents = '';
+ if (elgg_view_exists("icon/$entity_type/$subtype")) {
+ $contents = elgg_view("icon/$entity_type/$subtype", $vars);
+ }
+ if (empty($contents)) {
+ $contents = elgg_view("icon/$entity_type/default", $vars);
+ }
+ if (empty($contents)) {
+ $contents = elgg_view("icon/default", $vars);
+ }
+
+ return $contents;
+}
+
+/**
+ * Returns a string of a rendered annotation.
+ *
+ * Annotation views are expected to be in annotation/$annotation_name.
+ * If a view is not found for $annotation_name, the default annotation/default
+ * will be used.
+ *
+ * @warning annotation/default is not currently defined in core.
+ *
+ * The annotation view is called with the following in $vars:
+ * - ElggEntity 'annotation' The annotation being viewed.
*
* @param ElggAnnotation $annotation The annotation to display
- * @param boolean $full Determines whether or not to display the full version of an object, or a smaller version for use in aggregators etc
- * @param boolean $bypass If set to true, elgg_view will bypass any specified alternative template handler; by default, it will hand off to this if requested (see set_template_handler)
- * @param boolean $debug If set to true, the viewer will complain if it can't find a view
- * @return string HTML (etc) to display
+ * @param array $vars Variable array for view.
+ * @param bool $bypass If false, will not pass to a custom
+ * template handler. {@see set_template_handler()}
+ * @param bool $debug Complain if views are missing
+ *
+ * @return string/false Rendered annotation
*/
-function elgg_view_annotation(ElggAnnotation $annotation, $bypass = true, $debug = false) {
+function elgg_view_annotation(ElggAnnotation $annotation, array $vars = array(), $bypass = true, $debug = false) {
global $autofeed;
$autofeed = true;
+ $defaults = array(
+ 'full_view' => true,
+ );
+
+ $vars = array_merge($defaults, $vars);
+ $vars['annotation'] = $annotation;
+
+ // @todo setting the view on an annotation is not advertised anywhere
+ // do we want to keep this?
$view = $annotation->view;
if (is_string($view)) {
- return elgg_view($view,array('annotation' => $annotation), $bypass, $debug);
+ return elgg_view($view, $vars, $bypass, $debug);
}
$name = $annotation->name;
- $intname = (int) $name;
- if ("{$intname}" == "{$name}") {
- $name = get_metastring($intname);
- }
if (empty($name)) {
- return "";
+ return false;
}
- if (elgg_view_exists("annotation/{$name}")) {
- return elgg_view("annotation/{$name}",array('annotation' => $annotation), $bypass, $debug);
+ if (elgg_view_exists("annotation/$name")) {
+ return elgg_view("annotation/$name", $vars, $bypass, $debug);
} else {
- return elgg_view("annotation/default",array('annotation' => $annotation), $bypass, $debug);
+ return elgg_view("annotation/default", $vars, $bypass, $debug);
}
}
-
/**
- * Returns a view of a list of entities, plus navigation. It is intended that this function
- * be called from other wrapper functions.
- *
- * @see list_entities
- * @see list_user_objects
- * @see list_user_friends_objects
- * @see list_entities_from_metadata
- * @see list_entities_from_metadata_multi
- * @see list_entities_from_relationships
- * @see list_site_members
- *
- * @param array $entities List of entities
- * @param int $count The total number of entities across all pages
- * @param int $offset The current indexing offset
- * @param int $limit The number of entities to display per page
- * @param true|false $fullview Whether or not to display the full view (default: true)
- * @param true|false $viewtypetoggle Whether or not to allow users to toggle to gallery view
- * @param bool $pagination Whether pagination is offered.
- * @return string The list of entities
+ * Returns a rendered list of entities with pagination. This function should be
+ * called by wrapper functions.
+ *
+ * @see elgg_list_entities()
+ * @see list_user_friends_objects()
+ * @see elgg_list_entities_from_metadata()
+ * @see elgg_list_entities_from_relationships()
+ * @see elgg_list_entities_from_annotations()
+ *
+ * @param array $entities Array of entities
+ * @param array $vars Display variables
+ * 'count' The total number of entities across all pages
+ * 'offset' The current indexing offset
+ * 'limit' The number of entities to display per page
+ * 'full_view' Display the full view of the entities?
+ * 'list_class' CSS class applied to the list
+ * 'item_class' CSS class applied to the list items
+ * 'pagination' Display pagination?
+ * 'list_type' List type: 'list' (default), 'gallery'
+ * 'list_type_toggle' Display the list type toggle?
+ *
+ * @return string The rendered list of entities
+ * @access private
*/
-function elgg_view_entity_list($entities, $count, $offset, $limit, $fullview = true, $viewtypetoggle = true, $pagination = true) {
- $count = (int) $count;
- $limit = (int) $limit;
-
- // do not require views to explicitly pass in the offset
- if (!$offset = (int) $offset) {
- $offset = sanitise_int(get_input('offset', 0));
- }
-
- $context = get_context();
-
- $html = elgg_view('entities/entity_list',array(
- 'entities' => $entities,
- 'count' => $count,
- 'offset' => $offset,
- 'limit' => $limit,
- 'baseurl' => $_SERVER['REQUEST_URI'],
- 'fullview' => $fullview,
- 'context' => $context,
- 'viewtypetoggle' => $viewtypetoggle,
- 'viewtype' => get_input('search_viewtype','list'),
- 'pagination' => $pagination
- ));
-
- return $html;
+function elgg_view_entity_list($entities, $vars = array(), $offset = 0, $limit = 10, $full_view = true,
+$list_type_toggle = true, $pagination = true) {
+
+ if (!$vars["limit"] && !$vars["offset"]) {
+ // no need for pagination if listing is unlimited
+ $vars["pagination"] = false;
+ }
+
+ if (!is_int($offset)) {
+ $offset = (int)get_input('offset', 0);
+ }
+
+ // list type can be passed as request parameter
+ $list_type = get_input('list_type', 'list');
+ if (get_input('listtype')) {
+ elgg_deprecated_notice("'listtype' has been deprecated by 'list_type' for lists", 1.8);
+ $list_type = get_input('listtype');
+ }
+
+ if (is_array($vars)) {
+ // new function
+ $defaults = array(
+ 'items' => $entities,
+ 'list_class' => 'elgg-list-entity',
+ 'full_view' => true,
+ 'pagination' => true,
+ 'list_type' => $list_type,
+ 'list_type_toggle' => false,
+ 'offset' => $offset,
+ );
+
+ $vars = array_merge($defaults, $vars);
+
+ } else {
+ // old function parameters
+ elgg_deprecated_notice("Please update your use of elgg_view_entity_list()", 1.8);
+
+ $vars = array(
+ 'items' => $entities,
+ 'count' => (int) $vars, // the old count parameter
+ 'offset' => $offset,
+ 'limit' => (int) $limit,
+ 'full_view' => $full_view,
+ 'pagination' => $pagination,
+ 'list_type' => $list_type,
+ 'list_type_toggle' => $list_type_toggle,
+ 'list_class' => 'elgg-list-entity',
+ );
+ }
+
+ if ($vars['list_type'] != 'list') {
+ return elgg_view('page/components/gallery', $vars);
+ } else {
+ return elgg_view('page/components/list', $vars);
+ }
}
/**
- * Returns a view of a list of annotations, plus navigation. It is intended that this function
- * be called from other wrapper functions.
+ * Returns a rendered list of annotations, plus pagination. This function
+ * should be called by wrapper functions.
+ *
+ * @param array $annotations Array of annotations
+ * @param array $vars Display variables
+ * 'count' The total number of annotations across all pages
+ * 'offset' The current indexing offset
+ * 'limit' The number of annotations to display per page
+ * 'full_view' Display the full view of the annotation?
+ * 'list_class' CSS Class applied to the list
+ * 'offset_key' The url parameter key used for offset
*
- * @param array $annotations List of annotations
- * @param int $count The total number of annotations across all pages
- * @param int $offset The current indexing offset
- * @param int $limit The number of annotations to display per page
* @return string The list of annotations
+ * @access private
*/
-function elgg_view_annotation_list($annotations, $count, $offset, $limit) {
- $count = (int) $count;
- $offset = (int) $offset;
- $limit = (int) $limit;
-
- $html = "";
-
- $nav = elgg_view('navigation/pagination',array(
- 'baseurl' => $_SERVER['REQUEST_URI'],
- 'offset' => $offset,
- 'count' => $count,
- 'limit' => $limit,
- 'word' => 'annoff',
- 'nonefound' => false,
- ));
-
- $html .= $nav;
-
- if (is_array($annotations) && sizeof($annotations) > 0) {
- foreach($annotations as $annotation) {
- $html .= elgg_view_annotation($annotation, "", false);
- }
- }
-
- if ($count) {
- $html .= $nav;
+function elgg_view_annotation_list($annotations, array $vars = array()) {
+ $defaults = array(
+ 'items' => $annotations,
+ 'list_class' => 'elgg-list-annotation elgg-annotation-list', // @todo remove elgg-annotation-list in Elgg 1.9
+ 'full_view' => true,
+ 'offset_key' => 'annoff',
+ );
+
+ $vars = array_merge($defaults, $vars);
+
+ if (!$vars["limit"] && !$vars["offset"]) {
+ // no need for pagination if listing is unlimited
+ $vars["pagination"] = false;
}
- return $html;
+ return elgg_view('page/components/list', $vars);
}
/**
- * Display a selective rendered list of annotations for a given entity.
+ * Display a plugin-specified rendered list of annotations for an entity.
*
- * The list is produced as the result of the entity:annotate plugin hook
- * and is designed to provide a more generic framework to allow plugins
- * to extend the generic display of entities with their own annotation
- * renderings.
+ * This displays the output of functions registered to the entity:annotation,
+ * $entity_type plugin hook.
*
- * This is called automatically by the framework from elgg_view_entity()
+ * This is called automatically by the framework from {@link elgg_view_entity()}
*
- * @param ElggEntity $entity
- * @param bool $full
- * @return string or false on failure
+ * @param ElggEntity $entity Entity
+ * @param bool $full_view Display full view?
+ *
+ * @return mixed string or false on failure
+ * @todo Change the hook name.
*/
-function elgg_view_entity_annotations(ElggEntity $entity, $full = true) {
-
- // No point continuing if entity is null
- if (!$entity) {
- return false;
- }
-
+function elgg_view_entity_annotations(ElggEntity $entity, $full_view = true) {
if (!($entity instanceof ElggEntity)) {
return false;
}
$entity_type = $entity->getType();
- $annotations = trigger_plugin_hook('entity:annotate', $entity_type,
+ $annotations = elgg_trigger_plugin_hook('entity:annotate', $entity_type,
array(
'entity' => $entity,
- 'full' => $full,
+ 'full_view' => $full_view,
)
);
@@ -767,235 +1112,428 @@ function elgg_view_entity_annotations(ElggEntity $entity, $full = true) {
}
/**
- * Displays an internal layout for the use of a plugin canvas.
- * Takes a variable number of parameters, which are made available
- * in the views as $vars['area1'] .. $vars['areaN'].
+ * Renders a title.
*
- * @param string $layout The name of the views in canvas/layouts/.
- * @return string The layout
+ * This is a shortcut for {@elgg_view page/elements/title}.
+ *
+ * @param string $title The page title
+ * @param array $vars View variables (was submenu be displayed? (deprecated))
+ *
+ * @return string The HTML (etc)
*/
-function elgg_view_layout($layout) {
- $arg = 1;
- $param_array = array();
- while ($arg < func_num_args()) {
- $param_array['area' . $arg] = func_get_arg($arg);
- $arg++;
+function elgg_view_title($title, $vars = array()) {
+ if (!is_array($vars)) {
+ elgg_deprecated_notice('setting $submenu in elgg_view_title() is deprecated', 1.8);
+ $vars = array('submenu' => $vars);
}
- if (elgg_view_exists("canvas/layouts/{$layout}")) {
- return elgg_view("canvas/layouts/{$layout}",$param_array);
- } else {
- return elgg_view("canvas/default",$param_array);
- }
+ $vars['title'] = $title;
+
+ return elgg_view('page/elements/title', $vars);
}
/**
- * Returns a view for the page title
+ * Displays a UNIX timestamp in a friendly way
*
- * @param string $title The page title
- * @param string $submenu Should a submenu be displayed? (default false, use not recommended)
- * @return string The HTML (etc)
+ * @see elgg_get_friendly_time()
+ *
+ * @param int $time A UNIX epoch timestamp
+ *
+ * @return string The friendly time HTML
+ * @since 1.7.2
*/
-function elgg_view_title($title, $submenu = false) {
- $title = elgg_view('page_elements/title', array('title' => $title, 'submenu' => $submenu));
-
- return $title;
+function elgg_view_friendly_time($time) {
+ return elgg_view('output/friendlytime', array('time' => $time));
}
/**
- * Automatically views comments and a comment form relating to the given entity
+ * Returns rendered comments and a comment form for an entity.
+ *
+ * @tip Plugins can override the output by registering a handler
+ * for the comments, $entity_type hook. The handler is responsible
+ * for formatting the comments and the add comment form.
*
- * @param ElggEntity $entity The entity to comment on
- * @param $add_comment Whether or not you want users to add more comments
- * @return string|false The HTML (etc) for the comments, or false on failure
+ * @param ElggEntity $entity The entity to view comments of
+ * @param bool $add_comment Include a form to add comments?
+ * @param array $vars Variables to pass to comment view
+ *
+ * @return string|false Rendered comments or false on failure
+ * @link http://docs.elgg.org/Entities/Comments
+ * @link http://docs.elgg.org/Annotations/Comments
*/
-function elgg_view_comments($entity, $add_comment = true){
-
+function elgg_view_comments($entity, $add_comment = true, array $vars = array()) {
if (!($entity instanceof ElggEntity)) {
return false;
}
- if ($comments = trigger_plugin_hook('comments',$entity->getType(),array('entity' => $entity),false)) {
- return $comments;
- } else {
- $comments = list_annotations($entity->getGUID(),'generic_comment');
-
- //display the new comment form if required
- if($add_comment){
- $comments .= elgg_view('comments/forms/edit',array('entity' => $entity));
- }
+ $vars['entity'] = $entity;
+ $vars['show_add_form'] = $add_comment;
+ $vars['class'] = elgg_extract('class', $vars, "{$entity->getSubtype()}-comments");
- return $comments;
+ $output = elgg_trigger_plugin_hook('comments', $entity->getType(), $vars, false);
+ if ($output) {
+ return $output;
+ } else {
+ return elgg_view('page/elements/comments', $vars);
}
}
-
/**
- * Wrapper function to display search listings.
+ * Wrapper function for the image block display pattern.
*
- * @param string $icon The icon for the listing
- * @param string $info Any information that needs to be displayed.
- * @return string The HTML (etc) representing the listing
+ * Fixed width media on the side (image, icon, flash, etc.).
+ * Descriptive content filling the rest of the column.
+ *
+ * This is a shortcut for {@elgg_view page/components/image_block}.
+ *
+ * @param string $image The icon and other information
+ * @param string $body Description content
+ * @param array $vars Additional parameters for the view
+ *
+ * @return string
+ * @since 1.8.0
*/
-function elgg_view_listing($icon, $info) {
- return elgg_view('entities/entity_listing',array('icon' => $icon, 'info' => $info));
+function elgg_view_image_block($image, $body, $vars = array()) {
+ $vars['image'] = $image;
+ $vars['body'] = $body;
+ return elgg_view('page/components/image_block', $vars);
}
/**
- * Sets an alternative function to handle templates, which will be passed to by elgg_view.
- * This function must take the $view and $vars parameters from elgg_view:
+ * Wrapper function for the module display pattern.
*
- * function my_template_function(string $view, array $vars = array())
+ * Box with header, body, footer
*
- * @see elgg_view
+ * This is a shortcut for {@elgg_view page/components/module}.
*
- * @param string $function_name The name of the function to pass to.
- * @return true|false
+ * @param string $type The type of module (main, info, popup, aside, etc.)
+ * @param string $title A title to put in the header
+ * @param string $body Content of the module
+ * @param array $vars Additional parameters for the module
+ *
+ * @return string
+ * @since 1.8.0
*/
-function set_template_handler($function_name) {
- global $CONFIG;
- if (!empty($function_name) && is_callable($function_name)) {
- $CONFIG->template_handler = $function_name;
- return true;
- }
- return false;
+function elgg_view_module($type, $title, $body, array $vars = array()) {
+ $vars['class'] = elgg_extract('class', $vars, '') . " elgg-module-$type";
+ $vars['title'] = $title;
+ $vars['body'] = $body;
+ return elgg_view('page/components/module', $vars);
}
/**
- * Extends a view.
+ * Renders a human-readable representation of a river item
*
- * The addititional views are displayed before or after the primary view.
- * Priorities less than 500 are displayed before the primary view and
- * greater than 500 after. The default priority is 501.
+ * @param ElggRiverItem $item A river item object
+ * @param array $vars An array of variables for the view
*
- * @param string $view The view to extend.
- * @param string $view_extension This view is added to $view
- * @param int $priority The priority, from 0 to 1000, to add at (lowest numbers displayed first)
- * @param string $viewtype Not used
+ * @return string returns empty string if could not be rendered
*/
-function elgg_extend_view($view, $view_extension, $priority = 501, $viewtype = '') {
- global $CONFIG;
-
- if (!isset($CONFIG->views)) {
- $CONFIG->views = new stdClass;
+function elgg_view_river_item($item, array $vars = array()) {
+ if (!($item instanceof ElggRiverItem)) {
+ return '';
}
-
- if (!isset($CONFIG->views->extensions)) {
- $CONFIG->views->extensions = array();
+ // checking default viewtype since some viewtypes do not have unique views per item (rss)
+ $view = $item->getView();
+ if (!$view || !elgg_view_exists($view, 'default')) {
+ return '';
}
- if (!isset($CONFIG->views->extensions[$view])) {
- $CONFIG->views->extensions[$view][500] = "{$view}";
+ $subject = $item->getSubjectEntity();
+ $object = $item->getObjectEntity();
+ if (!$subject || !$object) {
+ // subject is disabled or subject/object deleted
+ return '';
}
- while(isset($CONFIG->views->extensions[$view][$priority])) {
- $priority++;
- }
+ // @todo this needs to be cleaned up
+ // Don't hide objects in closed groups that a user can see.
+ // see https://github.com/elgg/elgg/issues/4789
+ // else {
+ // // hide based on object's container
+ // $visibility = ElggGroupItemVisibility::factory($object->container_guid);
+ // if ($visibility->shouldHideItems) {
+ // return '';
+ // }
+ // }
- $CONFIG->views->extensions[$view][$priority] = "{$view_extension}";
- ksort($CONFIG->views->extensions[$view]);
+ $vars['item'] = $item;
+
+ return elgg_view('river/item', $vars);
}
/**
- * Unextends a view.
+ * Convenience function for generating a form from a view in a standard location.
*
- * @param string $view The view that was extended.
- * @param string $view_extension This view that was added to $view
- * @return bool
- * @since 1.7.2
+ * This function assumes that the body of the form is located at "forms/$action" and
+ * sets the action by default to "action/$action". Automatically wraps the forms/$action
+ * view with a <form> tag and inserts the anti-csrf security tokens.
+ *
+ * @tip This automatically appends elgg-form-action-name to the form's class. It replaces any
+ * slashes with dashes (blog/save becomes elgg-form-blog-save)
+ *
+ * @example
+ * <code>echo elgg_view_form('login');</code>
+ *
+ * This would assume a "login" form body to be at "forms/login" and would set the action
+ * of the form to "http://yoursite.com/action/login".
+ *
+ * If elgg_view('forms/login') is:
+ * <input type="text" name="username" />
+ * <input type="password" name="password" />
+ *
+ * Then elgg_view_form('login') generates:
+ * <form action="http://yoursite.com/action/login" method="post">
+ * ...security tokens...
+ * <input type="text" name="username" />
+ * <input type="password" name="password" />
+ * </form>
+ *
+ * @param string $action The name of the action. An action name does not include
+ * the leading "action/". For example, "login" is an action name.
+ * @param array $form_vars $vars environment passed to the "input/form" view
+ * @param array $body_vars $vars environment passed to the "forms/$action" view
+ *
+ * @return string The complete form
*/
-function elgg_unextend_view($view, $view_extension) {
+function elgg_view_form($action, $form_vars = array(), $body_vars = array()) {
global $CONFIG;
- if (!isset($CONFIG->views)) {
- return FALSE;
+ $defaults = array(
+ 'action' => $CONFIG->wwwroot . "action/$action",
+ 'body' => elgg_view("forms/$action", $body_vars)
+ );
+
+ $form_class = 'elgg-form-' . preg_replace('/[^a-z0-9]/i', '-', $action);
+
+ // append elgg-form class to any class options set
+ if (isset($form_vars['class'])) {
+ $form_vars['class'] = $form_vars['class'] . " $form_class";
+ } else {
+ $form_vars['class'] = $form_class;
}
- if (!isset($CONFIG->views->extensions)) {
- return FALSE;
+ return elgg_view('input/form', array_merge($defaults, $form_vars));
+}
+
+/**
+ * View an item in a list
+ *
+ * @param ElggEntity|ElggAnnotation $item
+ * @param array $vars Additional parameters for the rendering
+ *
+ * @return string
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_view_list_item($item, array $vars = array()) {
+ global $CONFIG;
+
+ $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);
}
- if (!isset($CONFIG->views->extensions[$view])) {
- return FALSE;
+ return '';
+}
+
+/**
+ * View one of the elgg sprite icons
+ *
+ * Shorthand for <span class="elgg-icon elgg-icon-$name"></span>
+ *
+ * @param string $name The specific icon to display
+ * @param string $class Additional class: float, float-alt, or custom class
+ *
+ * @return string The html for displaying an icon
+ */
+function elgg_view_icon($name, $class = '') {
+ // @todo deprecate boolean in Elgg 1.9
+ if ($class === true) {
+ $class = 'float';
}
+ return "<span class=\"elgg-icon elgg-icon-$name $class\"></span>";
+}
- $priority = array_search($view_extension, $CONFIG->views->extensions[$view]);
- if ($priority === FALSE) {
- return FALSE;
+/**
+ * Displays a user's access collections, using the core/friends/collections view
+ *
+ * @param int $owner_guid The GUID of the owning user
+ *
+ * @return string A formatted rendition of the collections
+ * @todo Move to the friends/collection.php page.
+ * @access private
+ */
+function elgg_view_access_collections($owner_guid) {
+ if ($collections = get_user_access_collections($owner_guid)) {
+ foreach ($collections as $key => $collection) {
+ $collections[$key]->members = get_members_of_access_collection($collection->id, true);
+ $collections[$key]->entities = get_user_friends($owner_guid, "", 9999);
+ }
}
- unset($CONFIG->views->extensions[$view][$priority]);
-
- return TRUE;
+ return elgg_view('core/friends/collections', array('collections' => $collections));
}
/**
- * @deprecated 1.7. Use elgg_extend_view().
- * @param $view
- * @param $view_name
- * @param $priority
- * @param $viewtype
+ * Registers a function to handle templates.
+ *
+ * Alternative template handlers can be registered to handle
+ * all output functions. By default, {@link elgg_view()} will
+ * simply include the view file. If an alternate template handler
+ * is registered, the view name and passed $vars will be passed to the
+ * registered function, which is then responsible for generating and returning
+ * output.
+ *
+ * Template handlers need to accept two arguments: string $view_name and array
+ * $vars.
+ *
+ * @warning This is experimental.
+ *
+ * @param string $function_name The name of the function to pass to.
+ *
+ * @return bool
+ * @see elgg_view()
+ * @link http://docs.elgg.org/Views/TemplateHandlers
*/
-function extend_view($view, $view_name, $priority = 501, $viewtype = '') {
- elgg_deprecated_notice('extend_view() was deprecated by elgg_extend_view()!', 1.7);
- elgg_extend_view($view, $view_name, $priority, $viewtype);
+function set_template_handler($function_name) {
+ global $CONFIG;
+
+ if (is_callable($function_name)) {
+ $CONFIG->template_handler = $function_name;
+ return true;
+ }
+ return false;
}
/**
- * Set an alternative base location for a view (as opposed to the default of $CONFIG->viewpath)
+ * Returns the name of views for in a directory.
*
- * @param string $view The name of the view
- * @param string $location The base location path
+ * Use this to get all namespaced views under the first element.
+ *
+ * @param string $dir The main directory that holds the views. (mod/profile/views/)
+ * @param string $base The root name of the view to use, without the viewtype. (profile)
+ *
+ * @return array
+ * @since 1.7.0
+ * @todo Why isn't this used anywhere else but in elgg_view_tree()?
+ * Seems like a useful function for autodiscovery.
+ * @access private
+ */
+function elgg_get_views($dir, $base) {
+ $return = array();
+ if (file_exists($dir) && is_dir($dir)) {
+ if ($handle = opendir($dir)) {
+ while ($view = readdir($handle)) {
+ if (!in_array($view, array('.', '..', '.svn', 'CVS'))) {
+ if (is_dir($dir . '/' . $view)) {
+ if ($val = elgg_get_views($dir . '/' . $view, $base . '/' . $view)) {
+ $return = array_merge($return, $val);
+ }
+ } else {
+ $view = str_replace('.php', '', $view);
+ $return[] = $base . '/' . $view;
+ }
+ }
+ }
+ }
+ }
+
+ return $return;
+}
+
+/**
+ * Returns all views below a partial view.
+ *
+ * Settings $view_root = 'profile' will show all available views under
+ * the "profile" namespace.
+ *
+ * @param string $view_root The root view
+ * @param string $viewtype Optionally specify a view type
+ * other than the current one.
+ *
+ * @return array A list of view names underneath that root view
+ * @todo This is used once in the deprecated get_activity_stream_data() function.
+ * @access private
*/
-function set_view_location($view, $location, $viewtype = '') {
+function elgg_view_tree($view_root, $viewtype = "") {
global $CONFIG;
+ static $treecache = array();
- if (empty($viewtype)) {
- $viewtype = 'default';
+ // Get viewtype
+ if (!$viewtype) {
+ $viewtype = elgg_get_viewtype();
}
- if (!isset($CONFIG->views)) {
- $CONFIG->views = new stdClass;
+ // A little light internal caching
+ if (!empty($treecache[$view_root])) {
+ return $treecache[$view_root];
}
- if (!isset($CONFIG->views->locations)) {
- $CONFIG->views->locations = array($viewtype => array($view => $location));
+ // Examine $CONFIG->views->locations
+ if (isset($CONFIG->views->locations[$viewtype])) {
+ foreach ($CONFIG->views->locations[$viewtype] as $view => $path) {
+ $pos = strpos($view, $view_root);
+ if ($pos === 0) {
+ $treecache[$view_root][] = $view;
+ }
+ }
+ }
- } else if (!isset($CONFIG->views->locations[$viewtype])) {
- $CONFIG->views->locations[$viewtype] = array($view => $location);
+ // Now examine core
+ $location = $CONFIG->viewpath;
+ $viewtype = elgg_get_viewtype();
+ $root = $location . $viewtype . '/' . $view_root;
- } else {
- $CONFIG->views->locations[$viewtype][$view] = $location;
+ if (file_exists($root) && is_dir($root)) {
+ $val = elgg_get_views($root, $view_root);
+ if (!is_array($treecache[$view_root])) {
+ $treecache[$view_root] = array();
+ }
+ $treecache[$view_root] = array_merge($treecache[$view_root], $val);
}
+
+ return $treecache[$view_root];
}
/**
- * Auto-registers views from a particular starting location
+ * Auto-registers views from a location.
+ *
+ * @note Views in plugin/views/ are automatically registered for active plugins.
+ * Plugin authors would only need to call this if optionally including
+ * an entire views structure.
*
- * @param string $view_base The base of the view name
- * @param string $folder The folder to begin looking in
- * @param string $base_location_path The base views directory to use with set_view_location
- * @param string $viewtype The type of view we're looking at (default, rss, etc)
+ * @param string $view_base Optional The base of the view name without the view type.
+ * @param string $folder Required The folder to begin looking in
+ * @param string $base_location_path The base views directory to use with elgg_set_view_location()
+ * @param string $viewtype The type of view we're looking at (default, rss, etc)
+ *
+ * @return bool returns false if folder can't be read
+ * @since 1.7.0
+ * @see elgg_set_view_location()
+ * @todo This seems overly complicated.
+ * @access private
*/
function autoregister_views($view_base, $folder, $base_location_path, $viewtype) {
- if (!isset($i)) {
- $i = 0;
- }
-
if ($handle = opendir($folder)) {
while ($view = readdir($handle)) {
- if (!in_array($view,array('.','..','.svn','CVS')) && !is_dir($folder . "/" . $view)) {
+ if (!in_array($view, array('.', '..', '.svn', 'CVS')) && !is_dir($folder . "/" . $view)) {
// this includes png files because some icons are stored within view directories.
// See commit [1705]
- if ((substr_count($view,".php") > 0) || (substr_count($view,".png") > 0)) {
+ if ((substr_count($view, ".php") > 0) || (substr_count($view, ".png") > 0)) {
if (!empty($view_base)) {
$view_base_new = $view_base . "/";
} else {
$view_base_new = "";
}
- set_view_location($view_base_new . str_replace('.php', '', $view), $base_location_path, $viewtype);
+ elgg_set_view_location($view_base_new . str_replace('.php', '', $view),
+ $base_location_path, $viewtype);
}
} else if (!in_array($view, array('.', '..', '.svn', 'CVS')) && is_dir($folder . "/" . $view)) {
if (!empty($view_base)) {
@@ -1003,7 +1541,8 @@ function autoregister_views($view_base, $folder, $base_location_path, $viewtype)
} else {
$view_base_new = "";
}
- autoregister_views($view_base_new . $view, $folder . "/" . $view, $base_location_path, $viewtype);
+ autoregister_views($view_base_new . $view, $folder . "/" . $view,
+ $base_location_path, $viewtype);
}
}
return TRUE;
@@ -1013,47 +1552,114 @@ function autoregister_views($view_base, $folder, $base_location_path, $viewtype)
}
/**
- * Returns a representation of a full 'page' (which might be an HTML page, RSS file, etc, depending on the current view)
+ * Add the rss link to the extras when if needed
*
- * @param unknown_type $title
- * @param unknown_type $body
- * @return unknown
+ * @return void
+ * @access private
*/
-function page_draw($title, $body, $sidebar = "", $page_shell = 'page_shells/default') {
+function elgg_views_add_rss_link() {
+ global $autofeed;
+ if (isset($autofeed) && $autofeed == true) {
+ $url = current_page_url();
+ if (substr_count($url, '?')) {
+ $url .= "&view=rss";
+ } else {
+ $url .= "?view=rss";
+ }
- // get messages - try for errors first
- $sysmessages = system_messages(null, "errors");
- if (count($sysmessages["errors"]) == 0) {
- // no errors so grab rest of messages
- $sysmessages = system_messages(null, "");
- } else {
- // we have errors - clear out remaining messages
- system_messages(null, "");
+ $url = elgg_format_url($url);
+ elgg_register_menu_item('extras', array(
+ 'name' => 'rss',
+ 'text' => elgg_view_icon('rss'),
+ 'href' => $url,
+ 'title' => elgg_echo('feed:rss'),
+ ));
}
+}
- // Draw the page
- $output = elgg_view($page_shell, array(
- 'title' => $title,
- 'body' => $body,
- 'sidebar' => $sidebar,
- 'sysmessages' => $sysmessages,
- )
- );
- $split_output = str_split($output, 1024);
-
- foreach($split_output as $chunk) {
- echo $chunk;
+/**
+ * Registers deprecated views to avoid making some pages from older plugins
+ * completely empty.
+ *
+ * @access private
+ */
+function elgg_views_handle_deprecated_views() {
+ $location = elgg_get_view_location('page_elements/contentwrapper');
+ if ($location === "/var/www/views/") {
+ elgg_extend_view('page_elements/contentwrapper', 'page/elements/wrapper');
}
}
/**
- * Checks if $view_type is valid on this installation.
+ * Initialize viewtypes on system boot event
+ * This ensures simplecache is cleared during upgrades. See #2252
*
- * @param string $view_type
- * @return bool
+ * @return void
+ * @access private
+ * @elgg_event_handler boot system
*/
-function elgg_is_valid_view_type($view_type) {
+function elgg_views_boot() {
global $CONFIG;
- return in_array($view_type, $CONFIG->view_types);
-} \ No newline at end of file
+ elgg_register_simplecache_view('css/ie');
+ elgg_register_simplecache_view('css/ie6');
+ elgg_register_simplecache_view('css/ie7');
+
+ elgg_register_js('jquery', '/vendors/jquery/jquery-1.6.4.min.js', 'head');
+ elgg_register_js('jquery-ui', '/vendors/jquery/jquery-ui-1.8.16.min.js', 'head');
+ elgg_register_js('jquery.form', '/vendors/jquery/jquery.form.js');
+
+ elgg_register_simplecache_view('js/elgg');
+ $elgg_js_url = elgg_get_simplecache_url('js', 'elgg');
+ elgg_register_js('elgg', $elgg_js_url, 'head');
+
+ elgg_load_js('jquery');
+ elgg_load_js('jquery-ui');
+ elgg_load_js('elgg');
+
+ elgg_register_simplecache_view('js/lightbox');
+ $lightbox_js_url = elgg_get_simplecache_url('js', 'lightbox');
+ elgg_register_js('lightbox', $lightbox_js_url);
+
+ elgg_register_simplecache_view('css/lightbox');
+ $lightbox_css_url = elgg_get_simplecache_url('css', 'lightbox');
+ elgg_register_css('lightbox', $lightbox_css_url);
+
+ elgg_register_simplecache_view('css/elgg');
+ $elgg_css_url = elgg_get_simplecache_url('css', 'elgg');
+ elgg_register_css('elgg', $elgg_css_url);
+
+ elgg_load_css('elgg');
+
+ elgg_register_ajax_view('js/languages');
+
+ elgg_register_plugin_hook_handler('output:before', 'layout', 'elgg_views_add_rss_link');
+
+ // discover the built-in view types
+ // @todo the cache is loaded in load_plugins() but we need to know view_types earlier
+ $view_path = $CONFIG->viewpath;
+
+ $views = scandir($view_path);
+
+ foreach ($views as $view) {
+ if ($view[0] !== '.' && is_dir($view_path . $view)) {
+ elgg_register_viewtype($view);
+ }
+ }
+
+ // set default icon sizes - can be overridden in settings.php or with plugin
+ if (!isset($CONFIG->icon_sizes)) {
+ $icon_sizes = array(
+ 'topbar' => array('w' => 16, 'h' => 16, 'square' => TRUE, 'upscale' => TRUE),
+ 'tiny' => array('w' => 25, 'h' => 25, 'square' => TRUE, 'upscale' => TRUE),
+ 'small' => array('w' => 40, 'h' => 40, 'square' => TRUE, 'upscale' => TRUE),
+ 'medium' => array('w' => 100, 'h' => 100, 'square' => TRUE, 'upscale' => TRUE),
+ 'large' => array('w' => 200, 'h' => 200, 'square' => FALSE, 'upscale' => FALSE),
+ 'master' => array('w' => 550, 'h' => 550, 'square' => FALSE, 'upscale' => FALSE),
+ );
+ elgg_set_config('icon_sizes', $icon_sizes);
+ }
+}
+
+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/api.php b/engine/lib/web_services.php
index 0da0c82f0..51cad6f39 100644
--- a/engine/lib/api.php
+++ b/engine/lib/web_services.php
@@ -1,286 +1,13 @@
<?php
/**
- * Elgg API
- * Functions and objects which make up the API engine.
+ * Elgg web services API
+ * Functions and objects for exposing custom web services.
*
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd <info@elgg.com>
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage WebServicesAPI
*/
-// Result classes /////////////////////////////////////////////////////////////////////////
-
-/**
- * GenericResult Result superclass.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Core
- */
-abstract class GenericResult {
- /**
- * The status of the result.
- * @var int
- */
- private $status_code;
-
- /**
- * Message returned along with the status which is almost always an error message.
- * This must be human readable, understandable and localised.
- * @var string
- */
- private $message;
-
- /**
- * Result store.
- * Attach result specific informaton here.
- *
- * @var mixed. Should probably be an object of some sort.
- */
- private $result;
-
- /**
- * Set a status code and optional message.
- *
- * @param int $status The status code.
- * @param string $message The message.
- */
- protected function setStatusCode($status, $message = "") {
- $this->status_code = $status;
- $this->message = $message;
- }
-
- /**
- * Set the result.
- *
- * @param mixed $result
- */
- protected function setResult($result) {
- $this->result = $result;
- }
-
- protected function getStatusCode() {
- return $this->status_code;
- }
-
- protected function getStatusMessage() {
- return $this->message;
- }
-
- protected function getResult() {
- return $this->result;
- }
-
- /**
- * Serialise to a standard class.
- *
- * DEVNOTE: The API is only interested in data, we can not easily serialise
- * custom classes without the need for 1) the other side being PHP, 2) you need to have the class
- * definition installed, 3) its the right version!
- *
- * Therefore, I'm not bothering.
- *
- * Override this to include any more specific information, however api results should be attached to the
- * class using setResult().
- *
- * if $CONFIG->debug is set then additional information about the runtime environment and authentication will be
- * returned.
- *
- * @return stdClass Object containing the serialised result.
- */
- public function export() {
- global $ERRORS, $CONFIG, $_PAM_HANDLERS_MSG;
-
- $result = new stdClass;
-
- $result->status = $this->getStatusCode();
- if ($this->getStatusMessage()!="") {
- $result->message = $this->getStatusMessage();
- }
-
- $resultdata = $this->getResult();
- if (isset($resultdata)) {
- $result->result = $resultdata;
- }
-
- if (isset($CONFIG->debug)) {
- if (count($ERRORS)) {
- $result->runtime_errors = $ERRORS;
- }
-
- if (count($_PAM_HANDLERS_MSG)) {
- $result->pam = $_PAM_HANDLERS_MSG;
- }
- }
-
- return $result;
- }
-}
-
-/**
- * SuccessResult
- * Generic success result class, extend if you want to do something special.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Core
- */
-class SuccessResult extends GenericResult {
- public static $RESULT_SUCCESS = 0; // Do not change this from 0
-
- public function SuccessResult($result) {
- $this->setResult($result);
- $this->setStatusCode(SuccessResult::$RESULT_SUCCESS);
- }
-
- public static function getInstance($result) {
- // Return a new error object.
- return new SuccessResult($result);
- }
-}
-
-/**
- * ErrorResult
- * The error result class.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage Core
- */
-class ErrorResult extends GenericResult {
- // Fail with no specific code
- public static $RESULT_FAIL = -1 ;
-
- public static $RESULT_FAIL_APIKEY_DISABLED = -30;
- public static $RESULT_FAIL_APIKEY_INACTIVE = -31;
- public static $RESULT_FAIL_APIKEY_INVALID = -32;
-
- // Invalid, expired or missing auth token
- public static $RESULT_FAIL_AUTHTOKEN = -20;
-
- public function ErrorResult($message, $code = "", Exception $exception = NULL) {
- if ($code == "") {
- $code = ErrorResult::$RESULT_FAIL;
- }
-
- if ($exception!=NULL) {
- $this->setResult($exception->__toString());
- }
-
- $this->setStatusCode($code, $message);
- }
-
- /**
- * Get a new instance of the ErrorResult.
- *
- * @param string $message
- * @param int $code
- * @param Exception $exception Optional exception for generating a stack trace.
- */
- public static function getInstance($message, $code = "", Exception $exception = NULL) {
- // Return a new error object.
- return new ErrorResult($message, $code, $exception);
- }
-}
-
-// Caching of HMACs ///////////////////////////////////////////////////////////////////////
-
-/**
- * ElggHMACCache
- * Store cached data in a temporary database, only used by the HMAC stuff.
- *
- * @author Curverider Ltd <info@elgg.com>
- * @package Elgg
- * @subpackage API
- */
-class ElggHMACCache extends ElggCache {
- /**
- * Set the Elgg cache.
- *
- * @param int $max_age Maximum age in seconds, 0 if no limit.
- */
- function __construct($max_age = 0) {
- $this->set_variable("max_age", $max_age);
- }
-
- /**
- * Save a key
- *
- * @param string $key
- * @param string $data
- * @return boolean
- */
- public function save($key, $data) {
- global $CONFIG;
-
- $key = sanitise_string($key);
- $time = time();
-
- return insert_data("INSERT into {$CONFIG->dbprefix}hmac_cache (hmac, ts) VALUES ('$key', '$time')");
- }
-
- /**
- * Load a key
- *
- * @param string $key
- * @param int $offset
- * @param int $limit
- * @return string
- */
- public function load($key, $offset = 0, $limit = null) {
- global $CONFIG;
-
- $key = sanitise_string($key);
-
- $row = get_data_row("SELECT * from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
- if ($row) {
- return $row->hmac;
- }
-
- return false;
- }
-
- /**
- * Invalidate a given key.
- *
- * @param string $key
- * @return bool
- */
- public function delete($key) {
- global $CONFIG;
-
- $key = sanitise_string($key);
-
- return delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
- }
-
- /**
- * Clear out all the contents of the cache.
- *
- * Not currently implemented in this cache type.
- */
- public function clear() {
- return true;
- }
-
- /**
- * Clean out old stuff.
- *
- */
- public function __destruct() {
- global $CONFIG;
-
- $time = time();
- $age = (int)$this->get_variable("max_age");
-
- $expires = $time-$age;
-
- delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where ts<$expires");
- }
-}
-
-// Primary Services API Server functions /////////////////////////////////////////////////////////////////////
+// Primary Services API Server functions
/**
* A global array holding API methods.
@@ -290,9 +17,10 @@ class ElggHMACCache extends ElggCache {
* "description" => "Some human readable description"
* "function" = 'my_function_callback'
* "parameters" = array (
- * "variable" = array ( // NB, the order should be the same as the function callback
+ * "variable" = array ( // the order should be the same as the function callback
* type => 'int' | 'bool' | 'float' | 'string'
* required => true (default) | false
+ * default => value // optional
* )
* )
* "call_method" = 'GET' | 'POST'
@@ -301,6 +29,7 @@ class ElggHMACCache extends ElggCache {
* )
* )
*/
+global $API_METHODS;
$API_METHODS = array();
/**
@@ -310,28 +39,38 @@ $API_METHODS = array();
* It also cannot handle arrays of bools or arrays of arrays.
* Also, input will be filtered to protect against XSS attacks through the API.
*
- * @param string $method The api name to expose - for example "myapi.dosomething"
- * @param string $function Your function callback.
- * @param array $parameters (optional) List of parameters in the same order as in your function.
- * Default values may be set for parameters which would allow REST api users flexibility in
- * what parameters are passed. Generally, optional parameters should be after required parameters.
- * This array should be in the format
- * "variable" = array (
- * type => 'int' | 'bool' | 'float' | 'string' | 'array'
- * required => true (default) | false
- * default => value (optional)
- * )
- * @param string $description (optional) human readable description of the function.
- * @param string $call_method (optional) Define what http method must be used for this function. Default: GET
- * @param bool $require_api_auth (optional) (default is false) Does this method require API authorization? (example: API key)
- * @param bool $require_user_auth (optional) (default is false) Does this method require user authorization?
+ * @param string $method The api name to expose - for example "myapi.dosomething"
+ * @param string $function Your function callback.
+ * @param array $parameters (optional) List of parameters in the same order as in
+ * your function. Default values may be set for parameters which
+ * allow REST api users flexibility in what parameters are passed.
+ * Generally, optional parameters should be after required
+ * parameters.
+ *
+ * This array should be in the format
+ * "variable" = array (
+ * type => 'int' | 'bool' | 'float' | 'string' | 'array'
+ * required => true (default) | false
+ * default => value (optional)
+ * )
+ * @param string $description (optional) human readable description of the function.
+ * @param string $call_method (optional) Define what http method must be used for
+ * this function. Default: GET
+ * @param bool $require_api_auth (optional) (default is false) Does this method
+ * require API authorization? (example: API key)
+ * @param bool $require_user_auth (optional) (default is false) Does this method
+ * require user authorization?
+ *
* @return bool
*/
-function expose_function($method, $function, array $parameters = NULL, $description = "", $call_method = "GET", $require_api_auth = false, $require_user_auth = false) {
+function expose_function($method, $function, array $parameters = NULL, $description = "",
+$call_method = "GET", $require_api_auth = false, $require_user_auth = false) {
+
global $API_METHODS;
if (($method == "") || ($function == "")) {
- throw new InvalidParameterException(elgg_echo('InvalidParameterException:APIMethodOrFunctionNotSet'));
+ $msg = elgg_echo('InvalidParameterException:APIMethodOrFunctionNotSet');
+ throw new InvalidParameterException($msg);
}
// does not check whether this method has already been exposed - good idea?
@@ -344,13 +83,15 @@ function expose_function($method, $function, array $parameters = NULL, $descript
if ($parameters != NULL) {
if (!is_array($parameters)) {
- throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:APIParametersArrayStructure'), $method));
+ $msg = elgg_echo('InvalidParameterException:APIParametersArrayStructure', array($method));
+ throw new InvalidParameterException($msg);
}
// catch common mistake of not setting up param array correctly
$first = current($parameters);
if (!is_array($first)) {
- throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:APIParametersArrayStructure'), $method));
+ $msg = elgg_echo('InvalidParameterException:APIParametersArrayStructure', array($method));
+ throw new InvalidParameterException($msg);
}
}
@@ -375,7 +116,10 @@ function expose_function($method, $function, array $parameters = NULL, $descript
$API_METHODS[$method]["call_method"] = 'GET';
break;
default :
- throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:UnrecognisedHttpMethod'), $call_method, $method));
+ $msg = elgg_echo('InvalidParameterException:UnrecognisedHttpMethod',
+ array($call_method, $method));
+
+ throw new InvalidParameterException($msg);
}
$API_METHODS[$method]["require_api_auth"] = $require_api_auth;
@@ -387,7 +131,12 @@ function expose_function($method, $function, array $parameters = NULL, $descript
/**
* Unregister an API method
+ *
* @param string $method The api name that was exposed
+ *
+ * @since 1.7.0
+ *
+ * @return void
*/
function unexpose_function($method) {
global $API_METHODS;
@@ -399,36 +148,37 @@ function unexpose_function($method) {
/**
* Check that the method call has the proper API and user authentication
+ *
* @param string $method The api name that was exposed
+ *
* @return true or throws an exception
* @throws APIException
+ * @since 1.7.0
+ * @access private
*/
function authenticate_method($method) {
global $API_METHODS;
// method must be exposed
if (!isset($API_METHODS[$method])) {
- throw new APIException(sprintf(elgg_echo('APIException:MethodCallNotImplemented'), $method));
- }
-
- // make sure that POST variables are available if relevant
- if (get_call_method() === 'POST') {
- include_post_data();
+ throw new APIException(elgg_echo('APIException:MethodCallNotImplemented', array($method)));
}
// check API authentication if required
if ($API_METHODS[$method]["require_api_auth"] == true) {
- if (pam_authenticate(null, "api") == false) {
+ $api_pam = new ElggPAM('api');
+ if ($api_pam->authenticate() !== true) {
throw new APIException(elgg_echo('APIException:APIAuthenticationFailed'));
}
}
- $user_auth_result = pam_authenticate();
+ $user_pam = new ElggPAM('user');
+ $user_auth_result = $user_pam->authenticate(array());
// check if user authentication is required
if ($API_METHODS[$method]["require_user_auth"] == true) {
if ($user_auth_result == false) {
- throw new APIException(elgg_echo('APIException:UserAuthenticationFailed'));
+ throw new APIException($user_pam->getFailureMessage(), ErrorResult::$RESULT_FAIL_AUTHTOKEN);
}
}
@@ -440,25 +190,34 @@ function authenticate_method($method) {
* A method is a function which you have previously exposed using expose_function.
*
* @param string $method Method, e.g. "foo.bar"
+ *
* @return GenericResult The result of the execution.
* @throws APIException, CallException
+ * @access private
*/
function execute_method($method) {
global $API_METHODS, $CONFIG;
// method must be exposed
if (!isset($API_METHODS[$method])) {
- throw new APIException(sprintf(elgg_echo('APIException:MethodCallNotImplemented'), $method));
+ $msg = elgg_echo('APIException:MethodCallNotImplemented', array($method));
+ throw new APIException($msg);
}
// function must be callable
- if (!(isset($API_METHODS[$method]["function"])) || !(is_callable($API_METHODS[$method]["function"]))) {
- throw new APIException(sprintf(elgg_echo('APIException:FunctionDoesNotExist'), $method));
+ if (!(isset($API_METHODS[$method]["function"]))
+ || !(is_callable($API_METHODS[$method]["function"]))) {
+
+ $msg = elgg_echo('APIException:FunctionDoesNotExist', array($method));
+ throw new APIException($msg);
}
// check http call method
if (strcmp(get_call_method(), $API_METHODS[$method]["call_method"]) != 0) {
- throw new CallException(sprintf(elgg_echo('CallException:InvalidCallMethod'), $method, $API_METHODS[$method]["call_method"]));
+ $msg = elgg_echo('CallException:InvalidCallMethod', array($method,
+ $API_METHODS[$method]["call_method"]));
+
+ throw new CallException($msg);
}
$parameters = get_parameters_for_method($method);
@@ -473,6 +232,7 @@ function execute_method($method) {
$function = $API_METHODS[$method]["function"];
$serialised_parameters = trim($serialised_parameters, ", ");
+ // @todo document why we cannot use call_user_func_array here
$result = eval("return $function($serialised_parameters);");
// Sanity check result
@@ -482,12 +242,14 @@ function execute_method($method) {
}
if ($result === false) {
- throw new APIException(sprintf(elgg_echo('APIException:FunctionParseError'), $function, $serialised_parameters));
+ $msg = elgg_echo('APIException:FunctionParseError', array($function, $serialised_parameters));
+ throw new APIException($msg);
}
- if ($result === NULL) {
+ if ($result === NULL) {
// If no value
- throw new APIException(sprintf(elgg_echo('APIException:FunctionNoReturn'), $function, $serialised_parameters));
+ $msg = elgg_echo('APIException:FunctionNoReturn', array($function, $serialised_parameters));
+ throw new APIException($msg);
}
// Otherwise assume that the call was successful and return it as a success object.
@@ -496,7 +258,9 @@ function execute_method($method) {
/**
* Get the request method.
+ *
* @return string HTTP request method
+ * @access private
*/
function get_call_method() {
return $_SERVER['REQUEST_METHOD'];
@@ -509,7 +273,9 @@ function get_call_method() {
* an associated array.
*
* @param string $method The method
+ *
* @return array containing parameters as key => value
+ * @access private
*/
function get_parameters_for_method($method) {
global $API_METHODS;
@@ -520,7 +286,7 @@ function get_parameters_for_method($method) {
if (isset($API_METHODS[$method]['parameters'])) {
foreach ($API_METHODS[$method]['parameters'] as $k => $v) {
$param = get_input($k); // Make things go through the sanitiser
- if ($param !== '') {
+ if ($param !== '' && $param !== null) {
$sanitised[$k] = $param;
} else {
// parameter wasn't passed so check for default
@@ -537,46 +303,27 @@ function get_parameters_for_method($method) {
/**
* Get POST data
* Since this is called through a handler, we need to manually get the post data
- * @return POST data from PHP
+ *
+ * @return POST data as string encoded as multipart/form-data
+ * @access private
*/
function get_post_data() {
- global $GLOBALS;
- $postdata = '';
- if (isset($GLOBALS['HTTP_RAW_POST_DATA']))
- $postdata = $GLOBALS['HTTP_RAW_POST_DATA'];
-
- // Attempt another method to return post data (incase always_populate_raw_post_data is switched off)
- if (!$postdata) {
- $postdata = file_get_contents('php://input');
- }
+ $postdata = file_get_contents('php://input');
return $postdata;
}
/**
- * This fixes the post parameters that are munged due to page handler
- */
-function include_post_data() {
-
- $postdata = get_post_data();
-
- if (isset($postdata)) {
- $query_arr = elgg_parse_str($postdata);
- if (is_array($query_arr)) {
- foreach($query_arr as $name => $val) {
- set_input($name, $val);
- }
- }
- }
-}
-
-/**
* Verify that the required parameters are present
- * @param $method
- * @param $parameters
+ *
+ * @param string $method Method name
+ * @param array $parameters List of expected parameters
+ *
* @return true on success or exception
* @throws APIException
+ * @since 1.7.0
+ * @access private
*/
function verify_parameters($method, $parameters) {
global $API_METHODS;
@@ -590,12 +337,15 @@ function verify_parameters($method, $parameters) {
foreach ($API_METHODS[$method]['parameters'] as $key => $value) {
// this tests the expose structure: must be array to describe parameter and type must be defined
if (!is_array($value) || !isset($value['type'])) {
- throw new APIException(sprintf(elgg_echo('APIException:InvalidParameter'), $key, $method));
+
+ $msg = elgg_echo('APIException:InvalidParameter', array($key, $method));
+ throw new APIException($msg);
}
// Check that the variable is present in the request if required
if ($value['required'] && !array_key_exists($key, $parameters)) {
- throw new APIException(sprintf(elgg_echo('APIException:MissingParameterInMethod'), $key, $method));
+ $msg = elgg_echo('APIException:MissingParameterInMethod', array($key, $method));
+ throw new APIException($msg);
}
}
@@ -605,10 +355,13 @@ function verify_parameters($method, $parameters) {
/**
* Serialize an array of parameters for an API method call
*
- * @param string $method API method name
- * @param array $parameters Array of parameters
+ * @param string $method API method name
+ * @param array $parameters Array of parameters
+ *
* @return string or exception
* @throws APIException
+ * @since 1.7.0
+ * @access private
*/
function serialise_parameters($method, $parameters) {
global $API_METHODS;
@@ -654,7 +407,8 @@ function serialise_parameters($method, $parameters) {
case 'array':
// we can handle an array of strings, maybe ints, definitely not booleans or other arrays
if (!is_array($parameters[$key])) {
- throw new APIException(sprintf(elgg_echo('APIException:ParameterNotArray'), $key));
+ $msg = elgg_echo('APIException:ParameterNotArray', array($key));
+ throw new APIException($msg);
}
$array = "array(";
@@ -666,7 +420,7 @@ function serialise_parameters($method, $parameters) {
$array .= "'$k'=>'$v',";
}
- $array = trim($array,",");
+ $array = trim($array, ",");
$array .= ")";
$array = ",$array";
@@ -674,7 +428,8 @@ function serialise_parameters($method, $parameters) {
$serialised_parameters .= $array;
break;
default:
- throw new APIException(sprintf(elgg_echo('APIException:UnrecognisedTypeCast'), $value['type'], $key, $method));
+ $msg = elgg_echo('APIException:UnrecognisedTypeCast', array($value['type'], $key, $method));
+ throw new APIException($msg);
}
}
@@ -685,8 +440,13 @@ function serialise_parameters($method, $parameters) {
/**
* PAM: Confirm that the call includes a valid API key
+ *
* @return true if good API key - otherwise throws exception
+ *
+ * @return mixed
* @throws APIException
+ * @since 1.7.0
+ * @access private
*/
function api_auth_key() {
global $CONFIG;
@@ -706,14 +466,18 @@ function api_auth_key() {
// can be used for keeping stats
// plugin can also return false to fail this authentication method
- return trigger_plugin_hook('api_key', 'use', $api_key, true);
+ return elgg_trigger_plugin_hook('api_key', 'use', $api_key, true);
}
/**
* PAM: Confirm the HMAC signature
+ *
* @return true if success - otherwise throws exception
+ *
* @throws SecurityException
+ * @since 1.7.0
+ * @access private
*/
function api_auth_hmac() {
global $CONFIG;
@@ -725,7 +489,8 @@ function api_auth_hmac() {
$api_user = get_api_user($CONFIG->site_id, $api_header->api_key);
if (!$api_user) {
- throw new SecurityException(elgg_echo('SecurityException:InvalidAPIKey'), ErrorResult::$RESULT_FAIL_APIKEY_INVALID);
+ throw new SecurityException(elgg_echo('SecurityException:InvalidAPIKey'),
+ ErrorResult::$RESULT_FAIL_APIKEY_INVALID);
}
// Get the secret key
@@ -754,12 +519,15 @@ function api_auth_hmac() {
}
// Validate post data
- if ($api_header->method=="POST") {
+ if ($api_header->method == "POST") {
$postdata = get_post_data();
$calculated_posthash = calculate_posthash($postdata, $api_header->posthash_algo);
- if (strcmp($api_header->posthash, $calculated_posthash)!=0) {
- throw new SecurityException(sprintf(elgg_echo('SecurityException:InvalidPostHash'), $calculated_posthash, $api_header->posthash));
+ if (strcmp($api_header->posthash, $calculated_posthash) != 0) {
+ $msg = elgg_echo('SecurityException:InvalidPostHash',
+ array($calculated_posthash, $api_header->posthash));
+
+ throw new SecurityException($msg);
}
}
@@ -774,6 +542,7 @@ function api_auth_hmac() {
*
* @return stdClass Containing all the values.
* @throws APIException Detailing any error.
+ * @access private
*/
function get_and_validate_api_headers() {
$result = new stdClass;
@@ -809,7 +578,7 @@ function get_and_validate_api_headers() {
// This values determines how long the HMAC cache needs to store previous
// signatures. Heavy use of HMAC is better handled with a shorter sig lifetime.
// See cache_hmac_check_replay()
- if (($result->time<(time()-90000)) || ($result->time>(time()+90000))) {
+ if (($result->time < (time() - 90000)) || ($result->time > (time() + 90000))) {
throw new APIException(elgg_echo('APIException:TemporalDrift'));
}
@@ -843,13 +612,15 @@ function get_and_validate_api_headers() {
* This also gives us an easy way to disable algorithms.
*
* @param string $algo The algorithm
+ *
* @return string The php algorithm
* @throws APIException if an algorithm is not supported.
+ * @access private
*/
function map_api_hash($algo) {
$algo = strtolower(sanitise_string($algo));
$supported_algos = array(
- "md5" => "md5", // TODO: Consider phasing this out
+ "md5" => "md5", // @todo Consider phasing this out
"sha" => "sha1", // alias for sha1
"sha1" => "sha1",
"sha256" => "sha256"
@@ -859,7 +630,7 @@ function map_api_hash($algo) {
return $supported_algos[$algo];
}
- throw new APIException(sprintf(elgg_echo('APIException:AlgorithmNotSupported'), $algo));
+ throw new APIException(elgg_echo('APIException:AlgorithmNotSupported', array($algo)));
}
/**
@@ -867,15 +638,21 @@ function map_api_hash($algo) {
* This function signs an api request using the information provided. The signature returned
* has been base64 encoded and then url encoded.
*
- * @param string $algo The HMAC algorithm used
- * @param string $time String representation of unix time
- * @param string $api_key Your api key
- * @param string $secret Your private key
- * @param string $get_variables URLEncoded string representation of the get variable parameters, eg "method=user&guid=2"
- * @param string $post_hash Optional sha1 hash of the post data.
+ * @param string $algo The HMAC algorithm used
+ * @param string $time String representation of unix time
+ * @param string $nonce Nonce
+ * @param string $api_key Your api key
+ * @param string $secret_key Your private key
+ * @param string $get_variables URLEncoded string representation of the get variable parameters,
+ * eg "method=user&guid=2"
+ * @param string $post_hash Optional sha1 hash of the post data.
+ *
* @return string The HMAC signature
+ * @access private
*/
-function calculate_hmac($algo, $time, $nonce, $api_key, $secret_key, $get_variables, $post_hash = "") {
+function calculate_hmac($algo, $time, $nonce, $api_key, $secret_key,
+$get_variables, $post_hash = "") {
+
global $CONFIG;
elgg_log("HMAC Parts: $algo, $time, $api_key, $secret_key, $get_variables, $post_hash");
@@ -886,7 +663,7 @@ function calculate_hmac($algo, $time, $nonce, $api_key, $secret_key, $get_variab
hash_update($ctx, trim($nonce));
hash_update($ctx, trim($api_key));
hash_update($ctx, trim($get_variables));
- if (trim($post_hash)!="") {
+ if (trim($post_hash) != "") {
hash_update($ctx, trim($post_hash));
}
@@ -896,11 +673,13 @@ function calculate_hmac($algo, $time, $nonce, $api_key, $secret_key, $get_variab
/**
* Calculate a hash for some post data.
*
- * TODO: Work out how to handle really large bits of data.
+ * @todo Work out how to handle really large bits of data.
+ *
+ * @param string $postdata The post data.
+ * @param string $algo The algorithm used.
*
- * @param string $postdata string The post data.
- * @param string $algo The algorithm used.
* @return string The hash.
+ * @access private
*/
function calculate_posthash($postdata, $algo) {
$ctx = hash_init(map_api_hash($algo));
@@ -915,7 +694,9 @@ function calculate_posthash($postdata, $algo) {
* hasn't been seen before, and secondly it will add the given hmac to the cache.
*
* @param string $hmac The hmac string.
+ *
* @return bool True if replay detected, false if not.
+ * @access private
*/
function cache_hmac_check_replay($hmac) {
// cache lifetime is 25 hours (this should be related to the time drift
@@ -937,6 +718,7 @@ function cache_hmac_check_replay($hmac) {
* Generate a new API user for a site, returning a new keypair on success.
*
* @param int $site_guid The GUID of the site. (default is current site)
+ *
* @return stdClass object or false
*/
function create_api_user($site_guid) {
@@ -948,8 +730,8 @@ function create_api_user($site_guid) {
$site_guid = (int)$site_guid;
- $public = sha1(rand().$site_guid.microtime());
- $secret = sha1(rand().$site_guid.microtime().$public);
+ $public = sha1(rand() . $site_guid . microtime());
+ $secret = sha1(rand() . $site_guid . microtime() . $public);
$insert = insert_data("INSERT into {$CONFIG->dbprefix}api_users
(site_guid, api_key, secret) values
@@ -963,10 +745,12 @@ function create_api_user($site_guid) {
}
/**
- * Find an API User's details based on the provided public api key. These users are not users in the traditional sense.
+ * Find an API User's details based on the provided public api key.
+ * These users are not users in the traditional sense.
+ *
+ * @param int $site_guid The GUID of the site.
+ * @param string $api_key The API Key
*
- * @param int $site_guid The GUID of the site.
- * @param string $api_key The API Key
* @return mixed stdClass representing the database row or false.
*/
function get_api_user($site_guid, $api_key) {
@@ -975,14 +759,18 @@ function get_api_user($site_guid, $api_key) {
$api_key = sanitise_string($api_key);
$site_guid = (int)$site_guid;
- return get_data_row("SELECT * from {$CONFIG->dbprefix}api_users where api_key='$api_key' and site_guid=$site_guid and active=1");
+ $query = "SELECT * from {$CONFIG->dbprefix}api_users"
+ . " where api_key='$api_key' and site_guid=$site_guid and active=1";
+
+ return get_data_row($query);
}
/**
* Revoke an api user key.
*
- * @param int $site_guid The GUID of the site.
- * @param string $api_key The API Key (public).
+ * @param int $site_guid The GUID of the site.
+ * @param string $api_key The API Key (public).
+ *
* @return bool
*/
function remove_api_user($site_guid, $api_key) {
@@ -997,7 +785,7 @@ function remove_api_user($site_guid, $api_key) {
}
-// User Authorization functions ////////////////////////////////////////////////////////////////
+// User Authorization functions
/**
* Check the user token
@@ -1005,10 +793,10 @@ function remove_api_user($site_guid, $api_key) {
* it is present and is valid. The user gets logged in so with the current
* session code of Elgg, that user will be logged out of all other sessions.
*
- * @param array/mixed $credentials
* @return bool
+ * @access private
*/
-function pam_auth_usertoken($credentials = NULL) {
+function pam_auth_usertoken() {
global $CONFIG;
$token = get_input('auth_token');
@@ -1027,7 +815,7 @@ function pam_auth_usertoken($credentials = NULL) {
}
// Not an elgg user
- if ( (!$u instanceof ElggUser)) {
+ if ((!$u instanceof ElggUser)) {
return false;
}
@@ -1049,19 +837,22 @@ function pam_auth_usertoken($credentials = NULL) {
/**
* See if the user has a valid login sesson
+ *
* @return bool
+ * @access private
*/
-function pam_auth_session($credentials = NULL) {
- return isloggedin();
+function pam_auth_session() {
+ return elgg_is_logged_in();
}
-// user token functions /////////////////////////////////////////////////////////////////////
+// user token functions
/**
* Obtain a token for a user.
*
* @param string $username The username
- * @param int $expire minutes until token expires (default is 60 minutes)
+ * @param int $expire Minutes until token expires (default is 60 minutes)
+ *
* @return bool
*/
function create_user_token($username, $expire = 60) {
@@ -1071,7 +862,7 @@ function create_user_token($username, $expire = 60) {
$user = get_user_by_username($username);
$time = time();
$time += 60 * $expire;
- $token = md5(rand(). microtime() . $username . $time . $site_guid);
+ $token = md5(rand() . microtime() . $username . $time . $site_guid);
if (!$user) {
return false;
@@ -1079,7 +870,8 @@ function create_user_token($username, $expire = 60) {
if (insert_data("INSERT into {$CONFIG->dbprefix}users_apisessions
(user_guid, site_guid, token, expires) values
- ({$user->guid}, $site_guid, '$token', '$time') on duplicate key update token='$token', expires='$time'")) {
+ ({$user->guid}, $site_guid, '$token', '$time')
+ on duplicate key update token='$token', expires='$time'")) {
return $token;
}
@@ -1091,8 +883,10 @@ function create_user_token($username, $expire = 60) {
*
* @param int $user_guid The user GUID
* @param int $site_guid The ID of the site (default is current site)
+ *
* @return false if none available or array of stdClass objects
* (see users_apisessions schema for available variables in objects)
+ * @since 1.7.0
*/
function get_user_tokens($user_guid, $site_guid) {
global $CONFIG;
@@ -1113,11 +907,12 @@ function get_user_tokens($user_guid, $site_guid) {
/**
* Validate a token against a given site.
*
- * A token registered with one site can not be used from a different apikey(site), so be aware of this
- * during development.
+ * A token registered with one site can not be used from a
+ * different apikey(site), so be aware of this during development.
+ *
+ * @param string $token The Token.
+ * @param int $site_guid The ID of the site (default is current site)
*
- * @param string $token The Token.
- * @param int $site_guid The ID of the site (default is current site)
* @return mixed The user id attached to the token if not expired or false.
*/
function validate_user_token($token, $site_guid) {
@@ -1145,9 +940,11 @@ function validate_user_token($token, $site_guid) {
/**
* Remove user token
*
- * @param string $token
- * @param int $site_guid The ID of the site (default is current site)
+ * @param string $token The toekn
+ * @param int $site_guid The ID of the site (default is current site)
+ *
* @return bool
+ * @since 1.7.0
*/
function remove_user_token($token, $site_guid) {
global $CONFIG;
@@ -1167,6 +964,7 @@ function remove_user_token($token, $site_guid) {
* Remove expired tokens
*
* @return bool
+ * @since 1.7.0
*/
function remove_expired_user_tokens() {
global $CONFIG;
@@ -1179,13 +977,15 @@ function remove_expired_user_tokens() {
where site_guid=$site_guid and expires < $time");
}
-// Client api functions ///////////////////////////////////////////////////////////////////
+// Client api functions
/**
* Utility function to serialise a header array into its text representation.
*
* @param array $headers The array of headers "key" => "value"
+ *
* @return string
+ * @access private
*/
function serialise_api_headers(array $headers) {
$headers_str = "";
@@ -1200,15 +1000,18 @@ function serialise_api_headers(array $headers) {
/**
* Send a raw API call to an elgg api endpoint.
*
- * @param array $keys The api keys.
- * @param string $url URL of the endpoint.
- * @param array $call Associated array of "variable" => "value"
- * @param string $method GET or POST
- * @param string $post_data The post data
+ * @param array $keys The api keys.
+ * @param string $url URL of the endpoint.
+ * @param array $call Associated array of "variable" => "value"
+ * @param string $method GET or POST
+ * @param string $post_data The post data
* @param string $content_type The content type
+ *
* @return string
*/
-function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_data = '', $content_type = 'application/octet-stream') {
+function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_data = '',
+$content_type = 'application/octet-stream') {
+
global $CONFIG;
$headers = array();
@@ -1220,7 +1023,7 @@ function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_da
case 'POST' :
break;
default:
- $msg = sprintf(elgg_echo('NotImplementedException:CallMethodNotImplemented'), $method);
+ $msg = elgg_echo('NotImplementedException:CallMethodNotImplemented', array($method));
throw new NotImplementedException($msg);
}
@@ -1231,8 +1034,8 @@ function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_da
$nonce = uniqid('');
// URL encode all the parameters
- foreach ($call as $k => $v){
- $encoded_params[] = urlencode($k).'='.urlencode($v);
+ foreach ($call as $k => $v) {
+ $encoded_params[] = urlencode($k) . '=' . urlencode($v);
}
$params = implode('&', $encoded_params);
@@ -1292,9 +1095,10 @@ function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_da
/**
* Send a GET call
*
- * @param string $url URL of the endpoint.
- * @param array $call Associated array of "variable" => "value"
- * @param array $keys The keys dependant on chosen authentication method
+ * @param string $url URL of the endpoint.
+ * @param array $call Associated array of "variable" => "value"
+ * @param array $keys The keys dependant on chosen authentication method
+ *
* @return string
*/
function send_api_get_call($url, array $call, array $keys) {
@@ -1304,33 +1108,40 @@ function send_api_get_call($url, array $call, array $keys) {
/**
* Send a GET call
*
- * @param string $url URL of the endpoint.
- * @param array $call Associated array of "variable" => "value"
- * @param array $keys The keys dependant on chosen authentication method
- * @param string $post_data The post data
+ * @param string $url URL of the endpoint.
+ * @param array $call Associated array of "variable" => "value"
+ * @param array $keys The keys dependant on chosen authentication method
+ * @param string $post_data The post data
* @param string $content_type The content type
+ *
* @return string
*/
-function send_api_post_call($url, array $call, array $keys, $post_data, $content_type = 'application/octet-stream') {
+function send_api_post_call($url, array $call, array $keys, $post_data,
+$content_type = 'application/octet-stream') {
+
return send_api_call($keys, $url, $call, 'POST', $post_data, $content_type);
}
/**
- * Return a key array suitable for the API client using the standard authentication method based on api-keys and secret keys.
+ * Return a key array suitable for the API client using the standard
+ * authentication method based on api-keys and secret keys.
*
* @param string $secret_key Your secret key
- * @param string $api_key Your api key
+ * @param string $api_key Your api key
+ *
* @return array
*/
function get_standard_api_key_array($secret_key, $api_key) {
return array('public' => $api_key, 'private' => $secret_key);
}
-// System functions ///////////////////////////////////////////////////////////////////////
+// System functions
/**
* Simple api to return a list of all api's installed on the system.
+ *
* @return array
+ * @access private
*/
function list_all_apis() {
global $API_METHODS;
@@ -1349,11 +1160,24 @@ function list_all_apis() {
*
* @param string $username Username
* @param string $password Clear text password
+ *
* @return string Token string or exception
* @throws SecurityException
+ * @access private
*/
function auth_gettoken($username, $password) {
- if (authenticate($username, $password)) {
+ // check if username is an email address
+ if (is_email_address($username)) {
+ $users = get_user_by_email($username);
+
+ // check if we have a unique user
+ if (is_array($users) && (count($users) == 1)) {
+ $username = $users[0]->username;
+ }
+ }
+
+ // validate username and password
+ if (true === elgg_authenticate($username, $password)) {
$token = create_user_token($username);
if ($token) {
return $token;
@@ -1363,7 +1187,7 @@ function auth_gettoken($username, $password) {
throw new SecurityException(elgg_echo('SecurityException:authenticationfailed'));
}
-// Error handler functions ////////////////////////////////////////////////////////////////
+// Error handler functions
/** Define a global array of errors */
$ERRORS = array();
@@ -1373,22 +1197,28 @@ $ERRORS = array();
* This function acts as a wrapper to catch and report PHP error messages.
*
* @see http://uk3.php.net/set-error-handler
- * @param int $errno
- * @param string $errmsg
- * @param string $filename
- * @param int $linenum
- * @param array $vars
- * @return none
+ *
+ * @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
+ *
+ * @throws Exception
*/
-function __php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
+function _php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
global $ERRORS;
- $error = date("Y-m-d H:i:s (T)") . ": \"" . $errmsg . "\" in file " . $filename . " (line " . $linenum . ")";
+ $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);
- $ERRORS[] = "ERROR: " .$error;
+ $ERRORS[] = "ERROR: " . $error;
// Since this is a fatal error, we want to stop any further execution but do so gracefully.
throw new Exception("ERROR: " . $error);
@@ -1397,12 +1227,12 @@ function __php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
case E_WARNING :
case E_USER_WARNING :
error_log("WARNING: " . $error);
- $ERRORS[] = "WARNING: " .$error;
+ $ERRORS[] = "WARNING: " . $error;
break;
default:
error_log("DEBUG: " . $error);
- $ERRORS[] = "DEBUG: " .$error;
+ $ERRORS[] = "DEBUG: " . $error;
}
}
@@ -1412,45 +1242,50 @@ function __php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
* uncaught exception, end API execution and return the result to the requestor
* as an ErrorResult in the requested format.
*
- * @param Exception $exception
- * @return none
+ * @param Exception $exception Exception
+ *
+ * @return void
+ * @access private
*/
-function __php_api_exception_handler($exception) {
+function _php_api_exception_handler($exception) {
error_log("*** FATAL EXCEPTION (API) *** : " . $exception);
$code = $exception->getCode() == 0 ? ErrorResult::$RESULT_FAIL : $exception->getCode();
$result = new ErrorResult($exception->getMessage(), $code, NULL);
- page_draw($exception->getMessage(), elgg_view("api/output", array("result" => $result)));
+ echo elgg_view_page($exception->getMessage(), elgg_view("api/output", array("result" => $result)));
}
-// Services handler ///////////////////////////////////////////
+// Services handler
/**
* Services handler - turns request over to the registered handler
* If no handler is found, this returns a 404 error
*
- * @param string $handler
- * @param array $request
+ * @param string $handler Handler name
+ * @param array $request Request string
+ *
+ * @return void
+ * @access private
*/
function service_handler($handler, $request) {
global $CONFIG;
- set_context('api');
+ elgg_set_context('api');
- $request = explode('/',$request);
+ $request = explode('/', $request);
// after the handler, the first identifier is response format
- // ex) http://example.org/services/api/rest/xml/?method=test
- $reponse_format = array_shift($request);
+ // ex) http://example.org/services/api/rest/json/?method=test
+ $response_format = array_shift($request);
// Which view - xml, json, ...
- if ($reponse_format) {
- elgg_set_viewtype($reponse_format);
+ if ($response_format && elgg_is_valid_view_type($response_format)) {
+ elgg_set_viewtype($response_format);
} else {
- // default to xml
- elgg_set_viewtype("xml");
+ // default to json
+ elgg_set_viewtype("json");
}
if (!isset($CONFIG->servicehandler) || empty($handler)) {
@@ -1459,7 +1294,7 @@ function service_handler($handler, $request) {
exit;
} else if (isset($CONFIG->servicehandler[$handler]) && is_callable($CONFIG->servicehandler[$handler])) {
$function = $CONFIG->servicehandler[$handler];
- $function($request, $handler);
+ call_user_func($function, $request, $handler);
} else {
// no handler for this web service
header("HTTP/1.0 404 Not Found");
@@ -1470,16 +1305,19 @@ function service_handler($handler, $request) {
/**
* Registers a web services handler
*
- * @param string $handler web services type
+ * @param string $handler Web services type
* @param string $function Your function name
- * @return true|false Depending on success
+ *
+ * @return bool Depending on success
+ * @since 1.7.0
*/
function register_service_handler($handler, $function) {
global $CONFIG;
+
if (!isset($CONFIG->servicehandler)) {
$CONFIG->servicehandler = array();
}
- if (is_callable($function)) {
+ if (is_callable($function, true)) {
$CONFIG->servicehandler[$handler] = $function;
return true;
}
@@ -1493,32 +1331,90 @@ function register_service_handler($handler, $function) {
* with register_service_handler().
*
* @param string $handler web services type
+ *
+ * @return void
+ * @since 1.7.0
*/
function unregister_service_handler($handler) {
global $CONFIG;
- if (isset($CONFIG->servicehandler) && isset($CONFIG->servicehandler[$handler])) {
+
+ if (isset($CONFIG->servicehandler, $CONFIG->servicehandler[$handler])) {
unset($CONFIG->servicehandler[$handler]);
}
}
-// REST handler //////////////////////////////////////////////////////////////
-
/**
* REST API handler
+ *
+ * @return void
+ * @access private
+ *
+ * @throws SecurityException|APIException
*/
function rest_handler() {
global $CONFIG;
- require $CONFIG->path . "services/api/rest_api.php";
+ // Register the error handler
+ error_reporting(E_ALL);
+ set_error_handler('_php_api_error_handler');
+
+ // Register a default exception handler
+ set_exception_handler('_php_api_exception_handler');
+
+ // Check to see if the api is available
+ if ((isset($CONFIG->disable_api)) && ($CONFIG->disable_api == true)) {
+ throw new SecurityException(elgg_echo('SecurityException:APIAccessDenied'));
+ }
+
+ // plugins should return true to control what API and user authentication handlers are registered
+ if (elgg_trigger_plugin_hook('rest', 'init', null, false) == false) {
+ // for testing from a web browser, you can use the session PAM
+ // do not use for production sites!!
+ //register_pam_handler('pam_auth_session');
+
+ // user token can also be used for user authentication
+ register_pam_handler('pam_auth_usertoken');
+
+ // simple API key check
+ register_pam_handler('api_auth_key', "sufficient", "api");
+ // hmac
+ register_pam_handler('api_auth_hmac', "sufficient", "api");
+ }
+
+ // Get parameter variables
+ $method = get_input('method');
+ $result = null;
+
+ // this will throw an exception if authentication fails
+ authenticate_method($method);
+
+ $result = execute_method($method);
+
+
+ if (!($result instanceof GenericResult)) {
+ throw new APIException(elgg_echo('APIException:ApiResultUnknown'));
+ }
+
+ // Output the result
+ echo elgg_view_page($method, elgg_view("api/output", array("result" => $result)));
}
-// Initialisation /////////////////////////////////////////////////////////////
+// Initialization
/**
* Unit tests for API
+ *
+ * @param string $hook unit_test
+ * @param string $type system
+ * @param mixed $value Array of tests
+ * @param mixed $params Params
+ *
+ * @return array
+ * @access private
*/
function api_unit_test($hook, $type, $value, $params) {
global $CONFIG;
+
$value[] = $CONFIG->path . 'engine/tests/services/api.php';
return $value;
}
@@ -1526,27 +1422,33 @@ function api_unit_test($hook, $type, $value, $params) {
/**
* Initialise the API subsystem.
*
+ * @return void
+ * @access private
*/
function api_init() {
// Register a page handler, so we can have nice URLs
- register_service_handler('rest','rest_handler');
+ register_service_handler('rest', 'rest_handler');
- register_plugin_hook('unit_test', 'system', 'api_unit_test');
+ elgg_register_plugin_hook_handler('unit_test', 'system', 'api_unit_test');
// expose the list of api methods
- expose_function("system.api.list", "list_all_apis", NULL, elgg_echo("system.api.list"), "GET", false, false);
+ expose_function("system.api.list", "list_all_apis", NULL,
+ elgg_echo("system.api.list"), "GET", false, false);
// The authentication token api
- expose_function("auth.gettoken",
- "auth_gettoken", array(
- 'username' => array ('type' => 'string'),
- 'password' => array ('type' => 'string'),
- ),
- elgg_echo('auth.gettoken'),
- 'POST',
- false,
- false);
+ expose_function(
+ "auth.gettoken",
+ "auth_gettoken",
+ array(
+ 'username' => array ('type' => 'string'),
+ 'password' => array ('type' => 'string'),
+ ),
+ elgg_echo('auth.gettoken'),
+ 'POST',
+ false,
+ false
+ );
}
-register_elgg_event_handler('init','system','api_init');
+elgg_register_event_handler('init', 'system', 'api_init');
diff --git a/engine/lib/widgets.php b/engine/lib/widgets.php
index 7884f263a..699462a1b 100644
--- a/engine/lib/widgets.php
+++ b/engine/lib/widgets.php
@@ -3,290 +3,178 @@
* Elgg widgets library.
* Contains code for handling widgets.
*
- * @package Elgg
- * @subpackage Core
- * @link http://elgg.org/
+ * @package Elgg.Core
+ * @subpackage Widgets
*/
/**
- * Override ElggObject in order to store widget data in ultra-private stores.
+ * Get widgets for a particular context
+ *
+ * The widgets are ordered for display and grouped in columns.
+ * $widgets = elgg_get_widgets(elgg_get_logged_in_user_guid(), 'dashboard');
+ * $first_column_widgets = $widgets[1];
+ *
+ * @param int $user_guid The owner user GUID
+ * @param string $context The context (profile, dashboard, etc)
+ *
+ * @return array An 2D array of ElggWidget objects
+ * @since 1.8.0
*/
-class ElggWidget extends ElggObject {
- protected function initialise_attributes() {
- parent::initialise_attributes();
-
- $this->attributes['subtype'] = "widget";
- }
-
- public function __construct($guid = null) {
- parent::__construct($guid);
+function elgg_get_widgets($user_guid, $context) {
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'widget',
+ 'owner_guid' => $user_guid,
+ 'private_setting_name' => 'context',
+ 'private_setting_value' => $context,
+ 'limit' => 0
+ );
+ $widgets = elgg_get_entities_from_private_settings($options);
+ if (!$widgets) {
+ return array();
}
- /**
- * Override entity get and sets in order to save data to private data store.
- */
- public function get($name) {
- // See if its in our base attribute
- if (isset($this->attributes[$name])) {
- return $this->attributes[$name];
- }
-
- // No, so see if its in the private data store.
- $meta = get_private_setting($this->guid, $name);
- if ($meta) {
- return $meta;
+ $sorted_widgets = array();
+ foreach ($widgets as $widget) {
+ if (!isset($sorted_widgets[(int)$widget->column])) {
+ $sorted_widgets[(int)$widget->column] = array();
}
-
- // Can't find it, so return null
- return null;
+ $sorted_widgets[(int)$widget->column][$widget->order] = $widget;
}
- /**
- * Override entity get and sets in order to save data to private data store.
- */
- public function set($name, $value) {
- if (array_key_exists($name, $this->attributes)) {
- // Check that we're not trying to change the guid!
- if ((array_key_exists('guid', $this->attributes)) && ($name=='guid')) {
- return false;
- }
-
- $this->attributes[$name] = $value;
- } else {
- return set_private_setting($this->guid, $name, $value);
- }
-
- return true;
+ foreach ($sorted_widgets as $col => $widgets) {
+ ksort($sorted_widgets[$col]);
}
+
+ return $sorted_widgets;
}
/**
- * Register a particular context for use with widgets.
+ * Create a new widget instance
*
- * @param string $context The context we wish to enable context for
+ * @param int $owner_guid GUID of entity that owns this widget
+ * @param string $handler The handler for this widget
+ * @param string $context The context for this widget
+ * @param int $access_id If not specified, it is set to the default access level
+ *
+ * @return int|false Widget GUID or false on failure
+ * @since 1.8.0
*/
-function use_widgets($context) {
- global $CONFIG;
-
- if (!isset($CONFIG->widgets)) {
- $CONFIG->widgets = new stdClass;
+function elgg_create_widget($owner_guid, $handler, $context, $access_id = null) {
+ if (empty($owner_guid) || empty($handler) || !elgg_is_widget_type($handler)) {
+ return false;
}
- if (!isset($CONFIG->widgets->contexts)) {
- $CONFIG->widgets->contexts = array();
+ $owner = get_entity($owner_guid);
+ if (!$owner) {
+ return false;
}
- if (!empty($context)) {
- $CONFIG->widgets->contexts[] = $context;
+ $widget = new ElggWidget;
+ $widget->owner_guid = $owner_guid;
+ $widget->container_guid = $owner_guid; // @todo - will this work for group widgets
+ if (isset($access_id)) {
+ $widget->access_id = $access_id;
+ } else {
+ $widget->access_id = get_default_access();
}
-}
-/**
- * Determines whether or not the current context is using widgets
- *
- * @return true|false Depending on widget status
- */
-function using_widgets() {
- global $CONFIG;
-
- $context = get_context();
- if (isset($CONFIG->widgets->contexts) && is_array($CONFIG->widgets->contexts)) {
- if (in_array($context, $CONFIG->widgets->contexts)) return true;
+ if (!$widget->save()) {
+ return false;
}
- return false;
+ // private settings cannot be set until ElggWidget saved
+ $widget->handler = $handler;
+ $widget->context = $context;
+
+ return $widget->getGUID();
}
/**
- * When given a widget entity and a new requested location, saves the new location
- * and also provides a sensible ordering for all widgets in that column
+ * Can the user edit the widget layout
*
- * @param ElggObject $widget The widget entity
- * @param int $order The order within the column
- * @param int $column The column (1, 2 or 3)
- * @return true|false Depending on success
+ * Triggers a 'permissions_check', 'widget_layout' plugin hook
+ *
+ * @param string $context The widget context
+ * @param int $user_guid The GUID of the user (0 for logged in user)
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function save_widget_location(ElggObject $widget, $order, $column) {
- if ($widget instanceof ElggObject) {
- if ($widget->subtype == "widget") {
- // If you can't move the widget, don't save a new location
- if (!$widget->draggable) {
- return false;
- }
-
- // Sanitise the column value
- if ($column != 1 || $column != 2 || $column != 3) {
- $column = 1;
- }
-
- $widget->column = (int) $column;
-
- $ordertmp = array();
- $params = array(
- 'context' => $widget->context,
- 'column' => $column,
- );
-
- if ($entities = get_entities_from_metadata_multi($params,'object','widget')) {
- foreach($entities as $entity) {
- $entityorder = $entity->order;
- if ($entityorder < $order) {
- $ordertmp[$entityorder] = $entity;
- }
- if ($entityorder >= $order) {
- $ordertmp[$entityorder + 10000] = $entity;
- }
- }
- }
-
- $ordertmp[$order] = $widget;
- ksort($ordertmp);
-
- $orderticker = 10;
- foreach($ordertmp as $orderval => $entity) {
- $entity->order = $orderticker;
- $orderticker += 10;
- }
-
- return true;
- } else {
- register_error($widget->subtype);
- }
+function elgg_can_edit_widget_layout($context, $user_guid = 0) {
+ $user = get_entity((int)$user_guid);
+ if (!$user) {
+ $user = elgg_get_logged_in_user_entity();
}
- return false;
-}
+ $return = false;
+ if (elgg_is_admin_logged_in()) {
+ $return = true;
+ }
+ if (elgg_get_page_owner_guid() == $user->guid) {
+ $return = true;
+ }
-/**
- * Get widgets for a particular context and column, in order of display
- *
- * @param int $user_guid The owner user GUID
- * @param string $context The context (profile, dashboard etc)
- * @param int $column The column (1 or 2)
- * @return array|false An array of widget ElggObjects, or false
- */
-function get_widgets($user_guid, $context, $column) {
$params = array(
- 'column' => $column,
- 'context' => $context
+ 'user' => $user,
+ 'context' => $context,
+ 'page_owner' => elgg_get_page_owner_entity()
);
- $widgets = get_entities_from_private_setting_multi($params, "object", "widget", $user_guid, "", 10000);
- if ($widgets) {
- $widgetorder = array();
- foreach($widgets as $widget) {
- $order = $widget->order;
- while(isset($widgetorder[$order])) {
- $order++;
- }
- $widgetorder[$order] = $widget;
- }
-
- ksort($widgetorder);
-
- return $widgetorder;
- }
-
- return false;
+ return elgg_trigger_plugin_hook('permissions_check', 'widget_layout', $params, $return);
}
/**
- * Displays a particular widget
+ * Regsiter a widget type
*
- * @param ElggObject $widget The widget to display
- * @return string The HTML for the widget, including JavaScript wrapper
- */
-function display_widget(ElggObject $widget) {
- return elgg_view_entity($widget);
-}
-
-/**
- * Add a new widget
+ * This should be called by plugins in their init function.
*
- * @param int $user_guid User GUID to associate this widget with
- * @param string $handler The handler for this widget
- * @param string $context The page context for this widget
- * @param int $order The order to display this widget in
- * @param int $column The column to display this widget in (1, 2 or 3)
- * @param int $access_id If not specified, it is set to the default access level
- * @return true|false Depending on success
+ * @param string $handler The identifier for the widget handler
+ * @param string $name The name of the widget type
+ * @param string $description A description for the widget type
+ * @param string $context A comma-separated list of contexts where this
+ * widget is allowed (default: 'all')
+ * @param bool $multiple Whether or not multiple instances of this widget
+ * are allowed in a single layout (default: false)
+ *
+ * @return bool
+ * @since 1.8.0
*/
-function add_widget($user_guid, $handler, $context, $order = 0, $column = 1, $access_id = null) {
- if (empty($user_guid) || empty($context) || empty($handler) || !widget_type_exists($handler)) {
+function elgg_register_widget_type($handler, $name, $description, $context = "all", $multiple = false) {
+
+ if (!$handler || !$name) {
return false;
}
- if ($user = get_user($user_guid)) {
- $widget = new ElggWidget;
- $widget->owner_guid = $user_guid;
- $widget->container_guid = $user_guid;
- if (isset($access_id)) {
- $widget->access_id = $access_id;
- } else {
- $widget->access_id = get_default_access();
- }
-
- if (!$widget->save()) {
- return false;
- }
-
- $widget->handler = $handler;
- $widget->context = $context;
- $widget->column = $column;
- $widget->order = $order;
-
- // save_widget_location($widget, $order, $column);
- return true;
+ global $CONFIG;
+ if (!isset($CONFIG->widgets)) {
+ $CONFIG->widgets = new stdClass;
+ }
+ if (!isset($CONFIG->widgets->handlers)) {
+ $CONFIG->widgets->handlers = array();
}
- return false;
-}
-
-/**
- * Define a new widget type
- *
- * @param string $handler The identifier for the widget handler
- * @param string $name The name of the widget type
- * @param string $description A description for the widget type
- * @param string $context A comma-separated list of contexts where this widget is allowed (default: 'all')
- * @param true|false $multiple Whether or not multiple instances of this widget are allowed on a single dashboard (default: false)
- * @param string $position A comma-separated list of positions on the page (side or main) where this widget is allowed (default: "side,main")
- * @return true|false Depending on success
- */
-function add_widget_type($handler, $name, $description, $context = "all", $multiple = false, $positions = "side,main") {
- if (!empty($handler) && !empty($name)) {
- global $CONFIG;
-
- if (!isset($CONFIG->widgets)) {
- $CONFIG->widgets = new stdClass;
- }
-
- if (!isset($CONFIG->widgets->handlers)) {
- $CONFIG->widgets->handlers = array();
- }
-
- $handlerobj = new stdClass;
- $handlerobj->name = $name;
- $handlerobj->description = $description;
- $handlerobj->context = explode(",",$context);
- $handlerobj->multiple = $multiple;
- $handlerobj->positions = explode(",",$positions);
-
- $CONFIG->widgets->handlers[$handler] = $handlerobj;
+ $handlerobj = new stdClass;
+ $handlerobj->name = $name;
+ $handlerobj->description = $description;
+ $handlerobj->context = explode(",", $context);
+ $handlerobj->multiple = $multiple;
- return true;
- }
+ $CONFIG->widgets->handlers[$handler] = $handlerobj;
- return false;
+ return true;
}
/**
* Remove a widget type
*
- * @param string $handler The identifier for the widget handler
+ * @param string $handler The identifier for the widget
+ *
+ * @return void
+ * @since 1.8.0
*/
-function remove_widget_type($handler) {
+function elgg_unregister_widget_type($handler) {
global $CONFIG;
if (!isset($CONFIG->widgets)) {
@@ -303,224 +191,230 @@ function remove_widget_type($handler) {
}
/**
- * Determines whether or not widgets with the specified handler have been defined
+ * Has a widget type with the specified handler been registered
*
* @param string $handler The widget handler identifying string
- * @return true|false Whether or not those widgets exist
+ *
+ * @return bool Whether or not that widget type exists
+ * @since 1.8.0
*/
-function widget_type_exists($handler) {
+function elgg_is_widget_type($handler) {
global $CONFIG;
- if (!empty($CONFIG->widgets)
- && !empty($CONFIG->widgets->handlers)
- && is_array($CONFIG->widgets->handlers)
- && array_key_exists($handler, $CONFIG->widgets->handlers)) {
- return true;
+ if (!empty($CONFIG->widgets) &&
+ !empty($CONFIG->widgets->handlers) &&
+ is_array($CONFIG->widgets->handlers) &&
+ array_key_exists($handler, $CONFIG->widgets->handlers)) {
+
+ return true;
}
return false;
}
/**
- * Returns an array of stdClass objects representing the defined widget types
+ * Get the widget types for a context
*
- * @return array A list of types defined (if any)
+ * The widget types are stdClass objects.
+ *
+ * @param string $context The widget context or empty string for current context
+ * @param bool $exact Only return widgets registered for this context (false)
+ *
+ * @return array
+ * @since 1.8.0
*/
-function get_widget_types() {
+function elgg_get_widget_types($context = "", $exact = false) {
global $CONFIG;
- if (!empty($CONFIG->widgets)
- && !empty($CONFIG->widgets->handlers)
- && is_array($CONFIG->widgets->handlers)) {
+ if (empty($CONFIG->widgets) ||
+ empty($CONFIG->widgets->handlers) ||
+ !is_array($CONFIG->widgets->handlers)) {
+ // no widgets
+ return array();
+ }
- $context = get_context();
+ if (!$context) {
+ $context = elgg_get_context();
+ }
- foreach($CONFIG->widgets->handlers as $key => $handler) {
- if (!in_array('all',$handler->context) &&
- !in_array($context,$handler->context)) {
- unset($CONFIG->widgets->handlers[$key]);
- }
+ $widgets = array();
+ foreach ($CONFIG->widgets->handlers as $key => $handler) {
+ if ($exact) {
+ if (in_array($context, $handler->context)) {
+ $widgets[$key] = $handler;
+ }
+ } else {
+ if (in_array('all', $handler->context) || in_array($context, $handler->context)) {
+ $widgets[$key] = $handler;
}
-
- return $CONFIG->widgets->handlers;
}
+ }
- return array();
+ return $widgets;
}
/**
- * Saves a widget's settings (by passing an array of (name => value) pairs to save_{$handler}_widget)
+ * Regsiter entity of object, widget as ElggWidget objects
*
- * @param int $widget_guid The GUID of the widget we're saving to
- * @param array $params An array of name => value parameters
+ * @return void
+ * @access private
*/
-function save_widget_info($widget_guid, $params) {
- if ($widget = get_entity($widget_guid)) {
-
- $subtype = $widget->getSubtype();
-
- if ($subtype != "widget") {
- return false;
- }
- $handler = $widget->handler;
- if (empty($handler) || !widget_type_exists($handler)) {
- return false;
- }
-
- if (!$widget->canEdit()) {
- return false;
- }
-
- // Save the params to the widget
- if (is_array($params) && sizeof($params) > 0) {
- foreach($params as $name => $value) {
-
- if (!empty($name) && !in_array($name, array(
- 'guid','owner_guid','site_guid'
- ))) {
- if (is_array($value)) {
- // TODO: Handle arrays securely
- $widget->setMetaData($name, $value, "", true);
- } else {
- $widget->$name = $value;
- }
- }
- }
- $widget->save();
- }
-
- $function = "save_{$handler}_widget";
- if (is_callable($function)) {
- return $function($params);
- }
-
- return true;
- }
-
- return false;
+function elgg_widget_run_once() {
+ add_subtype("object", "widget", "ElggWidget");
}
-function reorder_widgets_from_panel($panelstring1, $panelstring2, $panelstring3, $context, $owner) {
- $return = true;
+/**
+ * Function to initialize widgets functionality
+ *
+ * @return void
+ * @access private
+ */
+function elgg_widgets_init() {
+ elgg_register_action('widgets/save');
+ elgg_register_action('widgets/add');
+ elgg_register_action('widgets/move');
+ elgg_register_action('widgets/delete');
+ elgg_register_action('widgets/upgrade', '', 'admin');
+
+ run_function_once("elgg_widget_run_once");
+}
- $mainwidgets = explode('::',$panelstring1);
- $sidewidgets = explode('::',$panelstring2);
- $rightwidgets = explode('::',$panelstring3);
+/**
+ * Gets a list of events to create default widgets for and
+ * register menu items for default widgets with the admin section.
+ *
+ * A plugin that wants to register a new context for default widgets should
+ * register for the plugin hook 'get_list', 'default_widgets'. The handler
+ * can register the new type of default widgets by adding an associate array to
+ * the return value array like this:
+ * array(
+ * 'name' => elgg_echo('profile'),
+ * 'widget_context' => 'profile',
+ * 'widget_columns' => 3,
+ *
+ * 'event' => 'create',
+ * 'entity_type' => 'user',
+ * 'entity_subtype' => ELGG_ENTITIES_ANY_VALUE,
+ * );
+ *
+ * The first set of keys define information about the new type of default
+ * widgets and the second set determine what event triggers the creation of the
+ * new widgets.
+ *
+ * @return void
+ * @access private
+ */
+function elgg_default_widgets_init() {
+ global $CONFIG;
+ $default_widgets = elgg_trigger_plugin_hook('get_list', 'default_widgets', null, array());
- $handlers = array();
- $guids = array();
+ $CONFIG->default_widget_info = $default_widgets;
- if (is_array($mainwidgets) && sizeof($mainwidgets) > 0) {
- foreach($mainwidgets as $widget) {
+ if ($default_widgets) {
+ elgg_register_admin_menu_item('configure', 'default_widgets', 'appearance');
- $guid = (int) $widget;
+ // override permissions for creating widget on logged out / just created entities
+ elgg_register_plugin_hook_handler('container_permissions_check', 'object', 'elgg_default_widgets_permissions_override');
- if ("{$guid}" == "{$widget}") {
- $guids[1][] = $widget;
- } else {
- $handlers[1][] = $widget;
- }
+ // only register the callback once per event
+ $events = array();
+ foreach ($default_widgets as $info) {
+ $events[$info['event'] . ',' . $info['entity_type']] = $info;
}
- }
- if (is_array($sidewidgets) && sizeof($sidewidgets) > 0) {
- foreach($sidewidgets as $widget) {
-
- $guid = (int) $widget;
-
- if ("{$guid}" == "{$widget}") {
- $guids[2][] = $widget;
- } else {
- $handlers[2][] = $widget;
- }
-
+ foreach ($events as $info) {
+ elgg_register_event_handler($info['event'], $info['entity_type'], 'elgg_create_default_widgets');
}
}
- if (is_array($rightwidgets) && sizeof($rightwidgets) > 0) {
- foreach($rightwidgets as $widget) {
-
- $guid = (int) $widget;
+}
- if ("{$guid}" == "{$widget}") {
- $guids[3][] = $widget;
- } else {
- $handlers[3][] = $widget;
- }
+/**
+ * Creates default widgets
+ *
+ * This plugin hook handler is registered for events based on what kinds of
+ * default widgets have been registered. See elgg_default_widgets_init() for
+ * information on registering new default widget contexts.
+ *
+ * @param string $event The event
+ * @param string $type The type of object
+ * @param ElggEntity $entity The entity being created
+ * @return void
+ * @access private
+ */
+function elgg_create_default_widgets($event, $type, $entity) {
+ $default_widget_info = elgg_get_config('default_widget_info');
- }
+ if (!$default_widget_info || !$entity) {
+ return;
}
- // Reorder existing widgets or delete ones that have vanished
- foreach (array(1,2,3) as $column) {
- if ($dbwidgets = get_widgets($owner,$context,$column)) {
-
- foreach($dbwidgets as $dbwidget) {
- if (in_array($dbwidget->getGUID(),$guids[1]) || in_array($dbwidget->getGUID(),$guids[2]) || in_array($dbwidget->getGUID(),$guids[3])) {
- if (in_array($dbwidget->getGUID(),$guids[1])) {
- $pos = array_search($dbwidget->getGUID(),$guids[1]);
- $col = 1;
- } else if (in_array($dbwidget->getGUID(),$guids[2])) {
- $pos = array_search($dbwidget->getGUID(),$guids[2]);
- $col = 2;
- } else {
- $pos = array_search($dbwidget->getGUID(),$guids[3]);
- $col = 3;
+ $type = $entity->getType();
+ $subtype = $entity->getSubtype();
+
+ // event is already guaranteed by the hook registration.
+ // need to check subtype and type.
+ foreach ($default_widget_info as $info) {
+ if ($info['entity_type'] == $type) {
+ if ($info['entity_subtype'] == ELGG_ENTITIES_ANY_VALUE || $info['entity_subtype'] == $subtype) {
+
+ // need to be able to access everything
+ $old_ia = elgg_set_ignore_access(true);
+ elgg_push_context('create_default_widgets');
+
+ // pull in by widget context with widget owners as the site
+ // not using elgg_get_widgets() because it sorts by columns and we don't care right now.
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'widget',
+ 'owner_guid' => elgg_get_site_entity()->guid,
+ 'private_setting_name' => 'context',
+ 'private_setting_value' => $info['widget_context'],
+ 'limit' => 0
+ );
+
+ $widgets = elgg_get_entities_from_private_settings($options);
+ /* @var ElggWidget[] $widgets */
+
+ foreach ($widgets as $widget) {
+ // change the container and owner
+ $new_widget = clone $widget;
+ $new_widget->container_guid = $entity->guid;
+ $new_widget->owner_guid = $entity->guid;
+
+ // pull in settings
+ $settings = get_all_private_settings($widget->guid);
+
+ foreach ($settings as $name => $value) {
+ $new_widget->$name = $value;
}
- $pos = ($pos + 1) * 10;
- $dbwidget->column = $col;
- $dbwidget->order = $pos;
- } else {
- $dbguid = $dbwidget->getGUID();
- if (!$dbwidget->delete()) {
- $return = false;
- } else {
- // Remove state cookie
- setcookie('widget' + $dbguid, null);
- }
- }
- }
- }
- // Add new ones
- if (sizeof($guids[$column]) > 0) {
- foreach($guids[$column] as $key => $guid) {
- if ($guid == 0) {
- $pos = ($key + 1) * 10;
- $handler = $handlers[$column][$key];
- if (!add_widget($owner,$handler,$context,$pos,$column)) {
- $return = false;
- }
+ $new_widget->save();
}
+
+ elgg_set_ignore_access($old_ia);
+ elgg_pop_context();
}
}
}
-
- return $return;
-}
-
-/**
- * Run some things once.
- *
- */
-function widget_run_once() {
- // Register a class
- add_subtype("object", "widget", "ElggWidget");
}
/**
- * Function to initialise widgets functionality on Elgg init
+ * Overrides permissions checks when creating widgets for logged out users.
*
+ * @param string $hook The permissions hook.
+ * @param string $type The type of entity being created.
+ * @param string $return Value
+ * @param mixed $params Params
+ * @return true|null
+ * @access private
*/
-function widgets_init() {
- register_action('widgets/reorder');
- register_action('widgets/save');
- register_action('widgets/add');
+function elgg_default_widgets_permissions_override($hook, $type, $return, $params) {
+ if ($type == 'object' && $params['subtype'] == 'widget') {
+ return elgg_in_context('create_default_widgets') ? true : null;
+ }
- // Now run this stuff, but only once
- run_function_once("widget_run_once");
+ return null;
}
-// Register event
-register_elgg_event_handler('init','system','widgets_init');
-
-// Use widgets on the dashboard
-use_widgets('dashboard'); \ No newline at end of file
+elgg_register_event_handler('init', 'system', 'elgg_widgets_init');
+// register default widget hooks from plugins
+elgg_register_event_handler('ready', 'system', 'elgg_default_widgets_init');
diff --git a/engine/lib/xml-rpc.php b/engine/lib/xml-rpc.php
index 7c369edc1..bfe1a8645 100644
--- a/engine/lib/xml-rpc.php
+++ b/engine/lib/xml-rpc.php
@@ -1,557 +1,203 @@
<?php
- /**
- * Elgg XML-RPC library.
- * Contains functions and classes to handle XML-RPC services, currently only server only.
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-
- // XMLRPC Call ////////////////////////////////////////////////////////////////////////////
-
- /**
- * @class XMLRPCCall
- * This class represents
- * @author Curverider Ltd
- */
- class XMLRPCCall
- {
- /** Method name */
- private $methodname;
- /** Parameters */
- private $params;
-
- /**
- * Construct a new XML RPC Call
- *
- * @param string $xml
- */
- function __construct($xml)
- {
- $this->parse($xml);
- }
-
- /**
- * Return the method name associated with the call.
- *
- * @return string
- */
- public function getMethodName() { return $this->methodname; }
-
- /**
- * Return the parameters.
- * Returns a nested array of XmlElement.
- *
- * @see XmlElement
- * @return array
- */
- public function getParameters() { return $this->params; }
-
- /**
- * Parse the xml into its components according to spec.
- * This first version is a little primitive.
- *
- * @param string $xml
- */
- private function parse($xml)
- {
- $xml = xml_to_object($xml);
-
- // sanity check
- if ((isset($xml->name)) && (strcasecmp($xml->name, "methodCall")!=0))
- throw new CallException(elgg_echo('CallException:NotRPCCall'));
-
- // method name
- $this->methodname = $xml->children[0]->content;
-
- // parameters
- $this->params = $xml->children[1]->children;
- }
- }
-
- // Response classes ///////////////////////////////////////////////////////////////////////
-
- /**
- * @class XMLRPCParameter Superclass for all RPC parameters.
- * @author Curverider Ltd
- */
- abstract class XMLRPCParameter
- {
- protected $value;
-
- function __construct() { }
-
- }
-
- /**
- * @class XMLRPCIntParameter An Integer.
- * @author Curverider Ltd
- */
- class XMLRPCIntParameter extends XMLRPCParameter
- {
- function __construct($value)
- {
- parent::__construct();
-
- $this->value = (int)$value;
- }
-
- function __toString()
- {
- return "<value><i4>{$this->value}</i4></value>";
- }
- }
-
- /**
- * @class XMLRPCBoolParameter A boolean.
- * @author Curverider Ltd
- */
- class XMLRPCBoolParameter extends XMLRPCParameter
- {
- function __construct($value)
- {
- parent::__construct();
-
- $this->value = (bool)$value;
- }
-
- function __toString()
- {
- $code = ($this->value) ? "1" : "0";
- return "<value><boolean>{$code}</boolean></value>";
- }
- }
-
- /**
- * @class XMLRPCStringParameter A string.
- * @author Curverider Ltd
- */
- class XMLRPCStringParameter extends XMLRPCParameter
- {
- function __construct($value)
- {
- parent::__construct();
-
- $this->value = $value;
- }
-
- function __toString()
- {
- $value = htmlentities($this->value);
- return "<value><string>{$value}</string></value>";
- }
- }
-
- /**
- * @class XMLRPCDoubleParameter A double precision signed floating point number.
- * @author Curverider Ltd
- */
- class XMLRPCDoubleParameter extends XMLRPCParameter
- {
- function __construct($value)
- {
- parent::__construct();
-
- $this->value = (float)$value;
- }
-
- function __toString()
- {
- return "<value><double>{$this->value}</double></value>";
- }
- }
-
- /**
- * @class XMLRPCDateParameter An ISO8601 data and time.
- * @author Curverider Ltd
- */
- class XMLRPCDateParameter extends XMLRPCParameter
- {
- /**
- * Construct a date
- *
- * @param int $timestamp The unix timestamp, or blank for "now".
- */
- function __construct($timestamp = 0)
- {
- parent::__construct();
-
- $this->value = $timestamp;
- if (!$timestamp)
- $this->value = time();
- }
-
- function __toString()
- {
- $value = date('c', $this->value);
- return "<value><dateTime.iso8601>{$value}</dateTime.iso8601></value>";
- }
- }
-
- /**
- * @class XMLRPCBase64Parameter A base 64 encoded blob of binary.
- * @author Curverider Ltd
- */
- class XMLRPCBase64Parameter extends XMLRPCParameter
- {
- /**
- * Construct a base64 encoded block
- *
- * @param string $blob Unencoded binary blob
- */
- function __construct($blob)
- {
- parent::__construct();
-
- $this->value = base64_encode($blob);
- }
-
- function __toString()
- {
- return "<value><base64>{$value}</base64></value>";
- }
+/**
+ * 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);
}
-
- /**
- * @class XMLRPCStructParameter A structure containing other XMLRPCParameter objects.
- * @author Curverider Ltd
- */
- class XMLRPCStructParameter extends XMLRPCParameter
- {
- /**
- * Construct a struct.
- *
- * @param array $parameters Optional associated array of parameters, if not provided then addField must be used.
- */
- function __construct($parameters = NULL)
- {
- parent::__construct();
-
- if (is_array($parameters))
- {
- foreach ($parameters as $k => $v)
- $this->addField($k, $v);
- }
- }
-
- /**
- * Add a field to the container.
- *
- * @param string $name The name of the field.
- * @param XMLRPCParameter $value The value.
- */
- public function addField($name, XMLRPCParameter $value)
- {
- if (!is_array($this->value))
- $this->value = array();
-
- $this->value[$name] = $value;
- }
-
- function __toString()
- {
- $params = "";
- foreach ($this->value as $k => $v)
- {
- $params .= "<member><name>$k</name>$v</member>";
- }
-
- return "<value><struct>$params</struct></value>";
- }
+
+ 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];
}
-
- /**
- * @class XMLRPCArrayParameter An array containing other XMLRPCParameter objects.
- * @author Curverider Ltd
- */
- class XMLRPCArrayParameter extends XMLRPCParameter
- {
- /**
- * Construct an array.
- *
- * @param array $parameters Optional array of parameters, if not provided then addField must be used.
- */
- function __construct($parameters = NULL)
- {
- parent::__construct();
-
- if (is_array($parameters))
- {
- foreach ($parameters as $v)
- $this->addField($v);
+
+ switch ($object->name) {
+ case 'string':
+ return $object->content;
+
+ case 'array':
+ foreach ($object->children[0]->children as $child) {
+ $value[] = xmlrpc_scalar_value($child);
}
- }
-
- /**
- * Add a field to the container.
- *
- * @param XMLRPCParameter $value The value.
- */
- public function addField(XMLRPCParameter $value)
- {
- if (!is_array($this->value))
- $this->value = array();
-
- $this->value[] = $value;
- }
-
- function __toString()
- {
- $params = "";
- foreach ($this->value as $value)
- {
- $params .= "$value";
+ return $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 "<array><data>$params</data></array>";
- }
- }
-
- /**
- * @class XMLRPCResponse XML-RPC Response.
- * @author Curverider Ltd
- */
- abstract class XMLRPCResponse
- {
- /** An array of parameters */
- protected $parameters = array();
-
- /**
- * Add a parameter here.
- *
- * @param XMLRPCParameter $param The parameter.
- */
- public function addParameter(XMLRPCParameter $param)
- {
- if (!is_array($this->parameters))
- $this->parameters = array();
-
- $this->parameters[] = $param;
- }
+ return $value;
- public function addInt($value) { $this->addParameter(new XMLRPCIntParameter($value)); }
- public function addString($value) { $this->addParameter(new XMLRPCStringParameter($value)); }
- public function addDouble($value) { $this->addParameter(new XMLRPCDoubleParameter($value)); }
- public function addBoolean($value) { $this->addParameter(new XMLRPCBoolParameter($value)); }
- }
+ case 'boolean':
+ return (boolean) $object->content;
- /**
- * @class XMLRPCSuccessResponse
- * @author Curverider Ltd
- */
- class XMLRPCSuccessResponse extends XMLRPCResponse
- {
- /**
- * Output to XML.
- */
- public function __toString()
- {
- $params = "";
- foreach ($this->parameters as $param)
- $params .= "<param>$param</param>\n";
-
- return "<methodResponse><params>$params</params></methodResponse>";
- }
- }
+ case 'i4':
+ case 'int':
+ return (int) $object->content;
- /**
- * @class XMLRPCErrorResponse
- * @author Curverider Ltd
- */
- class XMLRPCErrorResponse extends XMLRPCResponse
- {
- /**
- * Set the error response and error code.
- *
- * @param string $message The message
- * @param int $code Error code (default = system error as defined by http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
- */
- function __construct($message, $code = -32400)
- {
- $this->addParameter(
- new XMLRPCStructParameter(
- array (
- 'faultCode' => new XMLRPCIntParameter($code),
- 'faultString' => new XMLRPCStringParameter($message)
- )
- )
- );
- }
-
- /**
- * Output to XML.
- */
- public function __toString()
- {
- return "<methodResponse><fault><value>{$this->parameters[0]}</value></fault></methodResponse>";
- }
- }
-
-
- // Helper functions ///////////////////////////////////////////////////////////////////////
-
- /**
- * parse XMLRPCCall parameters
- *
- * Convert an XMLRPCCall result array into native data types
- *
- * @param array $parameters
- * @return array
- */
- function xmlrpc_parse_params($parameters)
- {
- $result = array();
-
- foreach ($parameters as $parameter)
- {
- $result[] = xmlrpc_scalar_value($parameter);
- }
-
- return $result;
- }
+ case 'double':
+ return (double) $object->content;
- /**
- * Extract the scalar value of an XMLObject type result array
- *
- * @param XMLObject $object
- * @return mixed
- */
- 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 */
- $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 once 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;
+ 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;
}
-
- /**
- * Trigger a method call and pass the relevant parameters to the funciton.
- *
- * @param XMLRPCCall $parameters The call and parameters.
- * @return XMLRPCCall
- */
- 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))
- throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:UnexpectedReturnFormat'), $parameters->getMethodName()));
-
- // Result in right format, return it.
- return $result;
+}
+
+// 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);
}
-
- // if no handler then throw exception
- throw new NotImplementedException(sprintf(elgg_echo('NotImplementedException:XMLRPCMethodNotImplemented'), $parameters->getMethodName()));
+
+ // Result in right format, return it.
+ return $result;
}
-
- // Error handler functions ////////////////////////////////////////////////////////////////
-
- /**
- * 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 unknown_type $errno
- * @param unknown_type $errmsg
- * @param unknown_type $filename
- * @param unknown_type $linenum
- * @param unknown_type $vars
- */
- 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);
- }
+
+ // 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
- */
- function __php_xmlrpc_exception_handler($exception) {
-
- error_log("*** FATAL EXCEPTION (XML-RPC) *** : " . $exception);
-
- page_draw($exception->getMessage(), elgg_view("xml-rpc/output", array('result' => new XMLRPCErrorResponse($exception->getMessage(), $exception->getCode()==0 ? -32400 : $exception->getCode()))));
+}
+
+/**
+ * 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/lib/xml.php b/engine/lib/xml.php
index b21fdd92b..497459d83 100644
--- a/engine/lib/xml.php
+++ b/engine/lib/xml.php
@@ -1,163 +1,111 @@
<?php
- /**
- * Elgg XML library.
- * Contains functions for generating and parsing XML.
- *
- * @package Elgg
- * @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-
- /**
- * @class XmlElement
- * A class representing an XML element for import.
- */
- class XmlElement
- {
- /** The name of the element */
- public $name;
-
- /** The attributes */
- public $attributes;
-
- /** CData */
- public $content;
-
- /** Child elements */
- public $children;
- };
-
- /**
- * This function serialises an object recursively into an XML representation.
- * The function attempts to call $data->export() which expects a stdClass in return, otherwise it will attempt to
- * get the object variables using get_object_vars (which will only return public variables!)
- * @param $data object The object to serialise.
- * @param $n int Level, only used for recursion.
- * @return string The serialised XML output.
- */
- function serialise_object_to_xml($data, $name = "", $n = 0)
- {
- $classname = ($name=="" ? get_class($data) : $name);
-
- $vars = method_exists($data, "export") ? get_object_vars($data->export()) : get_object_vars($data);
-
- $output = "";
-
- if (($n==0) || ( is_object($data) && !($data instanceof stdClass))) {
- $output = "<$classname>";
- }
-
- foreach ($vars as $key => $value) {
- $output .= "<$key type=\"".gettype($value)."\">";
-
- if (is_object($value)) {
- $output .= serialise_object_to_xml($value, $key, $n+1);
- } else if (is_array($value)) {
- $output .= serialise_array_to_xml($value, $n+1);
- } else if (gettype($value) == "boolean") {
- $output .= $value ? "true" : "false";
- } else {
- $output .= htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
- }
-
- $output .= "</$key>\n";
- }
-
- if (($n==0) || ( is_object($data) && !($data instanceof stdClass))) {
- $output .= "</$classname>\n";
- }
-
- return $output;
+/**
+ * Elgg XML library.
+ * Contains functions for generating and parsing XML.
+ *
+ * @package Elgg.Core
+ * @subpackage XML
+ */
+
+/**
+ * This function serialises an object recursively into an XML representation.
+ *
+ * The function attempts to call $data->export() which expects a stdClass in return,
+ * otherwise it will attempt to get the object variables using get_object_vars (which
+ * will only return public variables!)
+ *
+ * @param mixed $data The object to serialise.
+ * @param string $name The name?
+ * @param int $n Level, only used for recursion.
+ *
+ * @return string The serialised XML output.
+ */
+function serialise_object_to_xml($data, $name = "", $n = 0) {
+ $classname = ($name == "" ? get_class($data) : $name);
+
+ $vars = method_exists($data, "export") ? get_object_vars($data->export()) : get_object_vars($data);
+
+ $output = "";
+
+ if (($n == 0) || ( is_object($data) && !($data instanceof stdClass))) {
+ $output = "<$classname>";
}
- /**
- * Serialise an array.
- *
- * @param array $data
- * @param int $n Used for recursion
- * @return string
- */
- function serialise_array_to_xml(array $data, $n = 0)
- {
- $output = "";
-
- if ($n==0) {
- $output = "<array>\n";
+ foreach ($vars as $key => $value) {
+ $output .= "<$key type=\"" . gettype($value) . "\">";
+
+ if (is_object($value)) {
+ $output .= serialise_object_to_xml($value, $key, $n + 1);
+ } else if (is_array($value)) {
+ $output .= serialise_array_to_xml($value, $n + 1);
+ } else if (gettype($value) == "boolean") {
+ $output .= $value ? "true" : "false";
+ } else {
+ $output .= htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
}
-
- foreach ($data as $key => $value) {
- $item = "array_item";
-
- if (is_numeric($key)) {
- $output .= "<$item name=\"$key\" type=\"".gettype($value)."\">";
- } else {
- $item = $key;
- $output .= "<$item type=\"".gettype($value)."\">";
- }
-
- if (is_object($value)) {
- $output .= serialise_object_to_xml($value, "", $n+1);
- } else if (is_array($value)) {
- $output .= serialise_array_to_xml($value, $n+1);
- } else if (gettype($value) == "boolean") {
- $output .= $value ? "true" : "false";
- } else {
- $output .= htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
- }
-
- $output .= "</$item>\n";
+
+ $output .= "</$key>\n";
+ }
+
+ if (($n == 0) || (is_object($data) && !($data instanceof stdClass))) {
+ $output .= "</$classname>\n";
+ }
+
+ return $output;
+}
+
+/**
+ * Serialise an array.
+ *
+ * @param array $data The data to serialize
+ * @param int $n Used for recursion
+ *
+ * @return string
+ */
+function serialise_array_to_xml(array $data, $n = 0) {
+ $output = "";
+
+ if ($n == 0) {
+ $output = "<array>\n";
+ }
+
+ foreach ($data as $key => $value) {
+ $item = "array_item";
+
+ if (is_numeric($key)) {
+ $output .= "<$item name=\"$key\" type=\"" . gettype($value) . "\">";
+ } else {
+ $item = $key;
+ $output .= "<$item type=\"" . gettype($value) . "\">";
}
-
- if ($n==0) {
- $output .= "</array>\n";
+
+ if (is_object($value)) {
+ $output .= serialise_object_to_xml($value, "", $n + 1);
+ } else if (is_array($value)) {
+ $output .= serialise_array_to_xml($value, $n + 1);
+ } else if (gettype($value) == "boolean") {
+ $output .= $value ? "true" : "false";
+ } else {
+ $output .= htmlspecialchars($value, ENT_NOQUOTES, 'UTF-8');
}
-
- return $output;
+
+ $output .= "</$item>\n";
}
-
- /**
- * Parse an XML file into an object.
- * Based on code from http://de.php.net/manual/en/function.xml-parse-into-struct.php by
- * efredricksen at gmail dot com
- *
- * @param string $xml The XML.
- */
- function xml_to_object($xml)
- {
- $parser = xml_parser_create();
-
- // Parse $xml into a structure
- xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
- xml_parse_into_struct($parser, $xml, $tags);
-
- xml_parser_free($parser);
-
- $elements = array();
- $stack = array();
-
- foreach ($tags as $tag) {
- $index = count($elements);
-
- if ($tag['type'] == "complete" || $tag['type'] == "open") {
- $elements[$index] = new XmlElement;
- $elements[$index]->name = $tag['tag'];
- $elements[$index]->attributes = $tag['attributes'];
- $elements[$index]->content = $tag['value'];
-
- if ($tag['type'] == "open") {
- $elements[$index]->children = array();
- $stack[count($stack)] = &$elements;
- $elements = &$elements[$index]->children;
- }
- }
-
- if ($tag['type'] == "close") {
- $elements = &$stack[count($stack) - 1];
- unset($stack[count($stack) - 1]);
- }
- }
-
- return $elements[0];
+
+ if ($n == 0) {
+ $output .= "</array>\n";
}
+
+ return $output;
+}
+
+/**
+ * Parse an XML file into an object.
+ *
+ * @param string $xml The XML
+ *
+ * @return ElggXMLElement
+ */
+function xml_to_object($xml) {
+ return new ElggXMLElement($xml);
+}
diff --git a/engine/schema/mysql.sql b/engine/schema/mysql.sql
index 23e81f39d..4714b71bb 100644
--- a/engine/schema/mysql.sql
+++ b/engine/schema/mysql.sql
@@ -94,7 +94,7 @@ CREATE TABLE `prefix_api_users` (
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `prefix_config` (
- `name` varchar(32) NOT NULL,
+ `name` varchar(255) NOT NULL,
`value` text NOT NULL,
`site_guid` int(11) NOT NULL,
PRIMARY KEY (`name`,`site_guid`)
@@ -108,7 +108,7 @@ CREATE TABLE `prefix_config` (
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `prefix_datalists` (
- `name` varchar(32) NOT NULL,
+ `name` varchar(255) NOT NULL,
`value` text NOT NULL,
PRIMARY KEY (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
@@ -155,6 +155,7 @@ CREATE TABLE `prefix_entity_relationships` (
`guid_one` bigint(20) unsigned NOT NULL,
`relationship` varchar(50) NOT NULL,
`guid_two` bigint(20) unsigned NOT NULL,
+ `time_created` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `guid_one` (`guid_one`,`relationship`,`guid_two`),
KEY `relationship` (`relationship`),
@@ -360,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(46) NOT NULL,
PRIMARY KEY (`id`),
KEY `object_id` (`object_id`),
KEY `object_class` (`object_class`),
diff --git a/engine/schema/upgrades/2009100701.sql b/engine/schema/upgrades/2009100701.sql
index dbf52b4da..74249e901 100644
--- a/engine/schema/upgrades/2009100701.sql
+++ b/engine/schema/upgrades/2009100701.sql
@@ -1,2 +1,2 @@
--- Previously was the UTF8 migration that is now in code at 2010033101.
+-- Previously was the UTF8 migration that is now in code at engine/lib/upgrades/2010033101.php
-- Keeping this file to force an overwrite and to avoid confusion with missing migrations.
diff --git a/engine/schema/upgrades/2010062401.sql b/engine/schema/upgrades/2010062401.sql
new file mode 100644
index 000000000..fbb49bfcf
--- /dev/null
+++ b/engine/schema/upgrades/2010062401.sql
@@ -0,0 +1 @@
+ALTER TABLE `prefix_entity_relationships` ADD COLUMN `time_created` int(11) NOT NULL AFTER `guid_two`; \ No newline at end of file
diff --git a/engine/settings.example.php b/engine/settings.example.php
index 30d832456..3b139d710 100644
--- a/engine/settings.example.php
+++ b/engine/settings.example.php
@@ -1,11 +1,17 @@
<?php
/**
- * Elgg settings
+ * Defines database credentials.
*
- * Elgg manages most of its configuration from the admin panel. However, we need you to
- * include your database settings below.
+ * Most of Elgg's configuration is stored in the database. This file contains the
+ * credentials to connect to the database, as well as a few optional configuration
+ * values.
+ *
+ * The Elgg installation attempts to populate this file with the correct settings
+ * and then rename it to settings.php.
*
* @todo Turn this into something we handle more automatically.
+ * @package Elgg.Core
+ * @subpackage Configuration
*/
global $CONFIG;
@@ -23,75 +29,47 @@ if (!isset($CONFIG)) {
* to explain, but if you know you need it, skip past this section.
*/
-// Database username
-$CONFIG->dbuser = '{{CONFIG_DBUSER}}';
-
-// Database password
-$CONFIG->dbpass = '{{CONFIG_DBPASS}}';
-
-// Database name
-$CONFIG->dbname = '{{CONFIG_DBNAME}}';
-
-// Database server
-// (For most configurations, you can leave this as 'localhost')
-$CONFIG->dbhost = '{{CONFIG_DBHOST}}';
-
-// Database table prefix
-// If you're sharing a database with other applications, you will want to use this
-// to differentiate Elgg's tables.
-$CONFIG->dbprefix = '{{CONFIG_DBPREFIX}}';
-
-/*
- * Multiple database connections
+/**
+ * The database username
*
- * Here you can set up multiple connections for reads and writes. To do this, uncomment out
- * the lines below.
+ * @global string $CONFIG->dbuser
+ * @name $CONFIG->dbuser
*/
+$CONFIG->dbuser = '{{dbuser}}';
-/*
-
-// Yes! We want to split reads and writes
-$CONFIG->db->split = true;
-
-// READS
-// Database username
-$CONFIG->db['read']->dbuser = "";
-
-// Database password
-$CONFIG->db['read']->dbpass = "";
-
-// Database name
-$CONFIG->db['read']->dbname = "";
-
-// Database server
-// (For most configurations, you can leave this as 'localhost')
-$CONFIG->db['read']->dbhost = "localhost";
-
-// WRITES
-// Database username
-$CONFIG->db['write']->dbuser = "";
-
-// Database password
-$CONFIG->db['write']->dbpass = "";
-
-// Database name
-$CONFIG->db['write']->dbname = "";
+/**
+ * The database password
+ *
+ * @global string $CONFIG->dbpass
+ */
+$CONFIG->dbpass = '{{dbpassword}}';
-// Database server
-// (For most configurations, you can leave this as 'localhost')
-$CONFIG->db['write']->dbhost = "localhost";
+/**
+ * The database name
+ *
+ * @global string $CONFIG->dbname
+ */
+$CONFIG->dbname = '{{dbname}}';
+/**
+ * The database host.
+ *
+ * For most installations, this is 'localhost'
+ *
+ * @global string $CONFIG->dbhost
*/
+$CONFIG->dbhost = '{{dbhost}}';
-/*
- * For extra connections for both reads and writes, you can turn both
- * $CONFIG->db['read'] and $CONFIG->db['write'] into an array, eg:
+/**
+ * The database prefix
*
- * $CONFIG->db['read'][0]->dbhost = "localhost";
+ * This prefix will be appended to all Elgg tables. If you're sharing
+ * a database with other applications, use a database prefix to namespace tables
+ * in order to avoid table name collisions.
*
- * Note that the array keys must be numeric and consecutive, i.e., they start
- * at 0, the next one must be at 1, etc.
+ * @global string $CONFIG->dbprefix
*/
+$CONFIG->dbprefix = '{{dbprefix}}';
/**
@@ -102,7 +80,8 @@ $CONFIG->db['write']->dbhost = "localhost";
* 1) One or more memcache servers (http://www.danga.com/memcached/)
* 2) PHP memcache wrapper (http://uk.php.net/manual/en/memcache.setup.php)
*
- * Note: Multiple server support is only available on server 1.2.1 or higher with PECL library > 2.0.0
+ * Note: Multiple server support is only available on server 1.2.1
+ * or higher with PECL library > 2.0.0
*/
//$CONFIG->memcache = true;
//
@@ -111,16 +90,34 @@ $CONFIG->db['write']->dbhost = "localhost";
// array('server2', 11211)
//);
+
/**
- * Some work-around flags.
+ * Use non-standard headers for broken MTAs.
+ *
+ * The default header EOL for headers is \r\n. This causes problems
+ * on some broken MTAs. Setting this to TRUE will cause Elgg to use
+ * \n, which will fix some problems sending email on broken MTAs.
+ *
+ * @global bool $CONFIG->broken_mta
*/
-
-// Try uncommenting the below if your notification emails are not being sent
-// $CONFIG->broken_mta = true;
+$CONFIG->broken_mta = FALSE;
/**
- * Url - I am not sure if this will be here ?
- **/
+ * Disable the database query cache
+ *
+ * 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 TRUE.
+ *
+ * @global bool $CONFIG->db_disable_query_cache
+ */
+$CONFIG->db_disable_query_cache = FALSE;
-// URL
-$CONFIG->url = ""; \ No newline at end of file
+/**
+ * Minimum password length
+ *
+ * This value is used when validating a user's password during registration.
+ *
+ * @global int $CONFIG->min_password_length
+ */
+$CONFIG->min_password_length = 6;
diff --git a/engine/start.php b/engine/start.php
index 442222dc8..55b8ffa5b 100644
--- a/engine/start.php
+++ b/engine/start.php
@@ -1,146 +1,121 @@
<?php
/**
- * Elgg engine bootstrapper
- * Loads the various elements of the Elgg engine
+ * Bootstraps the Elgg engine.
*
- * @package Elgg
+ * This file loads the full Elgg engine, checks the installation
+ * state, and triggers a series of events to finish booting Elgg:
+ * - {@elgg_event boot system}
+ * - {@elgg_event init system}
+ * - {@elgg_event ready system}
+ *
+ * If Elgg is fully uninstalled, the browser will be redirected to an
+ * installation page.
+ *
+ * @see install.php
+ * @package Elgg.Core
* @subpackage Core
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
/*
- * Basic profiling
+ * No settings means a fresh install
+ */
+if (!file_exists(dirname(__FILE__) . '/settings.php')) {
+ header("Location: install.php");
+ exit;
+}
+
+/**
+ * The time with microseconds when the Elgg engine was started.
+ *
+ * @global float
*/
global $START_MICROTIME;
$START_MICROTIME = microtime(true);
-/*
- * Create global CONFIG object
+/**
+ * Configuration values.
+ *
+ * The $CONFIG global contains configuration values required
+ * for running Elgg as defined in the settings.php file.
+ *
+ * Plugin authors are encouraged to use elgg_get_config() instead of accessing
+ * the global directly.
+ *
+ * @see elgg_get_config()
+ * @see engine/settings.php
+ * @global stdClass $CONFIG
*/
global $CONFIG;
if (!isset($CONFIG)) {
$CONFIG = new stdClass;
}
+$CONFIG->boot_complete = false;
$lib_dir = dirname(__FILE__) . '/lib/';
-// bootstrapping with required files in a required order
-$required_files = array(
- 'exceptions.php', 'elgglib.php', 'views.php', 'access.php', 'system_log.php', 'export.php',
- 'sessions.php', 'languages.php', 'input.php', 'install.php', 'cache.php', 'output.php'
-);
-
-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;
}
-// Use fallback view until sanitised
-$oldview = get_input('view', 'default');
-set_input('view', 'failsafe');
-
-// Register the error handler
-set_error_handler('__elgg_php_error_handler');
-set_exception_handler('__elgg_php_exception_handler');
-
-// attempt to save settings.php and .htaccess if in installation.
-if ($sanitised = sanitised()) {
- /**
- * Load the system settings
- */
- if (!include_once(dirname(__FILE__) . "/settings.php")) {
- throw new InstallationException("Elgg could not load the settings file.");
- }
-
- // Get config
- global $CONFIG;
-
- // load the rest of the library files from engine/lib/
- $lib_files = array(
- // these want to be loaded first apparently?
- 'database.php', 'actions.php',
-
- 'admin.php', 'annotations.php', 'api.php', 'cache.php',
- 'calendar.php', 'configuration.php', 'cron.php', 'entities.php',
- 'export.php', 'extender.php', 'filestore.php', 'group.php',
- 'input.php', 'install.php', 'location.php', 'mb_wrapper.php',
- 'memcache.php', 'metadata.php', 'metastrings.php', 'notification.php',
- 'objects.php', 'opendd.php', 'pagehandler.php',
- 'pageowner.php', 'pam.php', 'plugins.php', 'query.php',
- 'relationships.php', 'river.php', 'sites.php', 'social.php',
- 'statistics.php', 'system_log.php', 'tags.php', 'usersettings.php',
- 'users.php', 'version.php', 'widgets.php', 'xml.php', 'xml-rpc.php'
- );
-
- foreach($lib_files as $file) {
- $file = $lib_dir . $file;
- elgg_log("Loading $file...");
- if (!include_once($file)) {
- throw new InstallationException("Could not load {$file}");
- }
- }
-} else {
- throw new InstallationException(elgg_echo('installation:error:configuration'));
+// Load the system settings
+if (!include_once(dirname(__FILE__) . "/settings.php")) {
+ $msg = 'Elgg could not load the settings file. It does not exist or there is a file permissions issue.';
+ throw new InstallationException($msg);
}
-// Autodetect some default configuration settings
-set_default_config();
-
-// Trigger events
-trigger_elgg_event('boot', 'system');
-// Check if installed
-$installed = is_installed();
-$db_installed = is_db_installed();
-
-// Forward if we haven't been installed
-if ((!$installed || !$db_installed)
- && !substr_count($_SERVER["PHP_SELF"], "install.php")
- && !substr_count($_SERVER["PHP_SELF"], "css.php")
- && !substr_count($_SERVER["PHP_SELF"], "action_handler.php")) {
+// load the rest of the library files from engine/lib/
+$lib_files = array(
+ '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',
+);
- header("Location: install.php");
- exit;
+foreach ($lib_files as $file) {
+ $file = $lib_dir . $file;
+ elgg_log("Loading $file...");
+ if (!include_once($file)) {
+ $msg = "Could not load $file";
+ throw new InstallationException($msg);
+ }
}
-// Load plugins
-if (($installed) && ($db_installed) && ($sanitised)) {
- load_plugins();
+// 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');
- trigger_elgg_event('plugins_boot', 'system');
-}
-
-// Trigger system init event for plugins
-if (!substr_count($_SERVER["PHP_SELF"], "install.php")
- && !substr_count($_SERVER["PHP_SELF"], "setup.php")) {
+// Load the plugins that are active
+elgg_load_plugins();
- trigger_elgg_event('init', 'system');
+// @todo move loading plugins into a single boot function that replaces 'boot', 'system' event
+// and then move this code in there.
+// This validates the view type - first opportunity to do it is after plugins load.
+$view_type = elgg_get_viewtype();
+if (!elgg_is_valid_view_type($view_type)) {
+ elgg_set_viewtype('default');
}
-// System booted, return to normal view
-set_input('view', $oldview);
+// @todo deprecate as plugins can use 'init', 'system' event
+elgg_trigger_event('plugins_boot', 'system');
-if (empty($oldview)) {
- if (empty($CONFIG->view)) {
- $oldview = 'default';
- } else {
- $oldview = $CONFIG->view;
- }
-}
+// 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.
-if (($installed) && ($db_installed) && !(defined('upgrading') && upgrading == 'upgrading')) {
- $lastupdate = datalist_get("simplecache_lastupdate_$oldview");
- $lastcached = datalist_get("simplecache_lastcached_$oldview");
- if ($lastupdate == 0 || $lastcached < $lastupdate) {
- elgg_view_regenerate_simplecache($oldview);
- }
- // needs to be set for links in html head
- $CONFIG->lastcache = $lastcached;
-}
+$CONFIG->boot_complete = true;
+
+// System loaded and ready
+elgg_trigger_event('ready', 'system');
diff --git a/engine/tests/api/access_collections.php b/engine/tests/api/access_collections.php
new file mode 100644
index 000000000..4acfae596
--- /dev/null
+++ b/engine/tests/api/access_collections.php
@@ -0,0 +1,290 @@
+<?php
+/**
+ * Access Collections tests
+ *
+ * @package Elgg
+ * @subpackage Test
+ */
+class ElggCoreAccessCollectionsTest extends ElggCoreUnitTest {
+
+ /**
+ * Called before each test object.
+ */
+ public function __construct() {
+ parent::__construct();
+
+ $this->dbPrefix = get_config("dbprefix");
+
+ $user = new ElggUser();
+ $user->username = 'test_user_' . rand();
+ $user->email = 'fake_email@fake.com' . rand();
+ $user->name = 'fake user';
+ $user->access_id = ACCESS_PUBLIC;
+ $user->salt = generate_random_cleartext_password();
+ $user->password = generate_user_password($user, rand());
+ $user->owner_guid = 0;
+ $user->container_guid = 0;
+ $user->save();
+
+ $this->user = $user;
+ }
+
+ /**
+ * Called before each test method.
+ */
+ public function setUp() {
+
+ }
+
+ /**
+ * Called after each test method.
+ */
+ public function tearDown() {
+ // do not allow SimpleTest to interpret Elgg notices as exceptions
+ $this->swallowErrors();
+ }
+
+ /**
+ * Called after each test object.
+ */
+ public function __destruct() {
+ // all __destruct() code should go above here
+ $this->user->delete();
+ parent::__destruct();
+ }
+
+ public function testCreateGetDeleteACL() {
+
+ $acl_name = 'test access collection';
+ $acl_id = create_access_collection($acl_name);
+
+ $this->assertTrue(is_int($acl_id));
+
+ $q = "SELECT * FROM {$this->dbPrefix}access_collections WHERE id = $acl_id";
+ $acl = get_data_row($q);
+
+ $this->assertEqual($acl->id, $acl_id);
+
+ if ($acl) {
+ $this->assertEqual($acl->name, $acl_name);
+
+ $result = delete_access_collection($acl_id);
+ $this->assertTrue($result);
+
+ $q = "SELECT * FROM {$this->dbPrefix}access_collections WHERE id = $acl_id";
+ $data = get_data($q);
+ $this->assertIdentical(array(), $data);
+ }
+ }
+
+ public function testAddRemoveUserToACL() {
+ $acl_id = create_access_collection('test acl');
+
+ $result = add_user_to_access_collection($this->user->guid, $acl_id);
+ $this->assertTrue($result);
+
+ if ($result) {
+ $result = remove_user_from_access_collection($this->user->guid, $acl_id);
+ $this->assertIdentical(true, $result);
+ }
+
+ delete_access_collection($acl_id);
+ }
+
+ public function testUpdateACL() {
+ // another fake user to test with
+ $user = new ElggUser();
+ $user->username = 'test_user_' . rand();
+ $user->email = 'fake_email@fake.com' . rand();
+ $user->name = 'fake user';
+ $user->access_id = ACCESS_PUBLIC;
+ $user->salt = generate_random_cleartext_password();
+ $user->password = generate_user_password($user, rand());
+ $user->owner_guid = 0;
+ $user->container_guid = 0;
+ $user->save();
+
+ $acl_id = create_access_collection('test acl');
+
+ $member_lists = array(
+ // adding
+ array(
+ $this->user->guid,
+ $user->guid
+ ),
+ // removing one, keeping one.
+ array(
+ $user->guid
+ ),
+ // removing one, adding one
+ array(
+ $this->user->guid,
+ ),
+ // removing all.
+ array()
+ );
+
+ foreach ($member_lists as $members) {
+ $result = update_access_collection($acl_id, $members);
+ $this->assertTrue($result);
+
+ if ($result) {
+ $q = "SELECT * FROM {$this->dbPrefix}access_collection_membership
+ WHERE access_collection_id = $acl_id";
+ $data = get_data($q);
+
+ if (count($members) == 0) {
+ $this->assertFalse($data);
+ } else {
+ $this->assertEqual(count($members), count($data));
+ }
+ foreach ($data as $row) {
+ $this->assertTrue(in_array($row->user_guid, $members));
+ }
+ }
+ }
+
+ delete_access_collection($acl_id);
+ $user->delete();
+ }
+
+ public function testCanEditACL() {
+ $acl_id = create_access_collection('test acl', $this->user->guid);
+
+ // should be true since it's the owner
+ $result = can_edit_access_collection($acl_id, $this->user->guid);
+ $this->assertTrue($result);
+
+ // should be true since IA is on.
+ $ia = elgg_set_ignore_access(true);
+ $result = can_edit_access_collection($acl_id);
+ $this->assertTrue($result);
+ elgg_set_ignore_access($ia);
+
+ // should be false since IA is off
+ $ia = elgg_set_ignore_access(false);
+ $result = can_edit_access_collection($acl_id);
+ $this->assertFalse($result);
+ elgg_set_ignore_access($ia);
+
+ delete_access_collection($acl_id);
+ }
+
+ public function testCanEditACLHook() {
+ // if only we supported closures!
+ global $acl_test_info;
+
+ $acl_id = create_access_collection('test acl');
+
+ $acl_test_info = array(
+ 'acl_id' => $acl_id,
+ 'user' => $this->user
+ );
+
+ function test_acl_access_hook($hook, $type, $value, $params) {
+ global $acl_test_info;
+ if ($params['user_id'] == $acl_test_info['user']->guid) {
+ $acl = get_access_collection($acl_test_info['acl_id']);
+ $value[$acl->id] = $acl->name;
+ }
+
+ return $value;
+ }
+
+ elgg_register_plugin_hook_handler('access:collections:write', 'all', 'test_acl_access_hook');
+
+ // enable security since we usually run as admin
+ $ia = elgg_set_ignore_access(false);
+ $result = can_edit_access_collection($acl_id, $this->user->guid);
+ $this->assertTrue($result);
+ $ia = elgg_set_ignore_access($ia);
+
+ elgg_unregister_plugin_hook_handler('access:collections:write', 'all', 'test_acl_access_hook');
+
+ delete_access_collection($acl_id);
+ }
+
+ // groups interface
+ // only runs if the groups plugin is enabled because implementation is split between
+ // core and the plugin.
+ public function testCreateDeleteGroupACL() {
+ if (!elgg_is_active_plugin('groups')) {
+ return;
+ }
+
+ $group = new ElggGroup();
+ $group->name = 'Test group';
+ $group->save();
+ $acl = get_access_collection($group->group_acl);
+
+ // ACLs are owned by groups
+ $this->assertEqual($acl->owner_guid, $group->guid);
+
+ // removing group and acl
+ $this->assertTrue($group->delete());
+
+ $acl = get_access_collection($group->group_acl);
+ $this->assertFalse($acl);
+
+ $group->delete();
+ }
+
+ public function testJoinLeaveGroupACL() {
+ if (!elgg_is_active_plugin('groups')) {
+ return;
+ }
+
+ $group = new ElggGroup();
+ $group->name = 'Test group';
+ $group->save();
+
+ $result = $group->join($this->user);
+ $this->assertTrue($result);
+
+ // disable security since we run as admin
+ $ia = elgg_set_ignore_access(false);
+
+ // need to set the page owner to emulate being in a group context.
+ // this is kinda hacky.
+ elgg_set_page_owner_guid($group->getGUID());
+
+ if ($result) {
+ $can_edit = can_edit_access_collection($group->group_acl, $this->user->guid);
+ $this->assertTrue($can_edit);
+ }
+
+ $result = $group->leave($this->user);
+ $this->assertTrue($result);
+
+ if ($result) {
+ $can_edit = can_edit_access_collection($group->group_acl, $this->user->guid);
+ $this->assertFalse($can_edit);
+ }
+
+ elgg_set_ignore_access($ia);
+
+ $group->delete();
+ }
+
+ public function testAccessCaching() {
+ // create a new user to check against
+ $user = new ElggUser();
+ $user->username = 'access_test_user';
+ $user->save();
+
+ foreach (array('get_access_list', 'get_access_array') as $func) {
+ $cache = _elgg_get_access_cache();
+ $cache->clear();
+
+ // admin users run tests, so disable access
+ elgg_set_ignore_access(true);
+ $access = $func($user->getGUID());
+
+ elgg_set_ignore_access(false);
+ $access2 = $func($user->getGUID());
+ $this->assertNotEqual($access, $access2, "Access test for $func");
+ }
+
+ $user->delete();
+ }
+}
diff --git a/engine/tests/api/annotations.php b/engine/tests/api/annotations.php
new file mode 100644
index 000000000..c0b0687cc
--- /dev/null
+++ b/engine/tests/api/annotations.php
@@ -0,0 +1,150 @@
+<?php
+/**
+ * Elgg Test annotation api
+ *
+ * @package Elgg
+ * @subpackage Test
+ */
+class ElggCoreAnnotationAPITest extends ElggCoreUnitTest {
+ protected $metastrings;
+
+ /**
+ * Called before each test method.
+ */
+ public function setUp() {
+ $this->object = new ElggObject();
+ }
+
+ /**
+ * Called after each test method.
+ */
+ public function tearDown() {
+ // do not allow SimpleTest to interpret Elgg notices as exceptions
+ $this->swallowErrors();
+
+ unset($this->object);
+ }
+
+ public function testElggGetAnnotationsCount() {
+ $this->object->title = 'Annotation Unit Test';
+ $this->object->save();
+
+ $guid = $this->object->getGUID();
+ create_annotation($guid, 'tested', 'tested1', 'text', 0, ACCESS_PUBLIC);
+ create_annotation($guid, 'tested', 'tested2', 'text', 0, ACCESS_PUBLIC);
+
+ $count = (int)elgg_get_annotations(array(
+ 'annotation_names' => array('tested'),
+ 'guid' => $guid,
+ 'count' => true,
+ ));
+
+ $this->assertIdentical($count, 2);
+
+ $this->object->delete();
+ }
+
+ public function testElggDeleteAnnotations() {
+ $e = new ElggObject();
+ $e->save();
+
+ for ($i=0; $i<30; $i++) {
+ $e->annotate('test_annotation', rand(0,10000));
+ }
+
+ $options = array(
+ 'guid' => $e->getGUID(),
+ 'limit' => 0
+ );
+
+ $annotations = elgg_get_annotations($options);
+ $this->assertIdentical(30, count($annotations));
+
+ $this->assertTrue(elgg_delete_annotations($options));
+
+ $annotations = elgg_get_annotations($options);
+ $this->assertTrue(empty($annotations));
+
+ // nothing to delete so null returned
+ $this->assertNull(elgg_delete_annotations($options));
+
+ $this->assertTrue($e->delete());
+ }
+
+ public function testElggDisableAnnotations() {
+ $e = new ElggObject();
+ $e->save();
+
+ for ($i=0; $i<30; $i++) {
+ $e->annotate('test_annotation', rand(0,10000));
+ }
+
+ $options = array(
+ 'guid' => $e->getGUID(),
+ 'limit' => 0
+ );
+
+ $this->assertTrue(elgg_disable_annotations($options));
+
+ $annotations = elgg_get_annotations($options);
+ $this->assertTrue(empty($annotations));
+
+ access_show_hidden_entities(true);
+ $annotations = elgg_get_annotations($options);
+ $this->assertIdentical(30, count($annotations));
+ access_show_hidden_entities(false);
+
+ $this->assertTrue($e->delete());
+ }
+
+ public function testElggEnableAnnotations() {
+ $e = new ElggObject();
+ $e->save();
+
+ for ($i=0; $i<30; $i++) {
+ $e->annotate('test_annotation', rand(0,10000));
+ }
+
+ $options = array(
+ 'guid' => $e->getGUID(),
+ 'limit' => 0
+ );
+
+ $this->assertTrue(elgg_disable_annotations($options));
+
+ // cannot see any annotations so returns null
+ $this->assertNull(elgg_enable_annotations($options));
+
+ access_show_hidden_entities(true);
+ $this->assertTrue(elgg_enable_annotations($options));
+ access_show_hidden_entities(false);
+
+ $annotations = elgg_get_annotations($options);
+ $this->assertIdentical(30, count($annotations));
+
+ $this->assertTrue($e->delete());
+ }
+
+ public function testElggAnnotationExists() {
+ $e = new ElggObject();
+ $e->save();
+ $guid = $e->getGUID();
+
+ $this->assertFalse(elgg_annotation_exists($guid, 'test_annotation'));
+
+ $e->annotate('test_annotation', rand(0, 10000));
+ $this->assertTrue(elgg_annotation_exists($guid, 'test_annotation'));
+ // this metastring should always exist but an annotation of this name should not
+ $this->assertFalse(elgg_annotation_exists($guid, 'email'));
+
+ $options = array(
+ 'guid' => $guid,
+ 'limit' => 0
+ );
+ $this->assertTrue(elgg_disable_annotations($options));
+ $this->assertTrue(elgg_annotation_exists($guid, 'test_annotation'));
+
+ $this->assertTrue($e->delete());
+ $this->assertFalse(elgg_annotation_exists($guid, 'test_annotation'));
+ }
+}
diff --git a/engine/tests/api/entity_getter_functions.php b/engine/tests/api/entity_getter_functions.php
index 9fcc6fe01..fef9dc0c5 100644
--- a/engine/tests/api/entity_getter_functions.php
+++ b/engine/tests/api/entity_getter_functions.php
@@ -4,15 +4,13 @@
* Elgg Test Entity Getter Functions
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
/**
* Called before each test object.
*/
public function __construct() {
- elgg_set_ignore_access(TRUE);
+ elgg_set_ignore_access(TRUE);
$this->entities = array();
$this->subtypes = array(
'object' => array(),
@@ -177,9 +175,10 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
}
/**
+ * Get a mix of valid and invalid types
*
- * @param unknown_type $num
- * @return unknown_type
+ * @param int $num
+ * @return array
*/
public function getRandomMixedTypes($num = 2) {
$have_valid = $have_invalid = false;
@@ -198,8 +197,8 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
* Get random mix of valid and invalid subtypes for types given.
*
* @param array $types
- * @param unknown_type $num
- * @return unknown_type
+ * @param int $num
+ * @return array
*/
public function getRandomMixedSubtypes(array $types, $num = 2) {
$types_c = count($types);
@@ -229,6 +228,24 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
return $r;
}
+ /**
+ * Creates random annotations on $entity
+ *
+ * @param ElggEntity $entity
+ * @param int $max
+ */
+ public function createRandomAnnotations($entity, $max = 1) {
+ $annotations = array();
+ for ($i=0; $i<$max; $i++) {
+ $name = 'test_annotation_name_' . rand();
+ $value = rand();
+ $id = create_annotation($entity->getGUID(), $name, $value, 'integer', $entity->getGUID());
+ $annotations[] = elgg_get_annotation_from_id($id);
+ }
+
+ return $annotations;
+ }
+
/***********************************
* TYPE TESTS
@@ -547,7 +564,9 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
* TYPE_SUBTYPE_PAIRS
***************************/
-
+ /**
+ * Valid type, valid subtype pairs
+ */
public function testElggAPIGettersTSPValidTypeValidSubtype() {
$type_num = 1;
$subtype_num = 1;
@@ -570,6 +589,9 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
}
}
+ /**
+ * Valid type, multiple valid subtypes
+ */
public function testElggAPIGettersTSPValidTypeValidPluralSubtype() {
$type_num = 1;
$subtype_num = 3;
@@ -592,6 +614,9 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
}
}
+ /**
+ * Valid type, both valid and invalid subtypes
+ */
public function testElggAPIGettersTSPValidTypeMixedPluralSubtype() {
$type_num = 1;
$valid_subtype_num = 2;
@@ -619,9 +644,6 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
}
-
-
-
/****************************
* FALSE-RETURNING TESTS
****************************
@@ -636,8 +658,8 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
*/
- /*
- * Test invalid types.
+ /**
+ * Test invalid types with singular 'type'.
*/
public function testElggApiGettersInvalidTypeUsingType() {
$type_arr = $this->getRandomInvalids();
@@ -651,7 +673,9 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$this->assertFalse($es);
}
-
+ /**
+ * Test invalid types with plural 'types'.
+ */
public function testElggApiGettersInvalidTypeUsingTypesAsString() {
$type_arr = $this->getRandomInvalids();
$type = $type_arr[0];
@@ -664,8 +688,11 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$this->assertFalse($es);
}
+ /**
+ * Test invalid types with plural 'types' and an array of a single type
+ */
public function testElggApiGettersInvalidTypeUsingTypesAsArray() {
- $type_arr = $this->getRandomInvalids();
+ $type_arr = $this->getRandomInvalids(1);
$options = array(
'types' => $type_arr
@@ -675,6 +702,9 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$this->assertFalse($es);
}
+ /**
+ * Test invalid types with plural 'types' and an array of a two types
+ */
public function testElggApiGettersInvalidTypes() {
$type_arr = $this->getRandomInvalids(2);
@@ -839,7 +869,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
public function testElggApiGettersEntityNoSubtype() {
// create an entity we can later delete.
- // order by time created and limit by 1 should == this entity.
+ // order by guid and limit by 1 should == this entity.
$e = new ElggObject();
$e->save();
@@ -1037,7 +1067,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$entities = elgg_get_entities_from_metadata($options);
- $this->assertFalse($entities);
+ $this->assertIdentical(array(), $entities);
$e->delete();
}
@@ -1065,7 +1095,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$entities = elgg_get_entities_from_metadata($options);
- $this->assertFalse($entities);
+ $this->assertIdentical(array(), $entities);
$e->delete();
}
@@ -1198,7 +1228,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
}
}
- function testElggApiGettersEntityMetadatavalueInvalidSingle() {
+ function testElggApiGettersEntityMetadataValueInvalidSingle() {
$subtypes = $this->getRandomValidSubtypes(array('object'), 1);
$subtype = $subtypes[0];
$md_name = 'test_metadata_name_' . rand();
@@ -1219,7 +1249,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$entities = elgg_get_entities_from_metadata($options);
- $this->assertFalse($entities);
+ $this->assertIdentical(array(), $entities);
$e->delete();
}
@@ -1247,7 +1277,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$entities = elgg_get_entities_from_metadata($options);
- $this->assertFalse($entities);
+ $this->assertIdentical(array(), $entities);
$e->delete();
}
@@ -1325,7 +1355,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$invalid_md_name = 'test_metadata_name_' . rand();
$e = new ElggObject();
$e->subtype = $subtype;
- $e->$md_name = $invalid_md_value;
+ $e->$invalid_md_name = $md_value;
$e->save();
$guids[] = $e->getGUID();
@@ -1390,11 +1420,13 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
// make some bad ones
$invalid_md_name = 'test_metadata_name_' . rand();
+ $invalid_md_name2 = 'test_metadata_name_' . rand();
+ $invalid_md_name3 = 'test_metadata_name_' . rand();
$e = new ElggObject();
$e->subtype = $subtype;
- $e->$md_name = $invalid_md_value;
- $e->$md_name2 = $invalid_md_value;
- $e->$md_name3 = $invalid_md_value;
+ $e->$invalid_md_name = $md_value;
+ $e->$invalid_md_name2 = $md_value2;
+ $e->$invalid_md_name3 = $md_value3;
$e->save();
$guids[] = $e->getGUID();
@@ -1467,10 +1499,11 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
// make some bad ones
$invalid_md_name = 'test_metadata_name_' . rand();
+ $invalid_md_name2 = 'test_metadata_name_' . rand();
$e = new ElggObject();
$e->subtype = $subtype;
- $e->$md_name = $invalid_md_value;
- $e->$md_name2 = $invalid_md_value;
+ $e->$invalid_md_name = $md_value;
+ $e->$invalid_md_name2 = $md_value2;
$e->save();
$guids[] = $e->getGUID();
@@ -1517,7 +1550,8 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
}
}
- function testElggApiGettersEntityMetadataNVPValidNValidVEqualsStupid() {
+ // this keeps locking up my database...
+ function xtestElggApiGettersEntityMetadataNVPValidNValidVEqualsStupid() {
$subtypes = $this->getRandomValidSubtypes(array('object'), 1);
$subtype = $subtypes[0];
$md_name = 'test_metadata_name_' . rand();
@@ -1555,11 +1589,11 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$invalid_md_name = 'test_metadata_name_' . rand();
$e = new ElggObject();
$e->subtype = $subtype;
- $e->$md_name = $invalid_md_value;
- $e->$md_name2 = $invalid_md_value;
- $e->$md_name3 = $invalid_md_value;
- $e->$md_name4 = $invalid_md_value;
- $e->$md_name5 = $invalid_md_value;
+ $e->$invalid_md_name = $md_value;
+ $e->$md_name2 = $md_value2;
+ $e->$md_name3 = $md_value3;
+ $e->$md_name4 = $md_value4;
+ $e->$md_name5 = $md_value5;
$e->save();
$guids[] = $e->getGUID();
@@ -1621,6 +1655,9 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
}
}
+ /**
+ * Name value pair with valid name and invalid value
+ */
function testElggApiGettersEntityMetadataNVPValidNInvalidV() {
$subtypes = $this->getRandomValidSubtypes(array('object'), 1);
$subtype = $subtypes[0];
@@ -1632,7 +1669,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$invalid_md_name = 'test_metadata_name_' . rand();
$e = new ElggObject();
$e->subtype = $subtype;
- $e->$md_name = $invalid_md_value;
+ $e->$invalid_md_name = $md_value;
$e->save();
$guids[] = $e->getGUID();
@@ -1656,7 +1693,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$entities = elgg_get_entities_from_metadata($options);
- $this->assertFalse($entities);
+ $this->assertIdentical(array(), $entities);
foreach ($guids as $guid) {
if ($e = get_entity($guid)) {
@@ -1665,7 +1702,9 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
}
}
-
+ /**
+ * Name value pair with invalid name and valid value
+ */
function testElggApiGettersEntityMetadataNVPInvalidNValidV() {
$subtypes = $this->getRandomValidSubtypes(array('object'), 1);
$subtype = $subtypes[0];
@@ -1677,7 +1716,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$invalid_md_name = 'test_metadata_name_' . rand();
$e = new ElggObject();
$e->subtype = $subtype;
- $e->$md_name = $invalid_md_value;
+ $e->$invalid_md_name = $md_value;
$e->save();
$guids[] = $e->getGUID();
@@ -1701,7 +1740,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$entities = elgg_get_entities_from_metadata($options);
- $this->assertFalse($entities);
+ $this->assertIdentical(array(), $entities);
foreach ($guids as $guid) {
if ($e = get_entity($guid)) {
@@ -1741,7 +1780,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$invalid_md_name = 'test_metadata_name_' . rand();
$e = new ElggObject();
$e->subtype = $subtype;
- $e->$md_name = $invalid_md_value;
+ $e->$invalid_md_name = $md_value;
$e->save();
$guids[] = $e->getGUID();
@@ -1819,7 +1858,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$invalid_md_name = 'test_metadata_name_' . rand();
$e = new ElggObject();
$e->subtype = $subtype;
- $e->$md_name = $invalid_md_value;
+ $e->$invalid_md_name = $md_value;
$e->save();
$guids[] = $e->getGUID();
@@ -1871,33 +1910,32 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$subtypes = $this->getRandomValidSubtypes(array('object'), 1);
$subtype = $subtypes[0];
$md_name = 'test_metadata_name_' . rand();
- $md_value = 2;
$guids = array();
$valid_guids = array();
// our targets
$valid = new ElggObject();
$valid->subtype = $subtype;
- $valid->$md_name = $md_value;
+ $valid->$md_name = 1;
$valid->save();
$guids[] = $valid->getGUID();
$valid_guids[] = $valid->getGUID();
$valid2 = new ElggObject();
$valid2->subtype = $subtype;
- $valid2->$md_name = 3;
+ $valid2->$md_name = 2;
$valid2->save();
$guids[] = $valid->getGUID();
$valid_guids[] = $valid2->getGUID();
$valid3 = new ElggObject();
$valid3->subtype = $subtype;
- $valid3->$md_name = 1;
+ $valid3->$md_name = 3;
$valid3->save();
$guids[] = $valid->getGUID();
$valid_guids[] = $valid3->getGUID();
- $md_valid_values = array($md_value, $md_value2);
+ $md_valid_values = array(1, 2, 3);
$options = array(
'type' => 'object',
@@ -1930,33 +1968,32 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$subtypes = $this->getRandomValidSubtypes(array('object'), 1);
$subtype = $subtypes[0];
$md_name = 'test_metadata_name_' . rand();
- $md_value = 'b';
$guids = array();
$valid_guids = array();
// our targets
$valid = new ElggObject();
$valid->subtype = $subtype;
- $valid->$md_name = $md_value;
+ $valid->$md_name = 'a';
$valid->save();
$guids[] = $valid->getGUID();
$valid_guids[] = $valid->getGUID();
$valid2 = new ElggObject();
$valid2->subtype = $subtype;
- $valid2->$md_name = 'c';
+ $valid2->$md_name = 'b';
$valid2->save();
$guids[] = $valid->getGUID();
$valid_guids[] = $valid2->getGUID();
$valid3 = new ElggObject();
$valid3->subtype = $subtype;
- $valid3->$md_name = 'a';
+ $valid3->$md_name = 'c';
$valid3->save();
$guids[] = $valid->getGUID();
$valid_guids[] = $valid3->getGUID();
- $md_valid_values = array($md_value, $md_value2);
+ $md_valid_values = array('a', 'b', 'c');
$options = array(
'type' => 'object',
@@ -2041,4 +2078,807 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
}
}
}
+
+ // Make sure metadata doesn't affect getting entities by relationship. See #2274
+ public function testElggApiGettersEntityRelationshipWithMetadata() {
+ $guids = array();
+
+ $obj1 = new ElggObject();
+ $obj1->test_md = 'test';
+ $obj1->save();
+ $guids[] = $obj1->guid;
+
+ $obj2 = new ElggObject();
+ $obj2->test_md = 'test';
+ $obj2->save();
+ $guids[] = $obj2->guid;
+
+ add_entity_relationship($guids[0], 'test', $guids[1]);
+
+ $options = array(
+ 'relationship' => 'test',
+ 'relationship_guid' => $guids[0]
+ );
+
+ $es = elgg_get_entities_from_relationship($options);
+ $this->assertTrue(is_array($es));
+ $this->assertIdentical(count($es), 1);
+
+ foreach ($es as $e) {
+ $this->assertEqual($guids[1], $e->guid);
+ }
+
+ foreach ($guids as $guid) {
+ $e = get_entity($guid);
+ $e->delete();
+ }
+ }
+
+ public function testElggApiGettersEntityRelationshipWithOutMetadata() {
+ $guids = array();
+
+ $obj1 = new ElggObject();
+ $obj1->save();
+ $guids[] = $obj1->guid;
+
+ $obj2 = new ElggObject();
+ $obj2->save();
+ $guids[] = $obj2->guid;
+
+ add_entity_relationship($guids[0], 'test', $guids[1]);
+
+ $options = array(
+ 'relationship' => 'test',
+ 'relationship_guid' => $guids[0]
+ );
+
+ $es = elgg_get_entities_from_relationship($options);
+ $this->assertTrue(is_array($es));
+ $this->assertIdentical(count($es), 1);
+
+ foreach ($es as $e) {
+ $this->assertEqual($guids[1], $e->guid);
+ }
+
+ foreach ($guids as $guid) {
+ $e = get_entity($guid);
+ $e->delete();
+ }
+ }
+
+ public function testElggApiGettersEntityRelationshipWithMetadataIncludingRealMetadata() {
+ $guids = array();
+
+ $obj1 = new ElggObject();
+ $obj1->test_md = 'test';
+ $obj1->save();
+ $guids[] = $obj1->guid;
+
+ $obj2 = new ElggObject();
+ $obj2->test_md = 'test';
+ $obj2->save();
+ $guids[] = $obj2->guid;
+
+ add_entity_relationship($guids[0], 'test', $guids[1]);
+
+ $options = array(
+ 'relationship' => 'test',
+ 'relationship_guid' => $guids[0],
+ 'metadata_name' => 'test_md',
+ 'metadata_value' => 'test',
+ );
+
+ $es = elgg_get_entities_from_relationship($options);
+ $this->assertTrue(is_array($es));
+ $this->assertIdentical(count($es), 1);
+
+ foreach ($es as $e) {
+ $this->assertEqual($guids[1], $e->guid);
+ }
+
+ foreach ($guids as $guid) {
+ $e = get_entity($guid);
+ $e->delete();
+ }
+ }
+
+ public function testElggApiGettersEntityRelationshipWithMetadataIncludingFakeMetadata() {
+ $guids = array();
+
+ $obj1 = new ElggObject();
+ $obj1->test_md = 'test';
+ $obj1->save();
+ $guids[] = $obj1->guid;
+
+ $obj2 = new ElggObject();
+ $obj2->test_md = 'test';
+ $obj2->save();
+ $guids[] = $obj2->guid;
+
+ add_entity_relationship($guids[0], 'test', $guids[1]);
+
+ $options = array(
+ 'relationship' => 'test',
+ 'relationship_guid' => $guids[0],
+ 'metadata_name' => 'test_md',
+ 'metadata_value' => 'invalid',
+ );
+
+ $es = elgg_get_entities_from_relationship($options);
+
+ $this->assertTrue(empty($es));
+
+ foreach ($guids as $guid) {
+ $e = get_entity($guid);
+ $e->delete();
+ }
+ }
+
+ public function testElggApiGettersEntitySiteSingular() {
+ global $CONFIG;
+
+ $guids = array();
+
+ $obj1 = new ElggObject();
+ $obj1->test_md = 'test';
+ // luckily this is never checked.
+ $obj1->site_guid = 2;
+ $obj1->save();
+ $guids[] = $obj1->guid;
+ $right_guid = $obj1->guid;
+
+ $obj2 = new ElggObject();
+ $obj2->test_md = 'test';
+ $obj2->site_guid = $CONFIG->site->guid;
+ $obj2->save();
+ $guids[] = $obj2->guid;
+
+ $options = array(
+ 'metadata_name' => 'test_md',
+ 'metadata_value' => 'test',
+ 'site_guid' => 2
+ );
+
+ $es = elgg_get_entities_from_metadata($options);
+ $this->assertTrue(is_array($es));
+ $this->assertEqual(1, count($es));
+ $this->assertEqual($right_guid, $es[0]->guid);
+
+ foreach ($guids as $guid) {
+ get_entity($guid)->delete();
+ }
+ }
+
+ public function testElggApiGettersEntitySiteSingularAny() {
+ global $CONFIG;
+
+ $guids = array();
+
+ $obj1 = new ElggObject();
+ $obj1->test_md = 'test';
+ // luckily this is never checked.
+ $obj1->site_guid = 2;
+ $obj1->save();
+ $guids[] = $obj1->guid;
+
+ $obj2 = new ElggObject();
+ $obj2->test_md = 'test';
+ $obj2->site_guid = $CONFIG->site->guid;
+ $obj2->save();
+ $guids[] = $obj2->guid;
+
+ $options = array(
+ 'metadata_name' => 'test_md',
+ 'metadata_value' => 'test',
+ 'site_guid' => ELGG_ENTITIES_ANY_VALUE,
+ 'limit' => 2,
+ 'order_by' => 'e.guid DESC'
+ );
+
+ $es = elgg_get_entities_from_metadata($options);
+ $this->assertTrue(is_array($es));
+ $this->assertEqual(2, count($es));
+
+ foreach ($es as $e) {
+ $this->assertTrue(in_array($e->guid, $guids));
+ }
+
+ foreach ($guids as $guid) {
+ get_entity($guid)->delete();
+ }
+ }
+
+ public function testElggApiGettersEntitySitePlural() {
+ global $CONFIG;
+
+ $guids = array();
+
+ $obj1 = new ElggObject();
+ $obj1->test_md = 'test';
+ // luckily this is never checked.
+ $obj1->site_guid = 2;
+ $obj1->save();
+ $guids[] = $obj1->guid;
+
+ $obj2 = new ElggObject();
+ $obj2->test_md = 'test';
+ $obj2->site_guid = $CONFIG->site->guid;
+ $obj2->save();
+ $guids[] = $obj2->guid;
+
+ $options = array(
+ 'metadata_name' => 'test_md',
+ 'metadata_value' => 'test',
+ 'site_guids' => array($CONFIG->site->guid, 2),
+ 'limit' => 2,
+ 'order_by' => 'e.guid DESC'
+ );
+
+ $es = elgg_get_entities_from_metadata($options);
+ $this->assertTrue(is_array($es));
+ $this->assertEqual(2, count($es));
+
+ foreach ($es as $e) {
+ $this->assertTrue(in_array($e->guid, $guids));
+ }
+
+ foreach ($guids as $guid) {
+ get_entity($guid)->delete();
+ }
+ }
+
+ public function testElggApiGettersEntitySitePluralSomeInvalid() {
+ global $CONFIG;
+
+ $guids = array();
+
+ $obj1 = new ElggObject();
+ $obj1->test_md = 'test';
+ // luckily this is never checked.
+ $obj1->site_guid = 2;
+ $obj1->save();
+ $guids[] = $obj1->guid;
+
+ $obj2 = new ElggObject();
+ $obj2->test_md = 'test';
+ $obj2->save();
+ $guids[] = $obj2->guid;
+ $right_guid = $obj2->guid;
+
+ $options = array(
+ 'metadata_name' => 'test_md',
+ 'metadata_value' => 'test',
+ // just created the first entity so nothing will be "sited" by it.
+ 'site_guids' => array($CONFIG->site->guid, $guids[0]),
+ 'limit' => 2,
+ 'order_by' => 'e.guid DESC'
+ );
+
+ $es = elgg_get_entities_from_metadata($options);
+
+ $this->assertTrue(is_array($es));
+ $this->assertEqual(1, count($es));
+ $this->assertEqual($es[0]->guid, $right_guid);
+
+ foreach ($guids as $guid) {
+ get_entity($guid)->delete();
+ }
+ }
+
+ public function testElggApiGettersEntitySitePluralAllInvalid() {
+ global $CONFIG;
+
+ $guids = array();
+
+ $obj1 = new ElggObject();
+ $obj1->test_md = 'test';
+ // luckily this is never checked.
+ $obj1->site_guid = 2;
+ $obj1->save();
+ $guids[] = $obj1->guid;
+
+ $obj2 = new ElggObject();
+ $obj2->test_md = 'test';
+ $obj2->save();
+ $guids[] = $obj2->guid;
+ $right_guid = $obj2->guid;
+
+ $options = array(
+ 'metadata_name' => 'test_md',
+ 'metadata_value' => 'test',
+ // just created the first entity so nothing will be "sited" by it.
+ 'site_guids' => array($guids[0], $guids[1]),
+ 'limit' => 2,
+ 'order_by' => 'e.guid DESC'
+ );
+
+ $es = elgg_get_entities_from_metadata($options);
+
+ $this->assertTrue(empty($es));
+
+ foreach ($guids as $guid) {
+ get_entity($guid)->delete();
+ }
+ }
+
+ /**
+ * Private settings
+ */
+ public function testElggApiGettersEntitiesFromPrivateSettings() {
+
+ // create some test private settings
+ $setting_name = 'test_setting_name_' . rand();
+ $setting_value = rand(1000, 9999);
+ $setting_name2 = 'test_setting_name_' . rand();
+ $setting_value2 = rand(1000, 9999);
+
+ $subtypes = $this->getRandomValidSubtypes(array('object'), 1);
+ $subtype = $subtypes[0];
+ $guids = array();
+
+ // our targets
+ $valid = new ElggObject();
+ $valid->subtype = $subtype;
+ $valid->save();
+ $guids[] = $valid->getGUID();
+ set_private_setting($valid->getGUID(), $setting_name, $setting_value);
+ set_private_setting($valid->getGUID(), $setting_name2, $setting_value2);
+
+ $valid2 = new ElggObject();
+ $valid2->subtype = $subtype;
+ $valid2->save();
+ $guids[] = $valid2->getGUID();
+ set_private_setting($valid2->getGUID(), $setting_name, $setting_value);
+ set_private_setting($valid2->getGUID(), $setting_name2, $setting_value2);
+
+ // simple test with name
+ $options = array(
+ 'private_setting_name' => $setting_name
+ );
+
+ $entities = elgg_get_entities_from_private_settings($options);
+
+ foreach ($entities as $entity) {
+ $this->assertTrue(in_array($entity->getGUID(), $guids));
+ $value = get_private_setting($entity->getGUID(), $setting_name);
+ $this->assertEqual($value, $setting_value);
+ }
+
+ // simple test with value
+ $options = array(
+ 'private_setting_value' => $setting_value
+ );
+
+ $entities = elgg_get_entities_from_private_settings($options);
+
+ foreach ($entities as $entity) {
+ $this->assertTrue(in_array($entity->getGUID(), $guids));
+ $value = get_private_setting($entity->getGUID(), $setting_name);
+ $this->assertEqual($value, $setting_value);
+ }
+
+ // test pairs
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => $subtype,
+ 'private_setting_name_value_pairs' => array(
+ array(
+ 'name' => $setting_name,
+ 'value' => $setting_value
+ ),
+ array(
+ 'name' => $setting_name2,
+ 'value' => $setting_value2
+ )
+ )
+ );
+
+ $entities = elgg_get_entities_from_private_settings($options);
+ $this->assertEqual(2, count($entities));
+ foreach ($entities as $entity) {
+ $this->assertTrue(in_array($entity->getGUID(), $guids));
+ }
+
+ foreach ($guids as $guid) {
+ if ($e = get_entity($guid)) {
+ $e->delete();
+ }
+ }
+ }
+
+ /**
+ * Location
+ */
+ public function testElggApiGettersEntitiesFromLocation() {
+
+ // a test location that is out of this world
+ $lat = 500;
+ $long = 500;
+ $delta = 5;
+
+ $subtypes = $this->getRandomValidSubtypes(array('object'), 1);
+ $subtype = $subtypes[0];
+ $guids = array();
+
+ // our objects
+ $valid = new ElggObject();
+ $valid->subtype = $subtype;
+ $valid->save();
+ $guids[] = $valid->getGUID();
+ $valid->setLatLong($lat, $long);
+
+ $valid2 = new ElggObject();
+ $valid2->subtype = $subtype;
+ $valid2->save();
+ $guids[] = $valid2->getGUID();
+ $valid2->setLatLong($lat + 2 * $delta, $long + 2 * $delta);
+
+ // limit to first object
+ $options = array(
+ 'latitude' => $lat,
+ 'longitude' => $long,
+ 'distance' => $delta
+ );
+
+ //global $CONFIG;
+ //$CONFIG->debug = 'NOTICE';
+ $entities = elgg_get_entities_from_location($options);
+ //unset($CONFIG->debug);
+
+ $this->assertEqual(1, count($entities));
+ $this->assertEqual($entities[0]->getGUID(), $valid->getGUID());
+
+ // get both objects
+ $options = array(
+ 'latitude' => $lat,
+ 'longitude' => $long,
+ 'distance' => array('latitude' => 2 * $delta, 'longitude' => 2 * $delta)
+ );
+
+ $entities = elgg_get_entities_from_location($options);
+
+ $this->assertEqual(2, count($entities));
+ foreach ($entities as $entity) {
+ $this->assertTrue(in_array($entity->getGUID(), $guids));
+ }
+
+ foreach ($guids as $guid) {
+ if ($e = get_entity($guid)) {
+ $e->delete();
+ }
+ }
+ }
+
+
+ public function testElggGetEntitiesFromRelationshipCount() {
+ $entities = $this->entities;
+ $relationships = array();
+ $count = count($entities);
+ $max = $count - 1;
+ $relationship_name = 'test_relationship_' . rand(0, 1000);
+
+ for ($i = 0; $i < $count; $i++) {
+ do {
+ $popular_entity = $entities[array_rand($entities)];
+ } while (array_key_exists($popular_entity->guid, $relationships));
+
+ $relationships[$popular_entity->guid] = array();
+
+ for ($c = 0; $c < $max; $c++) {
+ do {
+ $fan_entity = $entities[array_rand($entities)];
+ } while ($fan_entity->guid == $popular_entity->guid || in_array($fan_entity->guid, $relationships[$popular_entity->guid]));
+
+ $relationships[$popular_entity->guid][] = $fan_entity->guid;
+ add_entity_relationship($fan_entity->guid, $relationship_name, $popular_entity->guid);
+ }
+
+ $max--;
+ }
+
+ $options = array(
+ 'relationship' => $relationship_name,
+ 'limit' => $count
+ );
+
+ $entities = elgg_get_entities_from_relationship_count($options);
+
+ foreach ($entities as $e) {
+ $options = array(
+ 'relationship' => $relationship_name,
+ 'limit' => 100,
+ 'relationship_guid' => $e->guid,
+ 'inverse_relationship' => true
+ );
+
+ $fan_entities = elgg_get_entities_from_relationship($options);
+
+ $this->assertEqual(count($fan_entities), count($relationships[$e->guid]));
+
+ foreach ($fan_entities as $fan_entity) {
+ $this->assertTrue(in_array($fan_entity->guid, $relationships[$e->guid]));
+ $this->assertNotIdentical(false, check_entity_relationship($fan_entity->guid, $relationship_name, $e->guid));
+ }
+ }
+ }
+
+ public function testElggGetEntitiesByGuidSingular() {
+ foreach ($this->entities as $e) {
+ $options = array(
+ 'guid' => $e->guid
+ );
+ $es = elgg_get_entities($options);
+
+ $this->assertEqual(count($es), 1);
+ $this->assertEqual($es[0]->guid, $e->guid);
+ }
+ }
+
+ public function testElggGetEntitiesByGuidPlural() {
+ $guids = array();
+
+ foreach ($this->entities as $e) {
+ $guids[] = $e->guid;
+ }
+
+ $options = array(
+ 'guids' => $guids,
+ 'limit' => 100
+ );
+
+ $es = elgg_get_entities($options);
+
+ $this->assertEqual(count($es), count($this->entities));
+
+ foreach ($es as $e) {
+ $this->assertTrue(in_array($e->guid, $guids));
+ }
+ }
+
+ public function testElggGetEntitiesFromAnnotationsCalculateX() {
+ $types = array(
+ 'sum',
+ 'avg',
+ 'min',
+ 'max'
+ );
+
+ foreach ($types as $type) {
+ $subtypes = $this->getRandomValidSubtypes(array('object'), 5);
+ $name = 'test_annotation_' . rand(0, 9999);
+ $values = array();
+ $options = array(
+ 'type' => 'object',
+ 'subtypes' => $subtypes,
+ 'limit' => 5
+ );
+
+ $es = elgg_get_entities($options);
+
+ foreach ($es as $e) {
+ $value = rand(0,9999);
+ $e->annotate($name, $value);
+
+ $value2 = rand(0,9999);
+ $e->annotate($name, $value2);
+
+ switch ($type) {
+ case 'sum':
+ $calc_value = $value + $value2;
+ break;
+
+ case 'avg':
+ $calc_value = ($value + $value2) / 2;
+ break;
+
+ case 'min':
+ $calc_value = min(array($value, $value2));
+ break;
+
+ case 'max':
+ $calc_value = max(array($value, $value2));
+ break;
+ }
+
+ $values[$e->guid] = $calc_value;
+ }
+
+ arsort($values);
+ $order = array_keys($values);
+
+ $options = array(
+ 'type' => 'object',
+ 'subtypes' => $subtypes,
+ 'limit' => 5,
+ 'annotation_name' => $name,
+ 'calculation' => $type
+ );
+
+ $es = elgg_get_entities_from_annotation_calculation($options);
+
+ foreach ($es as $i => $e) {
+ $value = 0;
+ $as = $e->getAnnotations($name);
+ // should only ever be 2
+ $this->assertEqual(2, count($as));
+
+ $value = $as[0]->value;
+ $value2 = $as[1]->value;
+
+ switch ($type) {
+ case 'sum':
+ $calc_value = $value + $value2;
+ break;
+
+ case 'avg':
+ $calc_value = ($value + $value2) / 2;
+ break;
+
+ case 'min':
+ $calc_value = min(array($value, $value2));
+ break;
+
+ case 'max':
+ $calc_value = max(array($value, $value2));
+ break;
+ }
+
+ $this->assertEqual($e->guid, $order[$i]);
+ $this->assertEqual($values[$e->guid], $calc_value);
+ }
+ }
+ }
+
+ public function testElggGetEntitiesFromAnnotationCalculationCount() {
+ // add two annotations with a unique name to an entity
+ // then count the number of entities with that annotation name
+
+ $subtypes = $this->getRandomValidSubtypes(array('object'), 1);
+ $name = 'test_annotation_' . rand(0, 9999);
+ $values = array();
+ $options = array(
+ 'type' => 'object',
+ 'subtypes' => $subtypes,
+ 'limit' => 1
+ );
+ $es = elgg_get_entities($options);
+ $entity = $es[0];
+ $value = rand(0, 9999);
+ $entity->annotate($name, $value);
+ $value = rand(0, 9999);
+ $entity->annotate($name, $value);
+
+ $options = array(
+ 'type' => 'object',
+ 'subtypes' => $subtypes,
+ 'annotation_name' => $name,
+ 'calculation' => 'count',
+ 'count' => true,
+ );
+ $count = elgg_get_entities_from_annotation_calculation($options);
+ $this->assertEqual(1, $count);
+ }
+
+ public function testElggGetAnnotationsAnnotationNames() {
+ $options = array('annotation_names' => array());
+ $a_e_map = array();
+
+ // create test annotations on a few entities.
+ for ($i=0; $i<3; $i++) {
+ do {
+ $e = $this->entities[array_rand($this->entities)];
+ } while(in_array($e->guid, $a_e_map));
+ $annotations = $this->createRandomAnnotations($e);
+
+ foreach($annotations as $a) {
+ $options['annotation_names'][] = $a->name;
+ $a_e_map[$a->id] = $e->guid;
+ }
+ }
+
+ $as = elgg_get_annotations($options);
+
+ $this->assertEqual(count($a_e_map), count($as));
+
+ foreach ($as as $a) {
+ $this->assertEqual($a_e_map[$a->id], $a->entity_guid);
+ }
+ }
+
+ public function testElggGetAnnotationsAnnotationValues() {
+ $options = array('annotation_values' => array());
+ $a_e_map = array();
+
+ // create test annotations on a few entities.
+ for ($i=0; $i<3; $i++) {
+ do {
+ $e = $this->entities[array_rand($this->entities)];
+ } while(in_array($e->guid, $a_e_map));
+ $annotations = $this->createRandomAnnotations($e);
+
+ foreach($annotations as $a) {
+ $options['annotation_values'][] = $a->value;
+ $a_e_map[$a->id] = $e->guid;
+ }
+ }
+
+ $as = elgg_get_annotations($options);
+
+ $this->assertEqual(count($a_e_map), count($as));
+
+ foreach ($as as $a) {
+ $this->assertEqual($a_e_map[$a->id], $a->entity_guid);
+ }
+ }
+
+ public function testElggGetAnnotationsAnnotationOwnerGuids() {
+ $options = array('annotation_owner_guids' => array());
+ $a_e_map = array();
+
+ // create test annotations on a single entity
+ for ($i=0; $i<3; $i++) {
+ do {
+ $e = $this->entities[array_rand($this->entities)];
+ } while(in_array($e->guid, $a_e_map));
+
+ // remove annotations left over from previous tests.
+ elgg_delete_annotations(array('annotation_owner_guid' => $e->guid));
+ $annotations = $this->createRandomAnnotations($e);
+
+ foreach($annotations as $a) {
+ $options['annotation_owner_guids'][] = $e->guid;
+ $a_e_map[$a->id] = $e->guid;
+ }
+ }
+
+ $as = elgg_get_annotations($options);
+ $this->assertEqual(count($a_e_map), count($as));
+
+ foreach ($as as $a) {
+ $this->assertEqual($a_e_map[$a->id], $a->owner_guid);
+ }
+ }
+
+ public function testElggGetEntitiesBadWheres() {
+ $options = array(
+ 'container_guid' => 'abc'
+ );
+
+ $entities = elgg_get_entities($options);
+ $this->assertFalse($entities);
+ }
+
+ public function testEGEEmptySubtypePlurality() {
+ $options = array(
+ 'type' => 'user',
+ 'subtypes' => ''
+ );
+
+ $entities = elgg_get_entities($options);
+ $this->assertTrue(is_array($entities));
+
+ $options = array(
+ 'type' => 'user',
+ 'subtype' => ''
+ );
+
+ $entities = elgg_get_entities($options);
+ $this->assertTrue(is_array($entities));
+
+ $options = array(
+ 'type' => 'user',
+ 'subtype' => array('')
+ );
+
+ $entities = elgg_get_entities($options);
+ $this->assertTrue(is_array($entities));
+
+ $options = array(
+ 'type' => 'user',
+ 'subtypes' => array('')
+ );
+
+ $entities = elgg_get_entities($options);
+ $this->assertTrue(is_array($entities));
+ }
}
diff --git a/engine/tests/api/helpers.php b/engine/tests/api/helpers.php
index a2152a0ef..414fb4145 100644
--- a/engine/tests/api/helpers.php
+++ b/engine/tests/api/helpers.php
@@ -5,8 +5,6 @@
*
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
class ElggCoreHelpersTest extends ElggCoreUnitTest {
@@ -30,6 +28,10 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {
public function tearDown() {
// do not allow SimpleTest to interpret Elgg notices as exceptions
$this->swallowErrors();
+
+ global $CONFIG;
+ unset($CONFIG->externals);
+ unset($CONFIG->externals_map);
}
/**
@@ -42,7 +44,6 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {
/**
* Test elgg_instanceof()
- * @return unknown_type
*/
public function testElggInstanceOf() {
$entity = new ElggObject();
@@ -63,5 +64,642 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {
$this->assertFalse(elgg_instanceof($bad_entity, 'object'));
$this->assertFalse(elgg_instanceof($bad_entity, 'object', 'test_subtype'));
+ remove_subtype('object', 'test_subtype');
+ }
+
+ /**
+ * Test elgg_normalize_url()
+ */
+ public function testElggNormalizeURL() {
+ $conversions = array(
+ 'http://example.com' => 'http://example.com',
+ 'https://example.com' => 'https://example.com',
+ 'http://example-time.com' => 'http://example-time.com',
+
+ '//example.com' => '//example.com',
+ 'ftp://example.com/file' => 'ftp://example.com/file',
+ 'mailto:brett@elgg.org' => 'mailto:brett@elgg.org',
+ 'javascript:alert("test")' => 'javascript:alert("test")',
+ 'app://endpoint' => 'app://endpoint',
+
+ 'example.com' => 'http://example.com',
+ 'example.com/subpage' => 'http://example.com/subpage',
+
+ 'page/handler' => elgg_get_site_url() . 'page/handler',
+ 'page/handler?p=v&p2=v2' => elgg_get_site_url() . 'page/handler?p=v&p2=v2',
+ 'mod/plugin/file.php' => elgg_get_site_url() . 'mod/plugin/file.php',
+ 'mod/plugin/file.php?p=v&p2=v2' => elgg_get_site_url() . 'mod/plugin/file.php?p=v&p2=v2',
+ 'rootfile.php' => elgg_get_site_url() . 'rootfile.php',
+ 'rootfile.php?p=v&p2=v2' => elgg_get_site_url() . 'rootfile.php?p=v&p2=v2',
+
+ '/page/handler' => elgg_get_site_url() . 'page/handler',
+ '/page/handler?p=v&p2=v2' => elgg_get_site_url() . 'page/handler?p=v&p2=v2',
+ '/mod/plugin/file.php' => elgg_get_site_url() . 'mod/plugin/file.php',
+ '/mod/plugin/file.php?p=v&p2=v2' => elgg_get_site_url() . 'mod/plugin/file.php?p=v&p2=v2',
+ '/rootfile.php' => elgg_get_site_url() . 'rootfile.php',
+ '/rootfile.php?p=v&p2=v2' => elgg_get_site_url() . 'rootfile.php?p=v&p2=v2',
+ );
+
+ foreach ($conversions as $input => $output) {
+ $this->assertIdentical($output, elgg_normalize_url($input));
+ }
+ }
+
+
+ /**
+ * Test elgg_register_js()
+ */
+ public function testElggRegisterJS() {
+ global $CONFIG;
+
+ // specify name
+ $result = elgg_register_js('key', 'http://test1.com', 'footer');
+ $this->assertTrue($result);
+ $this->assertTrue(isset($CONFIG->externals_map['js']['key']));
+
+ $item = $CONFIG->externals_map['js']['key'];
+ $this->assertTrue($CONFIG->externals['js']->contains($item));
+
+ $priority = $CONFIG->externals['js']->getPriority($item);
+ $this->assertTrue($priority !== false);
+
+ $item = $CONFIG->externals['js']->getElement($priority);
+ $this->assertIdentical('http://test1.com', $item->url);
+
+ // send a bad url
+ $result = elgg_register_js('bad', null);
+ $this->assertFalse($result);
+ }
+
+ /**
+ * Test elgg_register_css()
+ */
+ public function testElggRegisterCSS() {
+ global $CONFIG;
+
+ // specify name
+ $result = elgg_register_css('key', 'http://test1.com');
+ $this->assertTrue($result);
+ $this->assertTrue(isset($CONFIG->externals_map['css']['key']));
+
+ $item = $CONFIG->externals_map['css']['key'];
+ $this->assertTrue($CONFIG->externals['css']->contains($item));
+
+ $priority = $CONFIG->externals['css']->getPriority($item);
+ $this->assertTrue($priority !== false);
+
+ $item = $CONFIG->externals['css']->getElement($priority);
+ $this->assertIdentical('http://test1.com', $item->url);
+ }
+
+ /**
+ * Test elgg_unregister_js()
+ */
+ public function testElggUnregisterJS() {
+ global $CONFIG;
+
+ $base = trim(elgg_get_site_url(), "/");
+
+ $urls = array('id1' => "$base/urla", 'id2' => "$base/urlb", 'id3' => "$base/urlc");
+
+ foreach ($urls as $id => $url) {
+ elgg_register_js($id, $url);
+ }
+
+ $result = elgg_unregister_js('id1');
+ $this->assertTrue($result);
+
+ $js = $CONFIG->externals['js'];
+ $elements = $js->getElements();
+ $this->assertFalse(isset($CONFIG->externals_map['js']['id1']));
+
+ foreach ($elements as $element) {
+ if (isset($element->name)) {
+ $this->assertFalse($element->name == 'id1');
+ }
+ }
+
+ $result = elgg_unregister_js('id1');
+ $this->assertFalse($result);
+
+ $result = elgg_unregister_js('', 'does_not_exist');
+ $this->assertFalse($result);
+
+ $result = elgg_unregister_js('id2');
+ $elements = $js->getElements();
+
+ $this->assertFalse(isset($CONFIG->externals_map['js']['id2']));
+ foreach ($elements as $element) {
+ if (isset($element->name)) {
+ $this->assertFalse($element->name == 'id2');
+ }
+ }
+
+ $this->assertTrue(isset($CONFIG->externals_map['js']['id3']));
+
+ $priority = $CONFIG->externals['js']->getPriority($CONFIG->externals_map['js']['id3']);
+ $this->assertTrue($priority !== false);
+
+ $item = $CONFIG->externals['js']->getElement($priority);
+ $this->assertIdentical($urls['id3'], $item->url);
+ }
+
+ /**
+ * Test elgg_load_js()
+ */
+ public function testElggLoadJS() {
+ global $CONFIG;
+
+ // load before register
+ elgg_load_js('key');
+ $result = elgg_register_js('key', 'http://test1.com', 'footer');
+ $this->assertTrue($result);
+
+ $js_urls = elgg_get_loaded_js('footer');
+ $this->assertIdentical(array(500 => 'http://test1.com'), $js_urls);
+ }
+
+ /**
+ * Test elgg_get_loaded_js()
+ */
+ public function testElggGetJS() {
+ global $CONFIG;
+
+ $base = trim(elgg_get_site_url(), "/");
+
+ $urls = array(
+ 'id1' => "$base/urla",
+ 'id2' => "$base/urlb",
+ 'id3' => "$base/urlc"
+ );
+
+ foreach ($urls as $id => $url) {
+ elgg_register_js($id, $url);
+ elgg_load_js($id);
+ }
+
+ $js_urls = elgg_get_loaded_js('head');
+
+ $this->assertIdentical($js_urls[500], $urls['id1']);
+ $this->assertIdentical($js_urls[501], $urls['id2']);
+ $this->assertIdentical($js_urls[502], $urls['id3']);
+
+ $js_urls = elgg_get_loaded_js('footer');
+ $this->assertIdentical(array(), $js_urls);
+ }
+
+ // test ElggPriorityList
+ public function testElggPriorityListAdd() {
+ $pl = new ElggPriorityList();
+ $elements = array(
+ 'Test value',
+ 'Test value 2',
+ 'Test value 3'
+ );
+
+ shuffle($elements);
+
+ foreach ($elements as $element) {
+ $this->assertTrue($pl->add($element) !== false);
+ }
+
+ $test_elements = $pl->getElements();
+
+ $this->assertTrue(is_array($test_elements));
+
+ foreach ($test_elements as $i => $element) {
+ // should be in the array
+ $this->assertTrue(in_array($element, $elements));
+
+ // should be the only element, so priority 0
+ $this->assertEqual($i, array_search($element, $elements));
+ }
+ }
+
+ public function testElggPriorityListAddWithPriority() {
+ $pl = new ElggPriorityList();
+
+ $elements = array(
+ 10 => 'Test Element 10',
+ 5 => 'Test Element 5',
+ 0 => 'Test Element 0',
+ 100 => 'Test Element 100',
+ -1 => 'Test Element -1',
+ -5 => 'Test Element -5'
+ );
+
+ foreach ($elements as $priority => $element) {
+ $pl->add($element, $priority);
+ }
+
+ $test_elements = $pl->getElements();
+
+ // should be sorted by priority
+ $elements_sorted = array(
+ -5 => 'Test Element -5',
+ -1 => 'Test Element -1',
+ 0 => 'Test Element 0',
+ 5 => 'Test Element 5',
+ 10 => 'Test Element 10',
+ 100 => 'Test Element 100',
+ );
+
+ $this->assertIdentical($elements_sorted, $test_elements);
+
+ foreach ($test_elements as $priority => $element) {
+ $this->assertIdentical($elements[$priority], $element);
+ }
+ }
+
+ public function testElggPriorityListGetNextPriority() {
+ $pl = new ElggPriorityList();
+
+ $elements = array(
+ 2 => 'Test Element',
+ 0 => 'Test Element 2',
+ -2 => 'Test Element 3',
+ );
+
+ foreach ($elements as $priority => $element) {
+ $pl->add($element, $priority);
+ }
+
+ // we're not specifying a priority so it should be the next consecutive to 0.
+ $this->assertEqual(1, $pl->getNextPriority());
+
+ // add another one at priority 1
+ $pl->add('Test Element 1');
+
+ // next consecutive to 0 is now 3.
+ $this->assertEqual(3, $pl->getNextPriority());
+ }
+
+ public function testElggPriorityListRemove() {
+ $pl = new ElggPriorityList();
+
+ $elements = array();
+ for ($i=0; $i<3; $i++) {
+ $element = new stdClass();
+ $element->name = "Test Element $i";
+ $element->someAttribute = rand(0, 9999);
+ $elements[] = $element;
+ $pl->add($element);
+ }
+
+ $pl->remove($elements[1]);
+
+ $test_elements = $pl->getElements();
+
+ // make sure it's gone.
+ $this->assertEqual(2, count($test_elements));
+ $this->assertIdentical($elements[0], $test_elements[0]);
+ $this->assertIdentical($elements[2], $test_elements[2]);
+ }
+
+ public function testElggPriorityListMove() {
+ $pl = new ElggPriorityList();
+
+ $elements = array(
+ -5 => 'Test Element -5',
+ 0 => 'Test Element 0',
+ 5 => 'Test Element 5',
+ );
+
+ foreach ($elements as $priority => $element) {
+ $pl->add($element, $priority);
+ }
+
+ $this->assertEqual($pl->move($elements[-5], 10), 10);
+
+ // check it's at the new place
+ $this->assertIdentical($elements[-5], $pl->getElement(10));
+
+ // check it's not at the old
+ $this->assertFalse($pl->getElement(-5));
+ }
+
+ public function testElggPriorityListConstructor() {
+ $elements = array(
+ 10 => 'Test Element 10',
+ 5 => 'Test Element 5',
+ 0 => 'Test Element 0',
+ 100 => 'Test Element 100',
+ -1 => 'Test Element -1',
+ -5 => 'Test Element -5'
+ );
+
+ $pl = new ElggPriorityList($elements);
+ $test_elements = $pl->getElements();
+
+ $elements_sorted = array(
+ -5 => 'Test Element -5',
+ -1 => 'Test Element -1',
+ 0 => 'Test Element 0',
+ 5 => 'Test Element 5',
+ 10 => 'Test Element 10',
+ 100 => 'Test Element 100',
+ );
+
+ $this->assertIdentical($elements_sorted, $test_elements);
+ }
+
+ public function testElggPriorityListGetPriority() {
+ $pl = new ElggPriorityList();
+
+ $elements = array(
+ 'Test element 0',
+ 'Test element 1',
+ 'Test element 2',
+ );
+
+ foreach ($elements as $element) {
+ $pl->add($element);
+ }
+
+ $this->assertIdentical(0, $pl->getPriority($elements[0]));
+ $this->assertIdentical(1, $pl->getPriority($elements[1]));
+ $this->assertIdentical(2, $pl->getPriority($elements[2]));
+ }
+
+ public function testElggPriorityListGetElement() {
+ $pl = new ElggPriorityList();
+ $priorities = array();
+
+ $elements = array(
+ 'Test element 0',
+ 'Test element 1',
+ 'Test element 2',
+ );
+
+ foreach ($elements as $element) {
+ $priorities[] = $pl->add($element);
+ }
+
+ $this->assertIdentical($elements[0], $pl->getElement($priorities[0]));
+ $this->assertIdentical($elements[1], $pl->getElement($priorities[1]));
+ $this->assertIdentical($elements[2], $pl->getElement($priorities[2]));
+ }
+
+ public function testElggPriorityListPriorityCollision() {
+ $pl = new ElggPriorityList();
+
+ $elements = array(
+ 5 => 'Test element 5',
+ 6 => 'Test element 6',
+ 0 => 'Test element 0',
+ );
+
+ foreach ($elements as $priority => $element) {
+ $pl->add($element, $priority);
+ }
+
+ // add at a colliding priority
+ $pl->add('Colliding element', 5);
+
+ // should float to the top closest to 5, so 7
+ $this->assertEqual(7, $pl->getPriority('Colliding element'));
+ }
+
+ public function testElggPriorityListIterator() {
+ $elements = array(
+ -5 => 'Test element -5',
+ 0 => 'Test element 0',
+ 5 => 'Test element 5'
+ );
+
+ $pl = new ElggPriorityList($elements);
+
+ foreach ($pl as $priority => $element) {
+ $this->assertIdentical($elements[$priority], $element);
+ }
+ }
+
+ public function testElggPriorityListCountable() {
+ $pl = new ElggPriorityList();
+
+ $this->assertEqual(0, count($pl));
+
+ $pl->add('Test element 0');
+ $this->assertEqual(1, count($pl));
+
+ $pl->add('Test element 1');
+ $this->assertEqual(2, count($pl));
+
+ $pl->add('Test element 2');
+ $this->assertEqual(3, count($pl));
+ }
+
+ public function testElggPriorityListUserSort() {
+ $elements = array(
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ );
+
+ $elements_sorted_string = $elements;
+
+ shuffle($elements);
+ $pl = new ElggPriorityList($elements);
+
+ // will sort by priority
+ $test_elements = $pl->getElements();
+ $this->assertIdentical($elements, $test_elements);
+
+ function test_sort($elements) {
+ sort($elements, SORT_LOCALE_STRING);
+ return $elements;
+ }
+
+ // force a new sort using our function
+ $pl->sort('test_sort');
+ $test_elements = $pl->getElements();
+
+ $this->assertIdentical($elements_sorted_string, $test_elements);
+ }
+
+ // see https://github.com/elgg/elgg/issues/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);
+ }
+
+ public function testElggBatchReadHandlesBrokenEntities() {
+ $num_test_entities = 8;
+ $guids = array();
+ for ($i = $num_test_entities; $i > 0; $i--) {
+ $entity = new ElggObject();
+ $entity->type = 'object';
+ $entity->subtype = 'test_5357_subtype';
+ $entity->access_id = ACCESS_PUBLIC;
+ $entity->save();
+ $guids[] = $entity->guid;
+ _elgg_invalidate_cache_for_entity($entity->guid);
+ }
+
+ // break entities such that the first fetch has one incomplete
+ // and the second and third fetches have only incompletes!
+ $db_prefix = elgg_get_config('dbprefix');
+ delete_data("
+ DELETE FROM {$db_prefix}objects_entity
+ WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}, {$guids[4]}, {$guids[5]})
+ ");
+
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'test_5357_subtype',
+ 'order_by' => 'e.guid',
+ );
+
+ $entities_visited = array();
+ $batch = new ElggBatch('elgg_get_entities', $options, null, 2);
+ /* @var ElggEntity[] $batch */
+ foreach ($batch as $entity) {
+ $entities_visited[] = $entity->guid;
+ }
+
+ // The broken entities should not have been visited
+ $this->assertEqual($entities_visited, array($guids[0], $guids[6], $guids[7]));
+
+ // cleanup (including leftovers from previous tests)
+ $entity_rows = elgg_get_entities(array_merge($options, array(
+ 'callback' => '',
+ 'limit' => false,
+ )));
+ $guids = array();
+ foreach ($entity_rows as $row) {
+ $guids[] = $row->guid;
+ }
+ delete_data("DELETE FROM {$db_prefix}entities WHERE guid IN (" . implode(',', $guids) . ")");
+ delete_data("DELETE FROM {$db_prefix}objects_entity WHERE guid IN (" . implode(',', $guids) . ")");
+ }
+
+ public function testElggBatchDeleteHandlesBrokenEntities() {
+ $num_test_entities = 8;
+ $guids = array();
+ for ($i = $num_test_entities; $i > 0; $i--) {
+ $entity = new ElggObject();
+ $entity->type = 'object';
+ $entity->subtype = 'test_5357_subtype';
+ $entity->access_id = ACCESS_PUBLIC;
+ $entity->save();
+ $guids[] = $entity->guid;
+ _elgg_invalidate_cache_for_entity($entity->guid);
+ }
+
+ // break entities such that the first fetch has one incomplete
+ // and the second and third fetches have only incompletes!
+ $db_prefix = elgg_get_config('dbprefix');
+ delete_data("
+ DELETE FROM {$db_prefix}objects_entity
+ WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}, {$guids[4]}, {$guids[5]})
+ ");
+
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'test_5357_subtype',
+ 'order_by' => 'e.guid',
+ );
+
+ $entities_visited = array();
+ $batch = new ElggBatch('elgg_get_entities', $options, null, 2, false);
+ /* @var ElggEntity[] $batch */
+ foreach ($batch as $entity) {
+ $entities_visited[] = $entity->guid;
+ $entity->delete();
+ }
+
+ // The broken entities should not have been visited
+ $this->assertEqual($entities_visited, array($guids[0], $guids[6], $guids[7]));
+
+ // cleanup (including leftovers from previous tests)
+ $entity_rows = elgg_get_entities(array_merge($options, array(
+ 'callback' => '',
+ 'limit' => false,
+ )));
+ $guids = array();
+ foreach ($entity_rows as $row) {
+ $guids[] = $row->guid;
+ }
+ delete_data("DELETE FROM {$db_prefix}entities WHERE guid IN (" . implode(',', $guids) . ")");
+ delete_data("DELETE FROM {$db_prefix}objects_entity WHERE guid IN (" . implode(',', $guids) . ")");
+ }
+
+ 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/metadata.php b/engine/tests/api/metadata.php
new file mode 100644
index 000000000..d23510c6a
--- /dev/null
+++ b/engine/tests/api/metadata.php
@@ -0,0 +1,230 @@
+<?php
+/**
+ * Elgg Test metadata API
+ *
+ * @package Elgg
+ * @subpackage Test
+ */
+class ElggCoreMetadataAPITest extends ElggCoreUnitTest {
+ protected $metastrings;
+
+ /**
+ * Called before each test method.
+ */
+ public function setUp() {
+ $this->metastrings = array();
+ $this->object = new ElggObject();
+ }
+
+ /**
+ * Called after each test method.
+ */
+ public function tearDown() {
+ // do not allow SimpleTest to interpret Elgg notices as exceptions
+ $this->swallowErrors();
+
+ unset($this->object);
+ }
+
+ public function testGetMetastringById() {
+ foreach (array('metaUnitTest', 'metaunittest', 'METAUNITTEST') as $string) {
+ // since there is no guarantee that metastrings are garbage collected
+ // between unit test runs, we delete before testing
+ $this->delete_metastrings($string);
+ $this->create_metastring($string);
+ }
+
+ // lookup metastring id
+ $cs_ids = get_metastring_id('metaUnitTest', TRUE);
+ $this->assertEqual($cs_ids, $this->metastrings['metaUnitTest']);
+
+ // lookup all metastrings, ignoring case
+ $cs_ids = get_metastring_id('metaUnitTest', FALSE);
+ $this->assertEqual(count($cs_ids), 3);
+ $this->assertEqual(count($cs_ids), count($this->metastrings));
+ foreach ($cs_ids as $string )
+ {
+ $this->assertTrue(in_array($string, $this->metastrings));
+ }
+ }
+
+ public function testElggGetEntitiesFromMetadata() {
+ global $CONFIG, $METASTRINGS_CACHE, $METASTRINGS_DEADNAME_CACHE;
+ $METASTRINGS_CACHE = $METASTRINGS_DEADNAME_CACHE = array();
+
+ $this->object->title = 'Meta Unit Test';
+ $this->object->save();
+ $this->create_metastring('metaUnitTest');
+ $this->create_metastring('tested');
+
+ // create_metadata returns id of metadata on success
+ $this->assertNotEqual(false, create_metadata($this->object->guid, 'metaUnitTest', 'tested'));
+
+ // check value with improper case
+ $options = array('metadata_names' => 'metaUnitTest', 'metadata_values' => 'Tested', 'limit' => 10, 'metadata_case_sensitive' => TRUE);
+ $this->assertIdentical(array(), elgg_get_entities_from_metadata($options));
+
+ // compare forced case with ignored case
+ $options = array('metadata_names' => 'metaUnitTest', 'metadata_values' => 'tested', 'limit' => 10, 'metadata_case_sensitive' => TRUE);
+ $case_true = elgg_get_entities_from_metadata($options);
+ $this->assertIsA($case_true, 'array');
+
+ $options = array('metadata_names' => 'metaUnitTest', 'metadata_values' => 'Tested', 'limit' => 10, 'metadata_case_sensitive' => FALSE);
+ $case_false = elgg_get_entities_from_metadata($options);
+ $this->assertIsA($case_false, 'array');
+
+ $this->assertIdentical($case_true, $case_false);
+
+ // clean up
+ $this->object->delete();
+ }
+
+ public function testElggGetMetadataCount() {
+ $this->object->title = 'Meta Unit Test';
+ $this->object->save();
+
+ $guid = $this->object->getGUID();
+ create_metadata($guid, 'tested', 'tested1', 'text', 0, ACCESS_PUBLIC, true);
+ create_metadata($guid, 'tested', 'tested2', 'text', 0, ACCESS_PUBLIC, true);
+
+ $count = (int)elgg_get_metadata(array(
+ 'metadata_names' => array('tested'),
+ 'guid' => $guid,
+ 'count' => true,
+ ));
+
+ $this->assertIdentical($count, 2);
+
+ $this->object->delete();
+ }
+
+ public function testElggDeleteMetadata() {
+ $e = new ElggObject();
+ $e->save();
+
+ for ($i = 0; $i < 30; $i++) {
+ $name = "test_metadata$i";
+ $e->$name = rand(0, 10000);
+ }
+
+ $options = array(
+ 'guid' => $e->getGUID(),
+ 'limit' => 0,
+ );
+
+ $md = elgg_get_metadata($options);
+ $this->assertIdentical(30, count($md));
+
+ $this->assertTrue(elgg_delete_metadata($options));
+
+ $md = elgg_get_metadata($options);
+ $this->assertTrue(empty($md));
+
+ $e->delete();
+ }
+
+ /**
+ * https://github.com/Elgg/Elgg/issues/4867
+ */
+ public function testElggGetEntityMetadataWhereSqlWithFalseValue() {
+ $pair = array('name' => 'test' , 'value' => false);
+ $result = elgg_get_entity_metadata_where_sql('e', 'metadata', null, null, $pair);
+ $where = preg_replace( '/\s+/', ' ', $result['wheres'][0]);
+ $this->assertTrue(strpos($where, "msn1.string = 'test' AND BINARY msv1.string = 0") > 0);
+
+ $result = elgg_get_entity_metadata_where_sql('e', 'metadata', array('test'), array(false));
+ $where = preg_replace( '/\s+/', ' ', $result['wheres'][0]);
+ $this->assertTrue(strpos($where, "msn.string IN ('test')) AND ( BINARY msv.string IN ('0')"));
+ }
+
+ // Make sure metadata with multiple values is correctly deleted when re-written
+ // by another user
+ // https://github.com/elgg/elgg/issues/2776
+ public function test_elgg_metadata_multiple_values() {
+ $u1 = new ElggUser();
+ $u1->username = rand();
+ $u1->save();
+
+ $u2 = new ElggUser();
+ $u2->username = rand();
+ $u2->save();
+
+ $obj = new ElggObject();
+ $obj->owner_guid = $u1->guid;
+ $obj->container_guid = $u1->guid;
+ $obj->access_id = ACCESS_PUBLIC;
+ $obj->save();
+
+ $md_values = array(
+ 'one',
+ 'two',
+ 'three'
+ );
+
+ // need to fake different logins.
+ // good times without mocking.
+ $original_user = elgg_get_logged_in_user_entity();
+ $_SESSION['user'] = $u1;
+
+ elgg_set_ignore_access(false);
+
+ // add metadata as one user
+ $obj->test = $md_values;
+
+ // check only these md exists
+ $db_prefix = elgg_get_config('dbprefix');
+ $q = "SELECT * FROM {$db_prefix}metadata WHERE entity_guid = $obj->guid";
+ $data = get_data($q);
+
+ $this->assertEqual(count($md_values), count($data));
+ foreach ($data as $md_row) {
+ $md = elgg_get_metadata_from_id($md_row->id);
+ $this->assertTrue(in_array($md->value, $md_values));
+ $this->assertEqual('test', $md->name);
+ }
+
+ // add md w/ same name as a different user
+ $_SESSION['user'] = $u2;
+ $md_values2 = array(
+ 'four',
+ 'five',
+ 'six',
+ 'seven'
+ );
+
+ $obj->test = $md_values2;
+
+ $q = "SELECT * FROM {$db_prefix}metadata WHERE entity_guid = $obj->guid";
+ $data = get_data($q);
+
+ $this->assertEqual(count($md_values2), count($data));
+ foreach ($data as $md_row) {
+ $md = elgg_get_metadata_from_id($md_row->id);
+ $this->assertTrue(in_array($md->value, $md_values2));
+ $this->assertEqual('test', $md->name);
+ }
+
+ $_SESSION['user'] = $original_user;
+
+ $obj->delete();
+ $u1->delete();
+ $u2->delete();
+ }
+
+ protected function delete_metastrings($string) {
+ global $CONFIG, $METASTRINGS_CACHE, $METASTRINGS_DEADNAME_CACHE;
+ $METASTRINGS_CACHE = $METASTRINGS_DEADNAME_CACHE = array();
+
+ $string = sanitise_string($string);
+ mysql_query("DELETE FROM {$CONFIG->dbprefix}metastrings WHERE string = BINARY '$string'");
+ }
+
+ protected function create_metastring($string) {
+ global $CONFIG, $METASTRINGS_CACHE, $METASTRINGS_DEADNAME_CACHE;
+ $METASTRINGS_CACHE = $METASTRINGS_DEADNAME_CACHE = array();
+
+ $string = sanitise_string($string);
+ mysql_query("INSERT INTO {$CONFIG->dbprefix}metastrings (string) VALUES ('$string')");
+ $this->metastrings[$string] = mysql_insert_id();
+ }
+}
diff --git a/engine/tests/api/metadata_cache.php b/engine/tests/api/metadata_cache.php
new file mode 100644
index 000000000..7fb328169
--- /dev/null
+++ b/engine/tests/api/metadata_cache.php
@@ -0,0 +1,176 @@
+<?php
+/**
+ * Elgg Test metadata cache
+ *
+ * @package Elgg
+ * @subpackage Test
+ */
+class ElggCoreMetadataCacheTest extends ElggCoreUnitTest {
+
+ /**
+ * @var ElggVolatileMetadataCache
+ */
+ protected $cache;
+
+ /**
+ * @var ElggObject
+ */
+ protected $obj1;
+
+ /**
+ * @var int
+ */
+ protected $guid1;
+
+ /**
+ * @var ElggObject
+ */
+ protected $obj2;
+
+ /**
+ * @var int
+ */
+ protected $guid2;
+
+ protected $name = 'test';
+ protected $value = 'test';
+ protected $ignoreAccess;
+
+ /**
+ * Called before each test method.
+ */
+ public function setUp() {
+ $this->ignoreAccess = elgg_set_ignore_access(false);
+
+ $this->cache = elgg_get_metadata_cache();
+
+ $this->obj1 = new ElggObject();
+ $this->obj1->save();
+ $this->guid1 = $this->obj1->guid;
+
+ $this->obj2 = new ElggObject();
+ $this->obj2->save();
+ $this->guid2 = $this->obj2->guid;
+ }
+
+ /**
+ * Called after each test method.
+ */
+ public function tearDown() {
+ $this->obj1->delete();
+ $this->obj2->delete();
+
+ elgg_set_ignore_access($this->ignoreAccess);
+ }
+
+ public function testBasicApi() {
+ // test de-coupled instance
+ $cache = new ElggVolatileMetadataCache();
+ $cache->setIgnoreAccess(false);
+ $guid = 1;
+
+ $this->assertFalse($cache->isKnown($guid, $this->name));
+
+ $cache->markEmpty($guid, $this->name);
+ $this->assertTrue($cache->isKnown($guid, $this->name));
+ $this->assertNull($cache->load($guid, $this->name));
+
+ $cache->markUnknown($guid, $this->name);
+ $this->assertFalse($cache->isKnown($guid, $this->name));
+
+ $cache->save($guid, $this->name, $this->value);
+ $this->assertIdentical($cache->load($guid, $this->name), $this->value);
+
+ $cache->save($guid, $this->name, 1, true);
+ $this->assertIdentical($cache->load($guid, $this->name), array($this->value, 1));
+
+ $cache->clear($guid);
+ $this->assertFalse($cache->isKnown($guid, $this->name));
+ }
+
+ public function testReadsAreCached() {
+ // test that reads fill cache
+ $this->obj1->setMetaData($this->name, $this->value);
+ $this->cache->flush();
+
+ $this->obj1->getMetaData($this->name);
+ $this->assertIdentical($this->cache->load($this->guid1, $this->name), $this->value);
+ }
+
+ public function testWritesAreCached() {
+ // delete should mark cache as known to be empty
+ $this->obj1->deleteMetadata($this->name);
+ $this->assertTrue($this->cache->isKnown($this->guid1, $this->name));
+ $this->assertNull($this->cache->load($this->guid1, $this->name));
+
+ // without name, delete should invalidate the entire entity
+ $this->cache->save($this->guid1, $this->name, $this->value);
+ elgg_delete_metadata(array(
+ 'guid' => $this->guid1,
+ ));
+ $this->assertFalse($this->cache->isKnown($this->guid1, $this->name));
+
+ // test set
+ $this->obj1->setMetaData($this->name, $this->value);
+ $this->assertIdentical($this->cache->load($this->guid1, $this->name), $this->value);
+
+ // test set multiple
+ $this->obj1->setMetaData($this->name, 1, 'integer', true);
+ $this->assertIdentical($this->cache->load($this->guid1, $this->name), array($this->value, 1));
+
+ // writes when access is ignore should invalidate
+ $tmp_ignore = elgg_set_ignore_access(true);
+ $this->obj1->setMetaData($this->name, $this->value);
+ $this->assertFalse($this->cache->isKnown($this->guid1, $this->name));
+ elgg_set_ignore_access($tmp_ignore);
+ }
+
+ public function testDisableAndEnable() {
+ // both should mark cache unknown
+ $this->obj1->setMetaData($this->name, $this->value);
+ $this->obj1->disableMetadata($this->name);
+ $this->assertFalse($this->cache->isKnown($this->guid1, $this->name));
+
+ $this->cache->save($this->guid1, $this->name, $this->value);
+ $this->obj1->enableMetadata($this->name);
+ $this->assertFalse($this->cache->isKnown($this->guid1, $this->name));
+ }
+
+ public function testPopulateFromEntities() {
+ // test populating cache from set of entities
+ $this->obj1->setMetaData($this->name, $this->value);
+ $this->obj1->setMetaData($this->name, 4, 'integer', true);
+ $this->obj1->setMetaData("{$this->name}-2", "{$this->value}-2");
+ $this->obj2->setMetaData($this->name, $this->value);
+
+ $this->cache->flush();
+ $this->cache->populateFromEntities(array($this->guid1, $this->guid2));
+
+ $expected = array();
+ $expected[$this->name][] = $this->value;
+ $expected[$this->name][] = 4;
+ $expected["{$this->name}-2"] = "{$this->value}-2";
+ $this->assertIdentical($this->cache->loadAll($this->guid1), $expected);
+
+ $expected = array();
+ $expected[$this->name] = $this->value;
+ $this->assertIdentical($this->cache->loadAll($this->guid2), $expected);
+ }
+
+ public function testFilterHeavyEntities() {
+ $big_str = str_repeat('-', 5000);
+ $this->obj2->setMetaData($this->name, array($big_str, $big_str));
+
+ $guids = array($this->guid1, $this->guid2);
+ $expected = array($this->guid1);
+ $actual = $this->cache->filterMetadataHeavyEntities($guids, 6000);
+ $this->assertIdentical($actual, $expected);
+ }
+
+ public function testCreateMetadataInvalidates() {
+ $this->obj1->foo = 1;
+ create_metadata($this->guid1, 'foo', 2, '', elgg_get_logged_in_user_guid(), ACCESS_FRIENDS);
+
+ $this->assertEqual($this->obj1->foo, 2);
+ }
+}
diff --git a/engine/tests/api/metastrings.php b/engine/tests/api/metastrings.php
new file mode 100644
index 000000000..5efdab972
--- /dev/null
+++ b/engine/tests/api/metastrings.php
@@ -0,0 +1,217 @@
+<?php
+/**
+ * Elgg Metastrings test
+ *
+ * @package Elgg.Core
+ * @subpackage Metastrings.Test
+ */
+class ElggCoreMetastringsTest extends ElggCoreUnitTest {
+
+ public $metastringTypes = array('metadata', 'annotations');
+
+ /**
+ * Called before each test object.
+ */
+ public function __construct() {
+ parent::__construct();
+
+ $this->metastrings = array();
+ $this->object = new ElggObject();
+ $this->object->save();
+ }
+
+ public function createAnnotations($max = 1) {
+ $annotations = array();
+ for ($i=0; $i<$max; $i++) {
+ $name = 'test_annotation_name' . rand();
+ $value = 'test_annotation_value' . rand();
+ $id = create_annotation($this->object->guid, $name, $value);
+ $annotations[] = $id;
+ }
+
+ return $annotations;
+ }
+
+ public function createMetadata($max = 1) {
+ $metadata = array();
+ for ($i=0; $i<$max; $i++) {
+ $name = 'test_metadata_name' . rand();
+ $value = 'test_metadata_value' . rand();
+ $id = create_metadata($this->object->guid, $name, $value);
+ $metadata[] = $id;
+ }
+
+ return $metadata;
+ }
+
+ /**
+ * Called before each test method.
+ */
+ public function setUp() {
+
+ }
+
+ /**
+ * Called after each test method.
+ */
+ public function tearDown() {
+ access_show_hidden_entities(true);
+ elgg_delete_annotations(array(
+ 'guid' => $this->object->guid,
+ ));
+ access_show_hidden_entities(false);
+ }
+
+ /**
+ * Called after each test object.
+ */
+ public function __destruct() {
+ $this->object->delete();
+
+ parent::__destruct();
+ }
+
+ public function testDeleteByID() {
+ $db_prefix = elgg_get_config('dbprefix');
+ $annotations = $this->createAnnotations(1);
+ $metadata = $this->createMetadata(1);
+
+ foreach ($this->metastringTypes as $type) {
+ $id = ${$type}[0];
+ $table = $db_prefix . $type;
+ $q = "SELECT * FROM $table WHERE id = $id";
+ $test = get_data($q);
+
+ $this->assertEqual($test[0]->id, $id);
+ $this->assertIdentical(true, elgg_delete_metastring_based_object_by_id($id, $type));
+ $this->assertIdentical(array(), get_data($q));
+ }
+ }
+
+ public function testGetMetastringObjectFromID() {
+ $db_prefix = elgg_get_config('dbprefix');
+ $annotations = $this->createAnnotations(1);
+ $metadata = $this->createMetadata(1);
+
+ foreach ($this->metastringTypes as $type) {
+ $id = ${$type}[0];
+ $test = elgg_get_metastring_based_object_from_id($id, $type);
+
+ $this->assertEqual($id, $test->id);
+ }
+ }
+
+ public function testGetMetastringObjectFromIDWithDisabledAnnotation() {
+ $name = 'test_annotation_name' . rand();
+ $value = 'test_annotation_value' . rand();
+ $id = create_annotation($this->object->guid, $name, $value);
+ $annotation = elgg_get_annotation_from_id($id);
+ $this->assertTrue($annotation->disable());
+
+ $test = elgg_get_metastring_based_object_from_id($id, 'annotation');
+ $this->assertEqual(false, $test);
+ }
+
+ public function testGetMetastringBasedObjectWithDisabledAnnotation() {
+ $name = 'test_annotation_name' . rand();
+ $value = 'test_annotation_value' . rand();
+ $id = create_annotation($this->object->guid, $name, $value);
+ $annotation = elgg_get_annotation_from_id($id);
+ $this->assertTrue($annotation->disable());
+
+ $test = elgg_get_metastring_based_objects(array(
+ 'metastring_type' => 'annotations',
+ 'guid' => $this->object->guid,
+ ));
+ $this->assertEqual(array(), $test);
+ }
+
+ public function testEnableDisableByID() {
+ $db_prefix = elgg_get_config('dbprefix');
+ $annotations = $this->createAnnotations(1);
+ $metadata = $this->createMetadata(1);
+
+ foreach ($this->metastringTypes as $type) {
+ $id = ${$type}[0];
+ $table = $db_prefix . $type;
+ $q = "SELECT * FROM $table WHERE id = $id";
+ $test = get_data($q);
+
+ // disable
+ $this->assertEqual($test[0]->enabled, 'yes');
+ $this->assertTrue(elgg_set_metastring_based_object_enabled_by_id($id, 'no', $type));
+
+ $test = get_data($q);
+ $this->assertEqual($test[0]->enabled, 'no');
+
+ // enable
+ $ashe = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $this->assertTrue(elgg_set_metastring_based_object_enabled_by_id($id, 'yes', $type));
+
+ $test = get_data($q);
+ $this->assertEqual($test[0]->enabled, 'yes');
+
+ access_show_hidden_entities($ashe);
+ }
+ }
+
+ public function testKeepMeFromDeletingEverything() {
+ foreach ($this->metastringTypes as $type) {
+ $required = array(
+ 'guid', 'guids'
+ );
+
+ switch ($type) {
+ case 'metadata':
+ $metadata_required = array(
+ 'metadata_owner_guid', 'metadata_owner_guids',
+ 'metadata_name', 'metadata_names',
+ 'metadata_value', 'metadata_values'
+ );
+
+ $required = array_merge($required, $metadata_required);
+ break;
+
+ case 'annotations':
+ $annotations_required = array(
+ 'annotation_owner_guid', 'annotation_owner_guids',
+ 'annotation_name', 'annotation_names',
+ 'annotation_value', 'annotation_values'
+ );
+
+ $required = array_merge($required, $annotations_required);
+ break;
+ }
+
+ $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();
+
+ $options[$key] = ELGG_ENTITIES_ANY_VALUE;
+ $this->assertFalse(elgg_is_valid_options_for_batch_operation($options, $type), "Sent $key = ELGG_ENTITIES_ANY_VALUE");
+
+ $options[$key] = ELGG_ENTITIES_NO_VALUE;
+ $this->assertFalse(elgg_is_valid_options_for_batch_operation($options, $type), "Sent $key = ELGG_ENTITIES_NO_VALUE");
+
+ $options[$key] = false;
+ $this->assertFalse(elgg_is_valid_options_for_batch_operation($options, $type), "Sent $key = bool false");
+
+ $options[$key] = true;
+ $this->assertTrue(elgg_is_valid_options_for_batch_operation($options, $type), "Sent $key = bool true");
+
+ $options[$key] = 'test';
+ $this->assertTrue(elgg_is_valid_options_for_batch_operation($options, $type), "Sent $key = 'test'");
+
+ $options[$key] = array('test');
+ $this->assertTrue(elgg_is_valid_options_for_batch_operation($options, $type), "Sent $key = array('test')");
+ }
+ }
+ }
+}
diff --git a/engine/tests/api/output.php b/engine/tests/api/output.php
new file mode 100644
index 000000000..c3d5aa8c6
--- /dev/null
+++ b/engine/tests/api/output.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Test case for ElggAutoP functionality.
+ */
+class ElggCoreOutputAutoPTest extends ElggCoreUnitTest {
+
+ /**
+ * @var ElggAutoP
+ */
+ protected $_autop;
+
+ public function setUp() {
+ $this->_autop = new ElggAutoP();
+ }
+
+ public function testDomRoundtrip() {
+ $d = dir(dirname(dirname(__FILE__)) . '/test_files/output/autop');
+ $in = file_get_contents($d->path . "/domdoc_in.html");
+ $exp = file_get_contents($d->path . "/domdoc_exp.html");
+ $exp = $this->flattenString($exp);
+
+ $doc = new DOMDocument();
+ libxml_use_internal_errors(true);
+ $doc->loadHTML("<html><meta http-equiv='content-type' content='text/html; charset=utf-8'><body>"
+ . $in . '</body></html>');
+ $serialized = $doc->saveHTML();
+ list(,$out) = explode('<body>', $serialized, 2);
+ list($out) = explode('</body>', $out, 2);
+ $out = $this->flattenString($out);
+
+ $this->assertEqual($exp, $out, "DOMDocument's parsing/serialization roundtrip");
+ }
+
+ public function testProcess() {
+ $data = $this->provider();
+ foreach ($data as $row) {
+ list($test, $in, $exp) = $row;
+ $exp = $this->flattenString($exp);
+ $out = $this->_autop->process($in);
+ $out = $this->flattenString($out);
+
+ $this->assertEqual($exp, $out, "Equality case {$test}");
+ }
+ }
+
+ public function provider() {
+ $d = dir(dirname(dirname(__FILE__)) . '/test_files/output/autop');
+ $tests = array();
+ while (false !== ($entry = $d->read())) {
+ if (preg_match('/^([a-z\\-]+)\.in\.html$/i', $entry, $m)) {
+ $tests[] = $m[1];
+ }
+ }
+
+ $data = array();
+ foreach ($tests as $test) {
+ $data[] = array(
+ $test,
+ file_get_contents($d->path . '/' . "{$test}.in.html"),
+ file_get_contents($d->path . '/' . "{$test}.exp.html"),
+ );
+ }
+ return $data;
+ }
+
+ /**
+ * Different versions of PHP return different whitespace between tags.
+ * Removing all line breaks normalizes that.
+ */
+ public function flattenString($string) {
+ $r = preg_replace('/[\n\r]+/', '', $string);
+ return $r;
+ }
+} \ No newline at end of file
diff --git a/engine/tests/api/plugins.php b/engine/tests/api/plugins.php
new file mode 100644
index 000000000..d0f111c48
--- /dev/null
+++ b/engine/tests/api/plugins.php
@@ -0,0 +1,299 @@
+<?php
+/**
+ * Elgg Plugins Test
+ *
+ * @package Elgg.Core
+ * @subpackage Plugins.Test
+ */
+class ElggCorePluginsAPITest extends ElggCoreUnitTest {
+ // 1.8 manifest object
+ var $manifest18;
+
+ // 1.8 package at test_files/plugin_18/
+ var $package18;
+
+ // 1.7 manifest object
+ var $manifest17;
+
+ // 1.7 package at test_files/plugin_17/
+ var $package17;
+
+ public function __construct() {
+ parent::__construct();
+
+ $this->manifest18 = new ElggPluginManifest(get_config('path') . 'engine/tests/test_files/plugin_18/manifest.xml', 'plugin_test_18');
+ $this->manifest17 = new ElggPluginManifest(get_config('path') . 'engine/tests/test_files/plugin_17/manifest.xml', 'plugin_test_17');
+
+ $this->package18 = new ElggPluginPackage(get_config('path') . 'engine/tests/test_files/plugin_18');
+ $this->package17 = new ElggPluginPackage(get_config('path') . 'engine/tests/test_files/plugin_17');
+ }
+
+ /**
+ * Called after each test method.
+ */
+ public function tearDown() {
+ // do not allow SimpleTest to interpret Elgg notices as exceptions
+ $this->swallowErrors();
+ }
+
+ // generic tests
+ public function testElggPluginManifestFromString() {
+ $manifest_file = file_get_contents(get_config('path') . 'engine/tests/test_files/plugin_17/manifest.xml');
+ $manifest = new ElggPluginManifest($manifest_file);
+
+ $this->assertIsA($manifest, 'ElggPluginManifest');
+ }
+
+ public function testElggPluginManifestFromFile() {
+ $file = get_config('path') . 'engine/tests/test_files/plugin_17/manifest.xml';
+ $manifest = new ElggPluginManifest($file);
+
+ $this->assertIsA($manifest, 'ElggPluginManifest');
+ }
+
+ public function testElggPluginManifestFromXMLEntity() {
+ $xml = xml_to_object($manifest_file = file_get_contents(get_config('path') . 'engine/tests/test_files/plugin_17/manifest.xml'));
+ $manifest = new ElggPluginManifest($xml);
+
+ $this->assertIsA($manifest, 'ElggPluginManifest');
+ }
+
+ // exact manifest values
+ // 1.8 interface
+ public function testElggPluginManifest18() {
+ $manifest_array = array(
+ 'name' => 'Test Manifest',
+ 'author' => 'Anyone',
+ 'version' => '1.0',
+ 'blurb' => 'A concise description.',
+ 'description' => 'A longer, more interesting description.',
+ 'website' => 'http://www.elgg.org/',
+ 'repository' => 'https://github.com/Elgg/Elgg',
+ 'bugtracker' => 'https://github.com/elgg/elgg/issues',
+ 'donations' => 'http://elgg.org/supporter.php',
+ 'copyright' => '(C) Elgg Foundation 2011',
+ 'license' => 'GNU General Public License version 2',
+
+ 'requires' => array(
+ array('type' => 'elgg_version', 'version' => '3009030802', 'comparison' => 'lt'),
+ array('type' => 'elgg_release', 'version' => '1.8-svn'),
+ array('type' => 'php_extension', 'name' => 'gd'),
+ array('type' => 'php_ini', 'name' => 'short_open_tag', 'value' => 'off'),
+ array('type' => 'php_extension', 'name' => 'made_up', 'version' => '1.0'),
+ array('type' => 'plugin', 'name' => 'fake_plugin', 'version' => '1.0'),
+ array('type' => 'plugin', 'name' => 'profile', 'version' => '1.0'),
+ array('type' => 'plugin', 'name' => 'profile_api', 'version' => '1.3', 'comparison' => 'lt'),
+ array('type' => 'priority', 'priority' => 'after', 'plugin' => 'profile'),
+ ),
+
+ 'screenshot' => array(
+ array('description' => 'Fun things to do 1', 'path' => 'graphics/plugin_ss1.png'),
+ array('description' => 'Fun things to do 2', 'path' => 'graphics/plugin_ss2.png'),
+ ),
+
+ 'category' => array(
+ 'Admin', 'ServiceAPI'
+ ),
+
+ 'conflicts' => array(
+ array('type' => 'plugin', 'name' => 'profile_api', 'version' => '1.0')
+ ),
+
+ 'provides' => array(
+ array('type' => 'plugin', 'name' => 'profile_api', 'version' => '1.3'),
+ array('type' => 'php_extension', 'name' => 'big_math', 'version' => '1.0')
+ ),
+
+ 'suggests' => array(
+ array('type' => 'plugin', 'name' => 'facebook_connect', 'version' => '1.0'),
+ ),
+
+ // string because we are reading from a file
+ 'activate_on_install' => 'true',
+ );
+
+ $this->assertIdentical($this->manifest18->getManifest(), $manifest_array);
+ }
+
+ public function testElggPluginManifest17() {
+ $manifest_array = array(
+ 'author' => 'Anyone',
+ 'version' => '1.0',
+ 'description' => 'A 1.7-style manifest.',
+ 'website' => 'http://www.elgg.org/',
+ 'copyright' => '(C) Elgg Foundation 2011',
+ 'license' => 'GNU General Public License version 2',
+ 'elgg_version' => '2009030702',
+ 'name' => 'Plugin Test 17',
+ );
+
+ $this->assertIdentical($this->manifest17->getManifest(), $manifest_array);
+ }
+
+
+ public function testElggPluginManifestGetApiVersion() {
+ $this->assertEqual($this->manifest18->getApiVersion(), 1.8);
+ $this->assertEqual($this->manifest17->getApiVersion(), 1.7);
+ }
+
+ public function testElggPluginManifestGetPluginID() {
+ $this->assertEqual($this->manifest18->getPluginID(), 'plugin_test_18');
+ $this->assertEqual($this->manifest17->getPluginID(), 'plugin_test_17');
+ }
+
+
+ // normalized attributes
+ public function testElggPluginManifestGetName() {
+ $this->assertEqual($this->manifest18->getName(), 'Test Manifest');
+ $this->assertEqual($this->manifest17->getName(), 'Plugin Test 17');
+ }
+
+ public function testElggPluginManifestGetAuthor() {
+ $this->assertEqual($this->manifest18->getAuthor(), 'Anyone');
+ $this->assertEqual($this->manifest17->getAuthor(), 'Anyone');
+ }
+
+ public function testElggPluginManifestGetVersion() {
+ $this->assertEqual($this->manifest18->getVersion(), 1.0);
+ $this->assertEqual($this->manifest17->getVersion(), 1.0);
+ }
+
+ public function testElggPluginManifestGetBlurb() {
+ $this->assertEqual($this->manifest18->getBlurb(), 'A concise description.');
+ $this->assertEqual($this->manifest17->getBlurb(), 'A 1.7-style manifest.');
+ }
+
+ public function testElggPluginManifestGetWebsite() {
+ $this->assertEqual($this->manifest18->getWebsite(), 'http://www.elgg.org/');
+ $this->assertEqual($this->manifest17->getWebsite(), 'http://www.elgg.org/');
+ }
+
+ public function testElggPluginManifestGetRepository() {
+ $this->assertEqual($this->manifest18->getRepositoryURL(), 'https://github.com/Elgg/Elgg');
+ $this->assertEqual($this->manifest17->getRepositoryURL(), '');
+ }
+
+ public function testElggPluginManifestGetBugtracker() {
+ $this->assertEqual($this->manifest18->getBugTrackerURL(), 'https://github.com/elgg/elgg/issues');
+ $this->assertEqual($this->manifest17->getBugTrackerURL(), '');
+ }
+
+ public function testElggPluginManifestGetDonationsPage() {
+ $this->assertEqual($this->manifest18->getDonationsPageURL(), 'http://elgg.org/supporter.php');
+ $this->assertEqual($this->manifest17->getDonationsPageURL(), '');
+ }
+
+ public function testElggPluginManifestGetCopyright() {
+ $this->assertEqual($this->manifest18->getCopyright(), '(C) Elgg Foundation 2011');
+ $this->assertEqual($this->manifest18->getCopyright(), '(C) Elgg Foundation 2011');
+ }
+
+ public function testElggPluginManifestGetLicense() {
+ $this->assertEqual($this->manifest18->getLicense(), 'GNU General Public License version 2');
+ $this->assertEqual($this->manifest17->getLicense(), 'GNU General Public License version 2');
+ }
+
+
+ public function testElggPluginManifestGetRequires() {
+ $requires = array(
+ array('type' => 'elgg_version', 'version' => '3009030802', 'comparison' => 'lt'),
+ array('type' => 'elgg_release', 'version' => '1.8-svn', 'comparison' => 'ge'),
+ array('type' => 'php_extension', 'name' => 'gd', 'version' => '', 'comparison' => '='),
+ array('type' => 'php_ini', 'name' => 'short_open_tag', 'value' => 0, 'comparison' => '='),
+ array('type' => 'php_extension', 'name' => 'made_up', 'version' => '1.0', 'comparison' => '='),
+ array('type' => 'plugin', 'name' => 'fake_plugin', 'version' => '1.0', 'comparison' => 'ge'),
+ array('type' => 'plugin', 'name' => 'profile', 'version' => '1.0', 'comparison' => 'ge'),
+ array('type' => 'plugin', 'name' => 'profile_api', 'version' => '1.3', 'comparison' => 'lt'),
+ array('type' => 'priority', 'priority' => 'after', 'plugin' => 'profile'),
+ );
+
+ $this->assertIdentical($this->package18->getManifest()->getRequires(), $requires);
+
+ $requires = array(
+ array('type' => 'elgg_version', 'version' => '2009030702', 'comparison' => 'ge')
+ );
+
+ $this->assertIdentical($this->package17->getManifest()->getRequires(), $requires);
+ }
+
+ public function testElggPluginManifestGetSuggests() {
+ $suggests = array(
+ array('type' => 'plugin', 'name' => 'facebook_connect', 'version' => '1.0', 'comparison' => 'ge'),
+ );
+
+ $this->assertIdentical($this->package18->getManifest()->getSuggests(), $suggests);
+
+ $suggests = array();
+
+ $this->assertIdentical($this->package17->getManifest()->getSuggests(), $suggests);
+ }
+
+ public function testElggPluginManifestGetDescription() {
+ $this->assertEqual($this->package18->getManifest()->getDescription(), 'A longer, more interesting description.');
+ $this->assertEqual($this->package17->getManifest()->getDescription(), 'A 1.7-style manifest.');
+ }
+
+ public function testElggPluginManifestGetCategories() {
+ $categories = array(
+ 'Admin', 'ServiceAPI'
+ );
+
+ $this->assertIdentical($this->package18->getManifest()->getCategories(), $categories);
+ $this->assertIdentical($this->package17->getManifest()->getCategories(), array());
+ }
+
+ public function testElggPluginManifestGetScreenshots() {
+ $screenshots = array(
+ array('description' => 'Fun things to do 1', 'path' => 'graphics/plugin_ss1.png'),
+ array('description' => 'Fun things to do 2', 'path' => 'graphics/plugin_ss2.png'),
+ );
+
+ $this->assertIdentical($this->package18->getManifest()->getScreenshots(), $screenshots);
+ $this->assertIdentical($this->package17->getManifest()->getScreenshots(), array());
+ }
+
+ public function testElggPluginManifestGetProvides() {
+ $provides = array(
+ array('type' => 'plugin', 'name' => 'profile_api', 'version' => '1.3'),
+ array('type' => 'php_extension', 'name' => 'big_math', 'version' => '1.0'),
+ array('type' => 'plugin', 'name' => 'plugin_18', 'version' => '1.0')
+ );
+
+ $this->assertIdentical($this->package18->getManifest()->getProvides(), $provides);
+
+
+ $provides = array(
+ array('type' => 'plugin', 'name' => 'plugin_17', 'version' => '1.0')
+ );
+
+ $this->assertIdentical($this->package17->getManifest()->getProvides(), $provides);
+ }
+
+ public function testElggPluginManifestGetConflicts() {
+ $conflicts = array(
+ array(
+ 'type' => 'plugin',
+ 'name' => 'profile_api',
+ 'version' => '1.0',
+ 'comparison' => '='
+ )
+ );
+
+ $this->assertIdentical($this->manifest18->getConflicts(), $conflicts);
+ $this->assertIdentical($this->manifest17->getConflicts(), array());
+ }
+
+ public function testElggPluginManifestGetActivateOnInstall() {
+ $this->assertIdentical($this->manifest18->getActivateOnInstall(), true);
+ }
+
+ // ElggPluginPackage
+ public function testElggPluginPackageDetectIDFromPath() {
+ $this->assertEqual($this->package18->getID(), 'plugin_18');
+ }
+
+ public function testElggPluginPackageDetectIDFromPluginID() {
+ $package = new ElggPluginPackage('profile');
+ $this->assertEqual($package->getID(), 'profile');
+ }
+}
diff --git a/engine/tests/api/river.php b/engine/tests/api/river.php
new file mode 100644
index 000000000..6931b9f41
--- /dev/null
+++ b/engine/tests/api/river.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Elgg Test river api
+ *
+ * @package Elgg
+ * @subpackage Test
+ */
+class ElggCoreRiverAPITest extends ElggCoreUnitTest {
+
+ public function testElggTypeSubtypeWhereSQL() {
+ $types = array('object');
+ $subtypes = array('blog');
+ $result = elgg_get_river_type_subtype_where_sql('rv', $types, $subtypes, null);
+ $this->assertIdentical($result, "((rv.type = 'object') AND ((rv.subtype = 'blog')))");
+
+ $types = array('object');
+ $subtypes = array('blog', 'file');
+ $result = elgg_get_river_type_subtype_where_sql('rv', $types, $subtypes, null);
+ $this->assertIdentical($result, "((rv.type = 'object') AND ((rv.subtype = 'blog') OR (rv.subtype = 'file')))");
+ }
+}
diff --git a/engine/tests/objects/entities.php b/engine/tests/objects/entities.php
index eb6eee2a3..bac72079e 100644
--- a/engine/tests/objects/entities.php
+++ b/engine/tests/objects/entities.php
@@ -4,8 +4,6 @@
*
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
class ElggCoreEntityTest extends ElggCoreUnitTest {
/**
@@ -28,21 +26,25 @@ class ElggCoreEntityTest extends ElggCoreUnitTest {
*/
public function testElggEntityAttributes() {
$test_attributes = array();
- $test_attributes['guid'] = '';
- $test_attributes['type'] = '';
- $test_attributes['subtype'] = '';
- $test_attributes['owner_guid'] = get_loggedin_userid();
- $test_attributes['container_guid'] = get_loggedin_userid();
- $test_attributes['site_guid'] = 0;
+ $test_attributes['guid'] = NULL;
+ $test_attributes['type'] = NULL;
+ $test_attributes['subtype'] = NULL;
+ $test_attributes['owner_guid'] = elgg_get_logged_in_user_guid();
+ $test_attributes['container_guid'] = elgg_get_logged_in_user_guid();
+ $test_attributes['site_guid'] = NULL;
$test_attributes['access_id'] = ACCESS_PRIVATE;
- $test_attributes['time_created'] = '';
- $test_attributes['time_updated'] = '';
- $test_attributes['last_action'] = '';
+ $test_attributes['time_created'] = NULL;
+ $test_attributes['time_updated'] = NULL;
+ $test_attributes['last_action'] = NULL;
$test_attributes['enabled'] = 'yes';
$test_attributes['tables_split'] = 1;
$test_attributes['tables_loaded'] = 0;
+ ksort($test_attributes);
- $this->assertIdentical($this->entity->expose_attributes(), $test_attributes);
+ $entity_attributes = $this->entity->expose_attributes();
+ ksort($entity_attributes);
+
+ $this->assertIdentical($entity_attributes, $test_attributes);
}
public function testElggEntityGetAndSetBaseAttributes() {
@@ -75,7 +77,7 @@ class ElggCoreEntityTest extends ElggCoreUnitTest {
$this->assertIdentical($this->entity->getGUID(), $this->entity->guid );
$this->assertIdentical($this->entity->getType(), $this->entity->type );
$this->assertIdentical($this->entity->getSubtype(), $this->entity->subtype );
- $this->assertIdentical($this->entity->getOwner(), $this->entity->owner_guid );
+ $this->assertIdentical($this->entity->getOwnerGUID(), $this->entity->owner_guid );
$this->assertIdentical($this->entity->getAccessID(), $this->entity->access_id );
$this->assertIdentical($this->entity->getTimeCreated(), $this->entity->time_created );
$this->assertIdentical($this->entity->getTimeUpdated(), $this->entity->time_updated );
@@ -87,25 +89,25 @@ class ElggCoreEntityTest extends ElggCoreUnitTest {
$this->assertFalse(isset($this->entity->non_existent));
// create metadata
- $this->assertTrue($this->entity->non_existent = 'testing');
+ $this->entity->existent = 'testing';
+ $this->assertIdentical($this->entity->existent, 'testing');
// check metadata set
- $this->assertTrue(isset($this->entity->non_existent));
- $this->assertIdentical($this->entity->non_existent, 'testing');
- $this->assertIdentical($this->entity->getMetaData('non_existent'), 'testing');
+ $this->assertTrue(isset($this->entity->existent));
+ $this->assertIdentical($this->entity->getMetaData('existent'), 'testing');
// check internal metadata array
$metadata = $this->entity->expose_metadata();
- $this->assertIdentical($metadata['non_existent'], 'testing');
+ $this->assertIdentical($metadata['existent'], array('testing'));
}
public function testElggEnityGetAndSetAnnotations() {
$this->assertFalse(array_key_exists('non_existent', $this->entity->expose_annotations()));
- $this->assertFalse($this->entity->getAnnotations('non_existent'));
+ $this->assertIdentical($this->entity->getAnnotations('non_existent'), array());
// set and check temp annotation
$this->assertTrue($this->entity->annotate('non_existent', 'testing'));
- $this->assertIdentical($this->entity->getAnnotations('non_existent'), 'testing');
+ $this->assertIdentical($this->entity->getAnnotations('non_existent'), array('testing'));
$this->assertTrue(array_key_exists('non_existent', $this->entity->expose_annotations()));
// save entity and check for annotation
@@ -116,30 +118,27 @@ class ElggCoreEntityTest extends ElggCoreUnitTest {
$this->assertIsA($annotations[0], 'ElggAnnotation');
$this->assertIdentical($annotations[0]->name, 'non_existent');
$this->assertEqual($this->entity->countAnnotations('non_existent'), 1);
-
- $this->assertIdentical($annotations, get_annotations($this->entity->getGUID()));
- $this->assertIdentical($annotations, get_annotations($this->entity->getGUID(), 'site'));
- $this->assertIdentical($annotations, get_annotations($this->entity->getGUID(), 'site', 'testing'));
- $this->assertIdentical(FALSE, get_annotations($this->entity->getGUID(), 'site', 'fail'));
+
+ $this->assertIdentical($annotations, elgg_get_annotations(array('guid' => $this->entity->getGUID())));
+ $this->assertIdentical($annotations, elgg_get_annotations(array('guid' => $this->entity->getGUID(), 'type' => 'site')));
+ $this->assertIdentical($annotations, elgg_get_annotations(array('guid' => $this->entity->getGUID(), 'type' => 'site', 'subtype' => 'testing')));
+ $this->assertIdentical(FALSE, elgg_get_annotations(array('guid' => $this->entity->getGUID(), 'type' => 'site', 'subtype' => 'fail')));
// clear annotation
- $this->assertTrue($this->entity->clearAnnotations());
+ $this->assertTrue($this->entity->deleteAnnotations());
$this->assertEqual($this->entity->countAnnotations('non_existent'), 0);
-
- $this->assertIdentical(FALSE, get_annotations($this->entity->getGUID()));
- $this->assertIdentical(FALSE, get_annotations($this->entity->getGUID(), 'site'));
- $this->assertIdentical(FALSE, get_annotations($this->entity->getGUID(), 'site', 'testing'));
+
+ $this->assertIdentical(array(), elgg_get_annotations(array('guid' => $this->entity->getGUID())));
+ $this->assertIdentical(array(), elgg_get_annotations(array('guid' => $this->entity->getGUID(), 'type' => 'site')));
+ $this->assertIdentical(array(), elgg_get_annotations(array('guid' => $this->entity->getGUID(), 'type' => 'site', 'subtype' => 'testing')));
// clean up
$this->assertTrue($this->entity->delete());
+ remove_subtype('site', 'testing');
}
public function testElggEntityCache() {
global $ENTITY_CACHE;
- $ENTITY_CACHE = NULL;
-
- $this->assertNull($ENTITY_CACHE);
- initialise_entity_cache();
$this->assertIsA($ENTITY_CACHE, 'array');
}
@@ -179,7 +178,7 @@ class ElggCoreEntityTest extends ElggCoreUnitTest {
$this->AssertEqual($this->entity->get('non_existent'), 'testing');
// clean up with delete
- $this->assertTrue($this->entity->delete());
+ $this->assertIdentical(true, $this->entity->delete());
}
public function testElggEntityDisableAndEnable() {
@@ -188,19 +187,104 @@ class ElggCoreEntityTest extends ElggCoreUnitTest {
// ensure enabled
$this->assertTrue($this->entity->isEnabled());
- // false on disable
+ // false on disable because it's not saved yet.
$this->assertFalse($this->entity->disable());
// save and disable
$this->save_entity();
+
+ // add annotations and metadata to check if they're disabled.
+ $annotation_id = create_annotation($this->entity->guid, 'test_annotation_' . rand(), 'test_value_' . rand());
+ $metadata_id = create_metadata($this->entity->guid, 'test_metadata_' . rand(), 'test_value_' . rand());
+
$this->assertTrue($this->entity->disable());
// ensure disabled by comparing directly with database
$entity = get_data_row("SELECT * FROM {$CONFIG->dbprefix}entities WHERE guid = '{$this->entity->guid}'");
$this->assertIdentical($entity->enabled, 'no');
+ $annotation = get_data_row("SELECT * FROM {$CONFIG->dbprefix}annotations WHERE id = '$annotation_id'");
+ $this->assertIdentical($annotation->enabled, 'no');
+
+ $metadata = get_data_row("SELECT * FROM {$CONFIG->dbprefix}metadata WHERE id = '$metadata_id'");
+ $this->assertIdentical($metadata->enabled, 'no');
+
// re-enable for deletion to work
$this->assertTrue($this->entity->enable());
+
+ // check enabled
+ // check annotations and metadata enabled.
+ $entity = get_data_row("SELECT * FROM {$CONFIG->dbprefix}entities WHERE guid = '{$this->entity->guid}'");
+ $this->assertIdentical($entity->enabled, 'yes');
+
+ $annotation = get_data_row("SELECT * FROM {$CONFIG->dbprefix}annotations WHERE id = '$annotation_id'");
+ $this->assertIdentical($annotation->enabled, 'yes');
+
+ $metadata = get_data_row("SELECT * FROM {$CONFIG->dbprefix}metadata WHERE id = '$metadata_id'");
+ $this->assertIdentical($metadata->enabled, 'yes');
+
+ $this->assertTrue($this->entity->delete());
+ }
+
+ public function testElggEntityRecursiveDisableAndEnable() {
+ global $CONFIG;
+
+ $this->save_entity();
+ $obj1 = new ElggObject();
+ $obj1->container_guid = $this->entity->getGUID();
+ $obj1->save();
+ $obj2 = new ElggObject();
+ $obj2->container_guid = $this->entity->getGUID();
+ $obj2->save();
+
+ // disable $obj2 before disabling the container
+ $this->assertTrue($obj2->disable());
+
+ // disable entities container by $this->entity
+ $this->assertTrue($this->entity->disable());
+ $entity = get_data_row("SELECT * FROM {$CONFIG->dbprefix}entities WHERE guid = '{$obj1->guid}'");
+ $this->assertIdentical($entity->enabled, 'no');
+
+ // enable entities that were disabled with the container (but not $obj2)
+ $this->assertTrue($this->entity->enable());
+ $entity = get_data_row("SELECT * FROM {$CONFIG->dbprefix}entities WHERE guid = '{$obj1->guid}'");
+ $this->assertIdentical($entity->enabled, 'yes');
+ $entity = get_data_row("SELECT * FROM {$CONFIG->dbprefix}entities WHERE guid = '{$obj2->guid}'");
+ $this->assertIdentical($entity->enabled, 'no');
+
+ // cleanup
+ $this->assertTrue($obj2->enable());
+ $this->assertTrue($obj2->delete());
+ $this->assertTrue($obj1->delete());
+ $this->assertTrue($this->entity->delete());
+ }
+
+ public function testElggEntityMetadata() {
+ // let's delete a non-existent metadata
+ $this->assertFalse($this->entity->deleteMetadata('important'));
+
+ // let's add the metadata
+ $this->entity->important = 'indeed!';
+ $this->assertIdentical('indeed!', $this->entity->important);
+ $this->entity->less_important = 'true, too!';
+ $this->assertIdentical('true, too!', $this->entity->less_important);
+ $this->save_entity();
+
+ // test deleting incorrectly
+ // @link https://github.com/elgg/elgg/issues/2273
+ $this->assertNull($this->entity->deleteMetadata('impotent'));
+ $this->assertEqual($this->entity->important, 'indeed!');
+
+ // get rid of one metadata
+ $this->assertEqual($this->entity->important, 'indeed!');
+ $this->assertTrue($this->entity->deleteMetadata('important'));
+ $this->assertNull($this->entity->important);
+
+ // get rid of all metadata
+ $this->assertTrue($this->entity->deleteMetadata());
+ $this->assertNull($this->entity->less_important);
+
+ // clean up database
$this->assertTrue($this->entity->delete());
}
@@ -219,8 +303,101 @@ class ElggCoreEntityTest extends ElggCoreUnitTest {
$this->assertIdentical($exportables, $this->entity->getExportableValues());
}
- protected function save_entity($type='site')
- {
+ public function testElggEntityMultipleMetadata() {
+ foreach (array(false, true) as $save) {
+ if ($save) {
+ $this->save_entity();
+ }
+ $md = array('brett', 'bryan', 'brad');
+ $name = 'test_md_' . rand();
+
+ $this->entity->$name = $md;
+
+ $this->assertEqual($md, $this->entity->$name);
+
+ if ($save) {
+ $this->assertTrue($this->entity->delete());
+ }
+ }
+ }
+
+ public function testElggEntitySingleElementArrayMetadata() {
+ foreach (array(false, true) as $save) {
+ if ($save) {
+ $this->save_entity();
+ }
+ $md = array('test');
+ $name = 'test_md_' . rand();
+
+ $this->entity->$name = $md;
+
+ $this->assertEqual($md[0], $this->entity->$name);
+
+ if ($save) {
+ $this->assertTrue($this->entity->delete());
+ }
+ }
+ }
+
+ public function testElggEntityAppendMetadata() {
+ foreach (array(false, true) as $save) {
+ if ($save) {
+ $this->save_entity();
+ }
+ $md = 'test';
+ $name = 'test_md_' . rand();
+
+ $this->entity->$name = $md;
+ $this->entity->setMetaData($name, 'test2', '', true);
+
+ $this->assertEqual(array('test', 'test2'), $this->entity->$name);
+
+ if ($save) {
+ $this->assertTrue($this->entity->delete());
+ }
+ }
+ }
+
+ public function testElggEntitySingleElementArrayAppendMetadata() {
+ foreach (array(false, true) as $save) {
+ if ($save) {
+ $this->save_entity();
+ }
+ $md = 'test';
+ $name = 'test_md_' . rand();
+
+ $this->entity->$name = $md;
+ $this->entity->setMetaData($name, array('test2'), '', true);
+
+ $this->assertEqual(array('test', 'test2'), $this->entity->$name);
+
+ if ($save) {
+ $this->assertTrue($this->entity->delete());
+ }
+ }
+ }
+
+ public function testElggEntityArrayAppendMetadata() {
+ foreach (array(false, true) as $save) {
+ if ($save) {
+ $this->save_entity();
+ }
+ $md = array('brett', 'bryan', 'brad');
+ $md2 = array('test1', 'test2', 'test3');
+ $name = 'test_md_' . rand();
+
+ $this->entity->$name = $md;
+ $this->entity->setMetaData($name, $md2, '', true);
+
+ $this->assertEqual(array_merge($md, $md2), $this->entity->$name);
+
+ if ($save) {
+ $this->assertTrue($this->entity->delete());
+ }
+ }
+ }
+
+ protected function save_entity($type='site') {
$this->entity->type = $type;
$this->assertNotEqual($this->entity->save(), 0);
}
@@ -229,7 +406,7 @@ class ElggCoreEntityTest extends ElggCoreUnitTest {
// ElggEntity is an abstract class with no abstact methods.
class ElggEntityTest extends ElggEntity {
public function __construct() {
- $this->initialise_attributes();
+ $this->initializeAttributes();
}
public function expose_attributes() {
diff --git a/engine/tests/objects/filestore.php b/engine/tests/objects/filestore.php
index e20137187..9732f0af4 100644
--- a/engine/tests/objects/filestore.php
+++ b/engine/tests/objects/filestore.php
@@ -4,8 +4,6 @@
*
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
class ElggCoreFilestoreTest extends ElggCoreUnitTest {
@@ -50,12 +48,8 @@ class ElggCoreFilestoreTest extends ElggCoreUnitTest {
$user = $this->createTestUser();
$created = date('Y/m/d', $user->time_created);
- // check matrix with username
- $user_dir = $this->filestore->make_file_matrix($user->username);
- $this->assertIdentical($user_dir, "$created/$user->guid/");
-
// check matrix with guid
- $guid_dir = $this->filestore->make_file_matrix($user->guid);
+ $guid_dir = $this->filestore->makeFileMatrix($user->guid);
$this->assertIdentical($guid_dir, "$created/$user->guid/");
// clean up user
@@ -100,11 +94,7 @@ class ElggCoreFilestoreTest extends ElggCoreUnitTest {
}
class ElggDiskFilestoreTest extends ElggDiskFilestore {
- public function make_file_matrix($filename) {
- return parent::make_file_matrix($filename);
- }
-
- public function user_file_matrix($guid) {
- return parent::user_file_matrix($guid);
+ public function makeFileMatrix($guid) {
+ return parent::makeFileMatrix($guid);
}
}
diff --git a/engine/tests/objects/metadata.php b/engine/tests/objects/metadata.php
deleted file mode 100644
index bc6523279..000000000
--- a/engine/tests/objects/metadata.php
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-/**
- * Elgg Test ElggMetadata
- *
- * @package Elgg
- * @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
- */
-class ElggCoreMetadataTest extends ElggCoreUnitTest {
- protected $metastrings;
-
- /**
- * Called before each test method.
- */
- public function setUp() {
- $this->metastrings = array();
- $this->object = new ElggObject();
- }
-
- /**
- * Called after each test method.
- */
- public function tearDown() {
- // do not allow SimpleTest to interpret Elgg notices as exceptions
- $this->swallowErrors();
-
- unset($this->object);
- }
-
- public function testGetMetastringById() {
- foreach (array('metaUnitTest', 'metaunittest', 'METAUNITTEST') as $string) {
- $this->create_metastring($string);
- }
-
- // lookup metastring id
- $cs_ids = get_metastring_id('metaUnitTest', TRUE);
- $this->assertEqual($cs_ids, $this->metastrings['metaUnitTest']);
-
- // lookup all metastrings, ignoring case
- $cs_ids = get_metastring_id('metaUnitTest', FALSE);
- $this->assertEqual(count($cs_ids), 3);
- $this->assertEqual(count($cs_ids), count($this->metastrings));
- foreach ($cs_ids as $string )
- {
- $this->assertTrue(in_array($string, $this->metastrings));
- }
-
- // clean up
- $this->delete_metastrings();
- }
-
- public function testElggGetEntitiesFromMetadata() {
- global $CONFIG, $METASTRINGS_CACHE, $METASTRINGS_DEADNAME_CACHE;
- $METASTRINGS_CACHE = $METASTRINGS_DEADNAME_CACHE = array();
-
- $this->object->title = 'Meta Unit Test';
- $this->object->save();
- $this->create_metastring('metaUnitTest');
- $this->create_metastring('tested');
-
- $this->assertTrue(create_metadata($this->object->guid, 'metaUnitTest', 'tested'));
-
- // check value with improper case
- $options = array('metadata_names' => 'metaUnitTest', 'metadata_values' => 'Tested', 'limit' => 10, 'metadata_case_sensitive' => TRUE);
- $this->assertFalse(elgg_get_entities_from_metadata($options));
-
- // compare forced case with ignored case
- $options = array('metadata_names' => 'metaUnitTest', 'metadata_values' => 'tested', 'limit' => 10, 'metadata_case_sensitive' => TRUE);
- $case_true = elgg_get_entities_from_metadata($options);
- $this->assertIsA($case_true, 'array');
-
- $options = array('metadata_names' => 'metaUnitTest', 'metadata_values' => 'Tested', 'limit' => 10, 'metadata_case_sensitive' => FALSE);
- $case_false = elgg_get_entities_from_metadata($options);
- $this->assertIsA($case_false, 'array');
-
- $this->assertIdentical($case_true, $case_false);
-
- // check deprecated get_entities_from_metadata() function
- $deprecated = get_entities_from_metadata('metaUnitTest', 'tested', '', '', 0, 10, 0, '', 0, FALSE, TRUE);
- $this->assertIdentical($deprecated, $case_true);
-
- // check entity list
- //$this->dump(list_entities_from_metadata('metaUnitTest', 'Tested', '', '', 0, 10, TRUE, TRUE, TRUE, FALSE));
-
- // clean up
- $this->delete_metastrings();
- $this->object->delete();
- }
-
-
- protected function create_metastring($string) {
- global $CONFIG, $METASTRINGS_CACHE, $METASTRINGS_DEADNAME_CACHE;
- $METASTRINGS_CACHE = $METASTRINGS_DEADNAME_CACHE = array();
-
- mysql_query("INSERT INTO {$CONFIG->dbprefix}metastrings (string) VALUES ('$string')");
- $this->metastrings[$string] = mysql_insert_id();
- }
-
- protected function delete_metastrings() {
- global $CONFIG, $METASTRINGS_CACHE, $METASTRINGS_DEADNAME_CACHE;
- $METASTRINGS_CACHE = $METASTRINGS_DEADNAME_CACHE = array();
-
- $strings = implode(', ', $this->metastrings);
- mysql_query("DELETE FROM {$CONFIG->dbprefix}metastrings WHERE id IN ($strings)");
- }
-}
diff --git a/engine/tests/objects/objects.php b/engine/tests/objects/objects.php
index 171566661..263ab2414 100644
--- a/engine/tests/objects/objects.php
+++ b/engine/tests/objects/objects.php
@@ -4,8 +4,6 @@
*
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
class ElggCoreObjectTest extends ElggCoreUnitTest {
@@ -40,23 +38,27 @@ class ElggCoreObjectTest extends ElggCoreUnitTest {
public function testElggObjectConstructor() {
$attributes = array();
- $attributes['guid'] = '';
+ $attributes['guid'] = NULL;
$attributes['type'] = 'object';
- $attributes['subtype'] = '';
- $attributes['owner_guid'] = get_loggedin_userid();
- $attributes['container_guid'] = get_loggedin_userid();
- $attributes['site_guid'] = 0;
+ $attributes['subtype'] = NULL;
+ $attributes['owner_guid'] = elgg_get_logged_in_user_guid();
+ $attributes['container_guid'] = elgg_get_logged_in_user_guid();
+ $attributes['site_guid'] = NULL;
$attributes['access_id'] = ACCESS_PRIVATE;
- $attributes['time_created'] = '';
- $attributes['time_updated'] = '';
- $attributes['last_action'] = '';
+ $attributes['time_created'] = NULL;
+ $attributes['time_updated'] = NULL;
+ $attributes['last_action'] = NULL;
$attributes['enabled'] = 'yes';
$attributes['tables_split'] = 2;
$attributes['tables_loaded'] = 0;
- $attributes['title'] = '';
- $attributes['description'] = '';
+ $attributes['title'] = NULL;
+ $attributes['description'] = NULL;
+ ksort($attributes);
- $this->assertIdentical($this->entity->expose_attributes(), $attributes);
+ $entity_attributes = $this->entity->expose_attributes();
+ ksort($entity_attributes);
+
+ $this->assertIdentical($entity_attributes, $attributes);
}
public function testElggObjectSave() {
@@ -85,11 +87,11 @@ class ElggCoreObjectTest extends ElggCoreUnitTest {
public function testElggObjectLoad() {
// fail on wrong type
try {
- $error = new ElggObjectTest(get_loggedin_userid());
+ $error = new ElggObjectTest(elgg_get_logged_in_user_guid());
$this->assertTrue(FALSE);
} catch (Exception $e) {
$this->assertIsA($e, 'InvalidClassException');
- $message = sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), get_loggedin_userid(), 'ElggObject');
+ $message = sprintf(elgg_echo('InvalidClassException:NotValidElggStar'), elgg_get_logged_in_user_guid(), 'ElggObject');
$this->assertIdentical($e->getMessage(), $message);
}
}
@@ -142,18 +144,15 @@ class ElggCoreObjectTest extends ElggCoreUnitTest {
}
public function testElggObjectContainer() {
- $this->assertEqual($this->entity->getContainer(), get_loggedin_userid());
-
- // fals when container not a group
- $this->assertFalse($this->entity->getContainerEntity());
+ $this->assertEqual($this->entity->getContainerGUID(), elgg_get_logged_in_user_guid());
// create and save to group
$group = new ElggGroup();
$guid = $group->save();
- $this->assertTrue($this->entity->setContainer($guid));
+ $this->assertTrue($this->entity->setContainerGUID($guid));
// check container
- $this->assertEqual($this->entity->getContainer(), $guid);
+ $this->assertEqual($this->entity->getContainerGUID(), $guid);
$this->assertIdentical($group, $this->entity->getContainerEntity());
// clean up
@@ -195,7 +194,99 @@ class ElggCoreObjectTest extends ElggCoreUnitTest {
$old = elgg_set_ignore_access(true);
}
+ // see https://github.com/elgg/elgg/issues/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/objects/sites.php b/engine/tests/objects/sites.php
index 5e23c99ca..a01a661e3 100644
--- a/engine/tests/objects/sites.php
+++ b/engine/tests/objects/sites.php
@@ -4,8 +4,6 @@
*
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
class ElggCoreSiteTest extends ElggCoreUnitTest {
@@ -20,7 +18,7 @@ class ElggCoreSiteTest extends ElggCoreUnitTest {
* Called before each test method.
*/
public function setUp() {
- $this->site = new ElggSiteTest;
+ $this->site = new ElggSiteTest();
}
/**
@@ -38,34 +36,37 @@ class ElggCoreSiteTest extends ElggCoreUnitTest {
parent::__destruct();
}
- /**
- * A basic test that will be called and fail.
- */
public function testElggSiteConstructor() {
$attributes = array();
- $attributes['guid'] = '';
+ $attributes['guid'] = NULL;
$attributes['type'] = 'site';
- $attributes['subtype'] = '';
- $attributes['owner_guid'] = get_loggedin_userid();
- $attributes['container_guid'] = get_loggedin_userid();
- $attributes['site_guid'] = 0;
+ $attributes['subtype'] = NULL;
+ $attributes['owner_guid'] = elgg_get_logged_in_user_guid();
+ $attributes['container_guid'] = elgg_get_logged_in_user_guid();
+ $attributes['site_guid'] = NULL;
$attributes['access_id'] = ACCESS_PRIVATE;
- $attributes['time_created'] = '';
- $attributes['time_updated'] = '';
- $attributes['last_action'] = '';
+ $attributes['time_created'] = NULL;
+ $attributes['time_updated'] = NULL;
+ $attributes['last_action'] = NULL;
$attributes['enabled'] = 'yes';
$attributes['tables_split'] = 2;
$attributes['tables_loaded'] = 0;
- $attributes['name'] = '';
- $attributes['description'] = '';
- $attributes['url'] = '';
+ $attributes['name'] = NULL;
+ $attributes['description'] = NULL;
+ $attributes['url'] = NULL;
+ ksort($attributes);
+
+ $entity_attributes = $this->site->expose_attributes();
+ ksort($entity_attributes);
- $this->assertIdentical($this->site->expose_attributes(), $attributes);
+ $this->assertIdentical($entity_attributes, $attributes);
}
public function testElggSiteSaveAndDelete() {
- $this->assertTrue($this->site->save());
- $this->assertTrue($this->site->delete());
+ $guid = $this->site->save();
+ $this->assertIsA($guid, 'int');
+ $this->assertTrue($guid > 0);
+ $this->assertIdentical(true, $this->site->delete());
}
}
diff --git a/engine/tests/objects/users.php b/engine/tests/objects/users.php
index 81f02af5b..8a1033ac4 100644
--- a/engine/tests/objects/users.php
+++ b/engine/tests/objects/users.php
@@ -4,8 +4,6 @@
*
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
class ElggCoreUserTest extends ElggCoreUnitTest {
@@ -43,35 +41,39 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
parent::__destruct();
}
- /**
- * A basic test that will be called and fail.
- */
public function testElggUserConstructor() {
$attributes = array();
- $attributes['guid'] = '';
+ $attributes['guid'] = NULL;
$attributes['type'] = 'user';
- $attributes['subtype'] = '';
- $attributes['owner_guid'] = get_loggedin_userid();
- $attributes['container_guid'] = get_loggedin_userid();
- $attributes['site_guid'] = 0;
+ $attributes['subtype'] = NULL;
+ $attributes['owner_guid'] = elgg_get_logged_in_user_guid();
+ $attributes['container_guid'] = elgg_get_logged_in_user_guid();
+ $attributes['site_guid'] = NULL;
$attributes['access_id'] = ACCESS_PRIVATE;
- $attributes['time_created'] = '';
- $attributes['time_updated'] = '';
- $attributes['last_action'] = '';
+ $attributes['time_created'] = NULL;
+ $attributes['time_updated'] = NULL;
+ $attributes['last_action'] = NULL;
$attributes['enabled'] = 'yes';
$attributes['tables_split'] = 2;
$attributes['tables_loaded'] = 0;
- $attributes['name'] = '';
- $attributes['username'] = '';
- $attributes['password'] = '';
- $attributes['salt'] = '';
- $attributes['email'] = '';
- $attributes['language'] = '';
- $attributes['code'] = '';
+ $attributes['name'] = NULL;
+ $attributes['username'] = NULL;
+ $attributes['password'] = NULL;
+ $attributes['salt'] = NULL;
+ $attributes['email'] = NULL;
+ $attributes['language'] = NULL;
+ $attributes['code'] = NULL;
$attributes['banned'] = 'no';
$attributes['admin'] = 'no';
+ $attributes['prev_last_action'] = NULL;
+ $attributes['last_login'] = NULL;
+ $attributes['prev_last_login'] = NULL;
+ ksort($attributes);
+
+ $entity_attributes = $this->user->expose_attributes();
+ ksort($entity_attributes);
- $this->assertIdentical($this->user->expose_attributes(), $attributes);
+ $this->assertIdentical($entity_attributes, $attributes);
}
public function testElggUserLoad() {
@@ -96,7 +98,7 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
}
public function testElggUserConstructorByGuid() {
- $user = new ElggUser(get_loggedin_userid());
+ $user = new ElggUser(elgg_get_logged_in_user_guid());
$this->assertIdentical($user, $_SESSION['user']);
// fail with garbage
@@ -111,13 +113,13 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
}
public function testElggUserConstructorByDbRow() {
- $row = $this->fetchUser(get_loggedin_userid());
+ $row = $this->fetchUser(elgg_get_logged_in_user_guid());
$user = new ElggUser($row);
$this->assertIdentical($user, $_SESSION['user']);
}
public function testElggUserConstructorByUsername() {
- $row = $this->fetchUser(get_loggedin_userid());
+ $row = $this->fetchUser(elgg_get_logged_in_user_guid());
$user = new ElggUser($row->username);
$this->assertIdentical($user, $_SESSION['user']);
}
@@ -136,14 +138,14 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
$guid = $this->user->save();
// delete object
- $this->assertTrue($this->user->delete());
+ $this->assertIdentical(true, $this->user->delete());
// check GUID not in database
$this->assertFalse($this->fetchUser($guid));
}
public function testElggUserNameCache() {
- // Trac #1305
+ // issue https://github.com/elgg/elgg/issues/1305
// very unlikely a user would have this username
$name = (string)time();
@@ -157,6 +159,22 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
$this->assertFalse($user);
}
+ public function testGetUserByUsernameAcceptsUrlEncoded() {
+ $username = (string)time();
+ $this->user->username = $username;
+ $guid = $this->user->save();
+
+ // percent encode first letter
+ $first_letter = $username[0];
+ $first_letter = str_pad('%' . dechex(ord($first_letter)), 2, '0', STR_PAD_LEFT);
+ $username = $first_letter . substr($username, 1);
+
+ $user = get_user_by_username($username);
+ $this->assertTrue((bool) $user);
+ $this->assertEqual($guid, $user->guid);
+
+ $this->user->delete();
+ }
public function testElggUserMakeAdmin() {
global $CONFIG;
@@ -218,29 +236,6 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
$this->user->delete();
}
- // remove in 1.9
- public function testElggUserIsAdminLegacy() {
- $this->user->save();
- $this->user->makeAdmin();
-
- $this->assertTrue($this->user->admin);
- $this->assertTrue($this->user->siteadmin);
-
- $this->user->removeAdmin();
- $this->user->delete();
- }
-
- public function testElggUserIsNotAdminLegacy() {
- $this->user->save();
- $this->user->removeAdmin();
-
- $this->assertFalse($this->user->admin);
- $this->assertFalse($this->user->siteadmin);
-
- $this->user->removeAdmin();
- $this->user->delete();
- }
-
protected function fetchUser($guid) {
global $CONFIG;
diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php
index 01900b849..689275661 100644
--- a/engine/tests/regression/trac_bugs.php
+++ b/engine/tests/regression/trac_bugs.php
@@ -1,12 +1,10 @@
<?php
/**
- * Elgg Regression Tests -- Trac Bugfixes
- * Any bugfixes from Trac that require testing belong here.
+ * Elgg Regression Tests -- GitHub Bugfixes
+ * Any bugfixes from GitHub that require testing belong here.
*
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
class ElggCoreRegressionBugsTest extends ElggCoreUnitTest {
@@ -16,7 +14,7 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest {
public function __construct() {
$this->ia = elgg_set_ignore_access(TRUE);
parent::__construct();
-
+
// all __construct() code should come after here
}
@@ -47,20 +45,361 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest {
/**
* #1558
*/
- public function testElggObjectClearAnnotations() {
+ public function testElggObjectDeleteAnnotations() {
$this->entity = new ElggObject();
$guid = $this->entity->save();
-
+
$this->entity->annotate('test', 'hello', ACCESS_PUBLIC);
-
- $this->entity->clearAnnotations('does not exist');
-
+
+ $this->entity->deleteAnnotations('does not exist');
+
$num = $this->entity->countAnnotations('test');
-
+
//$this->assertIdentical($num, 1);
$this->assertEqual($num, 1);
-
+
// clean up
$this->entity->delete();
}
+
+ /**
+ * #2063 - get_resized_image_from_existing_file() fails asked for image larger than selection and not scaling an image up
+ * Test get_image_resize_parameters().
+ */
+ public function testElggResizeImage() {
+ $orig_width = 100;
+ $orig_height = 150;
+
+ // test against selection > max
+ $options = array(
+ 'maxwidth' => 50,
+ 'maxheight' => 50,
+ 'square' => TRUE,
+ 'upscale' => FALSE,
+
+ 'x1' => 25,
+ 'y1' => 75,
+ 'x2' => 100,
+ 'y2' => 150
+ );
+
+ // should get back the same x/y offset == x1, y1 and an image of 50x50
+ $params = get_image_resize_parameters($orig_width, $orig_height, $options);
+
+ $this->assertEqual($params['newwidth'], $options['maxwidth']);
+ $this->assertEqual($params['newheight'], $options['maxheight']);
+ $this->assertEqual($params['xoffset'], $options['x1']);
+ $this->assertEqual($params['yoffset'], $options['y1']);
+
+ // test against selection < max
+ $options = array(
+ 'maxwidth' => 50,
+ 'maxheight' => 50,
+ 'square' => TRUE,
+ 'upscale' => FALSE,
+
+ 'x1' => 75,
+ 'y1' => 125,
+ 'x2' => 100,
+ 'y2' => 150
+ );
+
+ // should get back the same x/y offset == x1, y1 and an image of 25x25 because no upscale
+ $params = get_image_resize_parameters($orig_width, $orig_height, $options);
+
+ $this->assertEqual($params['newwidth'], 25);
+ $this->assertEqual($params['newheight'], 25);
+ $this->assertEqual($params['xoffset'], $options['x1']);
+ $this->assertEqual($params['yoffset'], $options['y1']);
+ }
+
+ // #3722 Check canEdit() works for contains regardless of groups
+ function test_can_write_to_container() {
+ $user = new ElggUser();
+ $user->username = 'test_user_' . rand();
+ $user->name = 'test_user_name_' . rand();
+ $user->email = 'test@user.net';
+ $user->container_guid = 0;
+ $user->owner_guid = 0;
+ $user->save();
+
+ $object = new ElggObject();
+ $object->save();
+
+ $group = new ElggGroup();
+ $group->save();
+
+ // disable access overrides because we're admin.
+ $ia = elgg_set_ignore_access(false);
+
+ $this->assertFalse(can_write_to_container($user->guid, $object->guid));
+
+ global $elgg_test_user;
+ $elgg_test_user = $user;
+
+ // register hook to allow access
+ function can_write_to_container_test_hook($hook, $type, $value, $params) {
+ global $elgg_test_user;
+
+ if ($params['user']->getGUID() == $elgg_test_user->getGUID()) {
+ return true;
+ }
+ }
+
+ elgg_register_plugin_hook_handler('container_permissions_check', 'all', 'can_write_to_container_test_hook');
+ $this->assertTrue(can_write_to_container($user->guid, $object->guid));
+ elgg_unregister_plugin_hook_handler('container_permissions_check', 'all', 'can_write_to_container_test_hook');
+
+ $this->assertFalse(can_write_to_container($user->guid, $group->guid));
+ $group->join($user);
+ $this->assertTrue(can_write_to_container($user->guid, $group->guid));
+
+ elgg_set_ignore_access($ia);
+
+ $user->delete();
+ $object->delete();
+ $group->delete();
+ }
+
+ function test_db_shutdown_links() {
+ global $DB_DELAYED_QUERIES, $test_results;
+ $DB_DELAYED_QUERIES = array();
+
+ function test_delayed_results($results) {
+ global $test_results;
+ $test_results = $results;
+ }
+
+ $q = 'SELECT 1 as test';
+
+ $links = array('read', 'write', get_db_link('read'), get_db_link('write'));
+
+ foreach ($links as $link) {
+ $DB_DELAYED_QUERIES = array();
+
+ $result = execute_delayed_query($q, $link, 'test_delayed_results');
+
+ $this->assertTrue($result, "Failed with link = $link");
+ $this->assertEqual(count($DB_DELAYED_QUERIES), 1);
+ $this->assertEqual($DB_DELAYED_QUERIES[0]['q'], $q);
+ $this->assertEqual($DB_DELAYED_QUERIES[0]['l'], $link);
+ $this->assertEqual($DB_DELAYED_QUERIES[0]['h'], 'test_delayed_results');
+
+ db_delayedexecution_shutdown_hook();
+
+ $num_rows = mysql_num_rows($test_results);
+ $this->assertEqual($num_rows, 1);
+ $row = mysql_fetch_assoc($test_results);
+ $this->assertEqual($row['test'], 1);
+ }
+
+ // test bad case
+ $DB_DELAYED_QUERIES = array();
+ $result = execute_delayed_query($q, 'not_a_link', 'test_delayed_results');
+ $this->assertFalse($result);
+ $this->assertEqual(array(), $DB_DELAYED_QUERIES);
+ }
+
+ /**
+ * https://github.com/elgg/elgg/issues/3210 - Don't remove -s in friendly titles
+ * https://github.com/elgg/elgg/issues/2276 - improve char encoding
+ */
+ public function test_friendly_title() {
+ $cases = array(
+ // acid test
+ "B&N > Amazon, OK? <bold> 'hey!' $34"
+ => "bn-amazon-ok-bold-hey-34",
+
+ // hyphen, underscore and ASCII whitespace replaced by separator,
+ // other non-alphanumeric ASCII removed
+ "a-a_a a\na\ra\ta\va!a\"a#a\$a%aa'a(a)a*a+a,a.a/a:a;a=a?a@a[a\\a]a^a`a{a|a}a~a"
+ => "a-a-a-a-a-a-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+
+ // separators trimmed
+ "-_ hello _-"
+ => "hello",
+
+ // accents removed, lower case, other multibyte chars are URL encoded
+ "I\xC3\xB1t\xC3\xABrn\xC3\xA2ti\xC3\xB4n\xC3\xA0liz\xC3\xA6ti\xC3\xB8n, AND \xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"
+ // Iñtërnâtiônàlizætiøn, AND 日本語
+ => 'internationalizaetion-and-%E6%97%A5%E6%9C%AC%E8%AA%9E',
+ );
+
+ // where available, string is converted to NFC before transliteration
+ if (ElggTranslit::hasNormalizerSupport()) {
+ $form_d = "A\xCC\x8A"; // A followed by 'COMBINING RING ABOVE' (U+030A)
+ $cases[$form_d] = "a";
+ }
+
+ foreach ($cases as $case => $expected) {
+ $friendly_title = elgg_get_friendly_title($case);
+ $this->assertIdentical($expected, $friendly_title);
+ }
+ }
+
+ /**
+ * Test #5369 -- parse_urls()
+ * https://github.com/Elgg/Elgg/issues/5369
+ */
+ public function test_parse_urls() {
+
+ $cases = array(
+ 'no.link.here' =>
+ 'no.link.here',
+ 'simple link http://example.org test' =>
+ 'simple link <a href="http://example.org" rel="nofollow">http:/<wbr />/<wbr />example.org</a> test',
+ 'non-ascii http://ñew.org/ test' =>
+ 'non-ascii <a href="http://ñew.org/" rel="nofollow">http:/<wbr />/<wbr />ñew.org/<wbr /></a> test',
+
+ // section 2.1
+ 'percent encoded http://example.org/a%20b test' =>
+ 'percent encoded <a href="http://example.org/a%20b" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />a%20b</a> test',
+ // section 2.2: skipping single quote and parenthese
+ 'reserved characters http://example.org/:/?#[]@!$&*+,;= test' =>
+ 'reserved characters <a href="http://example.org/:/?#[]@!$&*+,;=" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />:/<wbr />?#[]@!$&*+,;=</a> test',
+ // section 2.3
+ 'unreserved characters http://example.org/a1-._~ test' =>
+ 'unreserved characters <a href="http://example.org/a1-._~" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />a1-._~</a> test',
+
+ 'parameters http://example.org/?val[]=1&val[]=2 test' =>
+ 'parameters <a href="http://example.org/?val[]=1&val[]=2" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />?val[]=1&val[]=2</a> test',
+ 'port http://example.org:80/ test' =>
+ 'port <a href="http://example.org:80/" rel="nofollow">http:/<wbr />/<wbr />example.org:80/<wbr /></a> test',
+
+ 'parentheses (http://www.google.com) test' =>
+ 'parentheses (<a href="http://www.google.com" rel="nofollow">http:/<wbr />/<wbr />www.google.com</a>) test',
+ 'comma http://elgg.org, test' =>
+ 'comma <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>, test',
+ 'period http://elgg.org. test' =>
+ 'period <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>. test',
+ 'exclamation http://elgg.org! test' =>
+ 'exclamation <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>! test',
+
+ 'already anchor <a href="http://twitter.com/">twitter</a> test' =>
+ 'already anchor <a href="http://twitter.com/">twitter</a> test',
+
+ 'ssl https://example.org/ test' =>
+ 'ssl <a href="https://example.org/" rel="nofollow">https:/<wbr />/<wbr />example.org/<wbr /></a> test',
+ 'ftp ftp://example.org/ test' =>
+ 'ftp <a href="ftp://example.org/" rel="nofollow">ftp:/<wbr />/<wbr />example.org/<wbr /></a> test',
+
+ 'web archive anchor <a href="http://web.archive.org/web/20000229040250/http://www.google.com/">google</a>' =>
+ 'web archive anchor <a href="http://web.archive.org/web/20000229040250/http://www.google.com/">google</a>',
+
+ 'single quotes already anchor <a href=\'http://www.yahoo.com\'>yahoo</a>' =>
+ 'single quotes already anchor <a href=\'http://www.yahoo.com\'>yahoo</a>',
+
+ 'unquoted already anchor <a href=http://www.yahoo.com>yahoo</a>' =>
+ 'unquoted already anchor <a href=http://www.yahoo.com>yahoo</a>',
+
+ 'parens in uri http://thedailywtf.com/Articles/A-(Long-Overdue)-BuildMaster-Introduction.aspx' =>
+ 'parens in uri <a href="http://thedailywtf.com/Articles/A-(Long-Overdue)-BuildMaster-Introduction.aspx" rel="nofollow">http:/<wbr />/<wbr />thedailywtf.com/<wbr />Articles/<wbr />A-(Long-Overdue)-BuildMaster-Introduction.aspx</a>'
+ );
+ foreach ($cases as $input => $output) {
+ $this->assertEqual($output, parse_urls($input));
+ }
+ }
+
+ /**
+ * Ensure additional select columns do not end up in entity attributes.
+ *
+ * https://github.com/Elgg/Elgg/issues/5538
+ */
+ public function test_extra_columns_dont_appear_in_attributes() {
+ global $ENTITY_CACHE;
+
+ // may not have groups in DB - let's create one
+ $group = new ElggGroup();
+ $group->name = 'test_group';
+ $group->access_id = ACCESS_PUBLIC;
+ $this->assertTrue($group->save() !== false);
+
+ // entity cache interferes with our test
+ $ENTITY_CACHE = array();
+
+ foreach (array('site', 'user', 'group', 'object') as $type) {
+ $entities = elgg_get_entities(array(
+ 'type' => $type,
+ 'selects' => array('1 as _nonexistent_test_column'),
+ 'limit' => 1,
+ ));
+ if (!$this->assertTrue($entities, "Query for '$type' did not return an entity.")) {
+ continue;
+ }
+ $entity = $entities[0];
+ $this->assertNull($entity->_nonexistent_test_column, "Additional select columns are leaking to attributes for '$type'");
+ }
+
+ $group->delete();
+ }
+
+ /**
+ * Ensure that ElggBatch doesn't go into infinite loop when disabling annotations recursively when show hidden is enabled.
+ *
+ * https://github.com/Elgg/Elgg/issues/5952
+ */
+ public function test_disabling_annotations_infinite_loop() {
+
+ //let's have some entity
+ $group = new ElggGroup();
+ $group->name = 'test_group';
+ $group->access_id = ACCESS_PUBLIC;
+ $this->assertTrue($group->save() !== false);
+
+ $total = 51;
+ //add some annotations
+ for ($cnt = 0; $cnt < $total; $cnt++) {
+ $group->annotate('test_annotation', 'value_' . $total);
+ }
+
+ //disable them
+ $show_hidden = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $options = array(
+ 'guid' => $group->guid,
+ 'limit' => $total, //using strict limit to avoid real infinite loop and just see ElggBatch limiting on it before finishing the work
+ );
+ elgg_disable_annotations($options);
+ access_show_hidden_entities($show_hidden);
+
+ //confirm all being disabled
+ $annotations = $group->getAnnotations(array(
+ 'limit' => $total,
+ ));
+ foreach ($annotations as $annotation) {
+ $this->assertTrue($annotation->enabled == 'no');
+ }
+
+ //delete group and annotations
+ $group->delete();
+ }
+
+ public function test_ElggXMLElement_does_not_load_external_entities() {
+ $elLast = libxml_disable_entity_loader(false);
+
+ // build payload that should trigger loading of external entity
+ $payload = file_get_contents(dirname(dirname(__FILE__)) . '/test_files/xxe/request.xml');
+ $path = realpath(dirname(dirname(__FILE__)) . '/test_files/xxe/external_entity.txt');
+ $path = str_replace('\\', '/', $path);
+ if ($path[0] != '/') {
+ $path = '/' . $path;
+ }
+ $path = 'file://' . $path;
+ $payload = sprintf($payload, $path);
+
+ // make sure we can actually this in this environment
+ $element = new SimpleXMLElement($payload);
+ $can_load_entity = preg_match('/secret/', (string)$element->methodName);
+
+ $this->skipUnless($can_load_entity, "XXE vulnerability cannot be tested on this system");
+
+ if ($can_load_entity) {
+ $el = new ElggXMLElement($payload);
+ $chidren = $el->getChildren();
+ $content = $chidren[0]->getContent();
+ $this->assertNoPattern('/secret/', $content);
+ }
+
+ libxml_disable_entity_loader($elLast);
+ }
}
diff --git a/engine/tests/services/api.php b/engine/tests/services/api.php
index 216c888ab..3d07c0bbb 100644
--- a/engine/tests/services/api.php
+++ b/engine/tests/services/api.php
@@ -4,8 +4,6 @@
*
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
class ElggCoreServicesApiTest extends ElggCoreUnitTest {
@@ -21,7 +19,7 @@ class ElggCoreServicesApiTest extends ElggCoreUnitTest {
// expose_function
public function testExposeFunctionNoMethod() {
try {
- expose_function();
+ @expose_function();
$this->assertTrue(FALSE);
} catch (Exception $e) {
$this->assertIsA($e, 'InvalidParameterException');
@@ -31,7 +29,7 @@ class ElggCoreServicesApiTest extends ElggCoreUnitTest {
public function testExposeFunctionNoFunction() {
try {
- expose_function('test');
+ @expose_function('test');
$this->assertTrue(FALSE);
} catch (Exception $e) {
$this->assertIsA($e, 'InvalidParameterException');
@@ -41,7 +39,7 @@ class ElggCoreServicesApiTest extends ElggCoreUnitTest {
public function testExposeFunctionBadParameters() {
try {
- expose_function('test', 'test', 'BAD');
+ @expose_function('test', 'test', 'BAD');
$this->assertTrue(FALSE);
} catch (Exception $e) {
$this->assertIsA($e, 'InvalidParameterException');
@@ -61,7 +59,7 @@ class ElggCoreServicesApiTest extends ElggCoreUnitTest {
public function testExposeFunctionBadHttpMethod() {
try {
- expose_function('test', 'test', null, '', 'BAD');
+ @expose_function('test', 'test', null, '', 'BAD');
$this->assertTrue(FALSE);
} catch (Exception $e) {
$this->assertIsA($e, 'InvalidParameterException');
@@ -130,7 +128,6 @@ class ElggCoreServicesApiTest extends ElggCoreUnitTest {
$this->assertTrue(FALSE);
} catch (Exception $e) {
$this->assertIsA($e, 'APIException');
- $this->assertIdentical($e->getMessage(), elgg_echo('APIException:UserAuthenticationFailed'));
}
}
@@ -286,9 +283,9 @@ class ElggCoreServicesApiTest extends ElggCoreUnitTest {
}
// api key methods
- public function testApiAuthenticate() {
- $this->assertFalse(pam_authenticate(null, "api"));
- }
+ //public function testApiAuthenticate() {
+ // $this->assertFalse(pam_authenticate(null, "api"));
+ //}
public function testApiAuthKeyNoKey() {
try {
diff --git a/engine/tests/suite.php b/engine/tests/suite.php
index 1f6ca61ff..4203bc5d6 100644
--- a/engine/tests/suite.php
+++ b/engine/tests/suite.php
@@ -4,13 +4,13 @@
*
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
require_once(dirname( __FILE__ ) . '/../start.php');
+admin_gatekeeper();
+
$vendor_path = "$CONFIG->path/vendors/simpletest";
$test_path = "$CONFIG->path/engine/tests";
@@ -19,6 +19,10 @@ require_once("$vendor_path/mock_objects.php");
require_once("$vendor_path/reporter.php");
require_once("$test_path/elgg_unit_test.php");
+// turn off system log
+elgg_unregister_event_handler('all', 'all', 'system_log_listener');
+elgg_unregister_event_handler('log', 'systemlog', 'system_log_default_logger');
+
// Disable maximum execution time.
// Tests take a while...
set_time_limit(0);
@@ -26,7 +30,7 @@ set_time_limit(0);
$suite = new TestSuite('Elgg Core Unit Tests');
// emit a hook to pull in all tests
-$test_files = trigger_plugin_hook('unit_test', 'system', null, array());
+$test_files = elgg_trigger_plugin_hook('unit_test', 'system', null, array());
foreach ($test_files as $file) {
$suite->addTestFile($file);
}
@@ -45,5 +49,5 @@ if (TextReporter::inCli()) {
// Ensure that only logged-in users can see this page
//admin_gatekeeper();
$old = elgg_set_ignore_access(TRUE);
-$suite->Run(new HtmlReporter());
+$suite->Run(new HtmlReporter('utf-8'));
elgg_set_ignore_access($old);
diff --git a/engine/tests/test_files/output/autop/block-a.exp.norun.html b/engine/tests/test_files/output/autop/block-a.exp.norun.html
new file mode 100644
index 000000000..addf29dec
--- /dev/null
+++ b/engine/tests/test_files/output/autop/block-a.exp.norun.html
@@ -0,0 +1,6 @@
+
+<p>HTML5 allows A to contain block-level content</p>
+<a href="foo"><h3>A treated as block</h3>
+<p>Read more</p>
+</a>
+<p><a href="foo">A treated as<br /> inline</a></p>
diff --git a/engine/tests/test_files/output/autop/block-a.in.norun.html b/engine/tests/test_files/output/autop/block-a.in.norun.html
new file mode 100644
index 000000000..fc2dac43a
--- /dev/null
+++ b/engine/tests/test_files/output/autop/block-a.in.norun.html
@@ -0,0 +1,9 @@
+HTML5 allows A to contain block-level content
+<a href="foo">
+
+ <h3>A treated as block</h3>
+
+ Read more
+</a>
+<a href="foo">A treated as
+ inline</a>
diff --git a/engine/tests/test_files/output/autop/domdoc_exp.html b/engine/tests/test_files/output/autop/domdoc_exp.html
new file mode 100644
index 000000000..8480c1083
--- /dev/null
+++ b/engine/tests/test_files/output/autop/domdoc_exp.html
@@ -0,0 +1,46 @@
+›
+Vietnamese - Tiếng Việt
+
+<h1>h1</h1>
+<p>Paragraph <a href="http://google.com/">link</a> <strong>Bold</strong> <em>italic</em> <em><strong>bolditalic</strong></em> <span style="background-color: #ffff00; "></span></p>
+<h2>h2</h2>
+<p>Paragraph <span style="font-size: xx-small;">size1</span> <span style="font-size: x-small;">size2</span> <span style="font-size: medium;">size4</span></p>
+<h3>h3</h3>
+<p>Paragraph <span style="text-decoration: underline;">underline</span> <span style="text-decoration: line-through;">strikethrough</span> <span style="color: #ff0000;">color</span> <span style="background-color: #ffff00; ">background</span></p>
+<blockquote>
+ <p>Blockquoted paragraph</p>
+</blockquote>
+<p>Paragraph following blockquote</p>
+<ul><li>Unordered</li>
+ <li>List</li>
+</ul><p>Paragraph between lists</p>
+<ol><li>Ordered</li>
+ <li>List</li>
+</ol><p>Paragraph between lists</p>
+<ul><li>OL list</li>
+ <li>nested<ol><li>inside a</li>
+ <li>UL list</li>
+ </ol></li>
+</ul><p>Paragraph between lists</p>
+<table border="0"><tbody><tr><td>Table with</td>
+ <td></td>
+ </tr><tr><td></td>
+ <td>border=0</td>
+ </tr></tbody></table><p>Paragraph</p>
+<ol><li>UL list</li>
+ <li>nested
+ <ul><li>inside a</li>
+ <li>OL list</li>
+ </ul></li>
+</ol><p>Paragraph between tables</p>
+<table border="1" cellpadding="5"><tbody><tr><td>Table with border=1</td>
+ <td></td>
+ </tr><tr><td></td>
+ <td>cellpadding = 5</td>
+ </tr></tbody></table><p>Paragraph between tables</p>
+<table border="2"><tbody><tr><td>Table with</td>
+ <td></td>
+ </tr><tr><td></td>
+ <td>border=2</td>
+ </tr></tbody></table> \ No newline at end of file
diff --git a/engine/tests/test_files/output/autop/domdoc_in.html b/engine/tests/test_files/output/autop/domdoc_in.html
new file mode 100644
index 000000000..4c465b435
--- /dev/null
+++ b/engine/tests/test_files/output/autop/domdoc_in.html
@@ -0,0 +1,80 @@
+&#8250;
+&nbsp;
+Vietnamese - Tiếng Việt
+
+<h1>h1</h1>
+<p>Paragraph <a href="http://google.com/">link</a> <strong>Bold</strong> <em>italic</em> <em><strong>bolditalic</strong></em>&nbsp;<span style="background-color: #ffff00; "></span></p>
+<h2>h2</h2>
+<p>Paragraph <span style="font-size: xx-small;">size1</span> <span style="font-size: x-small;">size2</span> <span style="font-size: medium;">size4</span></p>
+<h3>h3</h3>
+<p>Paragraph <span style="text-decoration: underline;">underline</span> <span style="text-decoration: line-through;">strikethrough</span> <span style="color: #ff0000;">color</span> <span style="background-color: #ffff00; ">background</span></p>
+<blockquote>
+ <p>Blockquoted paragraph</p>
+</blockquote>
+<p>Paragraph following blockquote</p>
+<ul>
+ <li>Unordered</li>
+ <li>List</li>
+</ul>
+<p>Paragraph between lists</p>
+<ol>
+ <li>Ordered</li>
+ <li>List</li>
+</ol>
+<p>Paragraph between lists</p>
+<ul>
+ <li>OL list</li>
+ <li>nested<ol>
+ <li>inside a</li>
+ <li>UL list</li>
+ </ol></li>
+</ul>
+<p>Paragraph between lists</p>
+<table border="0">
+ <tbody>
+ <tr>
+ <td>Table with</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>border=0</td>
+ </tr>
+ </tbody>
+</table>
+<p>Paragraph</p>
+<ol>
+ <li>UL list</li>
+ <li>nested
+ <ul>
+ <li>inside a</li>
+ <li>OL list</li>
+ </ul>
+ </li>
+</ol>
+<p>Paragraph between tables</p>
+<table border="1" cellpadding="5">
+ <tbody>
+ <tr>
+ <td>Table with border=1</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>cellpadding = 5</td>
+ </tr>
+ </tbody>
+</table>
+<p>Paragraph between tables</p>
+<table border="2">
+ <tbody>
+ <tr>
+ <td>Table with</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>border=2</td>
+ </tr>
+ </tbody>
+</table> \ No newline at end of file
diff --git a/engine/tests/test_files/output/autop/typical-post.exp.html b/engine/tests/test_files/output/autop/typical-post.exp.html
new file mode 100644
index 000000000..f9d75a114
--- /dev/null
+++ b/engine/tests/test_files/output/autop/typical-post.exp.html
@@ -0,0 +1,84 @@
+<h2>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h2>
+<p><img class="alignright size-thumbnail wp-image-905" title="Surest Things mixing session in Adobe Audition" src="http://www.mrclay.org/wp-content/uploads/2010/09/surestThings_audition-150x150.png" alt="screenshot of Audition mixing session" width="150" height="150">Vivamus enim ante, <em>mattis eget imperdiet nec, pharetra vel velit.</em> Sed at euismod nibh. Praesent lacus tellus, <a href="http://google.com/">posuere et convallis</a> a, <strong>mollis et tellus. Suspendisse potenti</strong>. Phasellus tincidunt dignissim est eget mattis. Vestibulum lacinia <del>condimentum tellus, non vestibulum erat dapibus</del> quis. Aliquam arcu nibh, viverra adipiscing eleifend quis, pretium vitae ipsum.</p>
+
+<p>Curabitur turpis ante, <span style="color: #993300;">congue ac dapibus quis, vehicula ac orci.</span> Nunc luctus neque non massa porta sed pharetra ante accumsan. <a href="http://google.com/">Nam suscipit</a> risus quis libero convallis viverra. Ut at arcu enim, vel pharetra dolor.</p>
+<h3>Donec at massa ante, sagittis fermentum urna.</h3><blockquote>
+<p>Mauris volutpat est id massa volutpat lacinia. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In in nisl mauris. In aliquet pretium nisl, vel convallis neque cursus vitae. Curabitur id mauris in urna gravida ornare.</p>
+
+<p>[caption id="attachment_719" align="alignleft" width="150" caption="Ibanez AGB140 Bass"]<img class="size-thumbnail wp-image-719" title="Ibanez AGB140 Bass" src="http://www.mrclay.org/wp-content/uploads/2010/04/agb140-e1271773766573-150x150.jpg" alt="Ibanez AGB140 Bass" width="150" height="150">[/caption]</p>
+
+<p>Aenean <a href="http://google.com/">aliquet cursus purus sed gravida. Cras auctor euismod justo, ac dictum purus facilisis dignissim.</a> Quisque facilisis porta sem, ac suscipit quam molestie nec. Pellentesque quis hendrerit enim. Vivamus tempor erat diam. Sed eu felis nunc. Cras posuere lorem commodo turpis mollis sagittis. Mauris lobortis nunc felis.</p>
+
+<p>Maecenas elit lorem, varius sed condimentum ac, cursus et magna. Nam ut massa id augue consectetur porttitor eleifend in nunc. Curabitur cursus varius dictum. Vestibulum vel justo et neque tempus placerat a vel sapien.</p>
+</blockquote>
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus enim ante, mattis eget imperdiet nec, <a href="http://google.com/">pharetra </a>vel velit. Sed at euismod nibh. Praesent lacus tellus, posuere et convallis a, mollis et tellus.</p>
+<pre><code>&lt;?php
+class DataTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider provider
+ */
+ public function testAdd($a, $b, $c)
+ {
+ $this-&gt;assertEquals($c, $a + $b);
+ }
+
+ public function provider()
+ {
+ return array(
+ array(0, 0, 0),
+ array(0, 1, 1),
+ array(1, 0, 1),
+ array(1, 1, 3)
+ );
+ }
+}</code></pre><ul><li>Suspendisse potenti. Phasellus tincidunt dignissim est eget mattis.</li>
+ <li>Vestibulum lacinia condimentum tellus, non vestibulum erat dapibus quis.</li>
+ <li>Aliquam arcu nibh, <a href="http://google.com/">viverra</a> adipiscing eleifend quis, pretium vitae ipsum.</li>
+ <li>Curabitur turpis ante, congue ac <a href="http://google.com/">dapibus quis</a>, vehicula ac orci.</li>
+</ul>
+<p>Nunc luctus neque non massa porta sed pharetra ante accumsan. Nam suscipit risus quis libero convallis viverra. Ut at arcu enim, vel pharetra dolor. Donec at massa ante, sagittis fermentum urna.</p>
+
+<p><object width="480" height="390"><param name="movie" value="http://www.youtube.com/v/zW9YOMaVTFI?fs=1&amp;hl=en_US"><param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="always"><embed type="application/x-shockwave-flash" width="480" height="390" src="http://www.youtube.com/v/zW9YOMaVTFI?fs=1&amp;hl=en_US" allowfullscreen="true" allowscriptaccess="always"></embed></object></p>
+<h2>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h2>
+<p><img class="alignright size-thumbnail wp-image-905" title="Surest Things mixing session in Adobe Audition" src="http://www.mrclay.org/wp-content/uploads/2010/09/surestThings_audition-150x150.png" alt="screenshot of Audition mixing session" width="150" height="150">Vivamus enim ante, <em>mattis eget imperdiet nec, pharetra vel velit.</em> Sed at euismod nibh. Praesent lacus tellus, <a href="http://google.com/">posuere et convallis</a> a, <strong>mollis et tellus. Suspendisse potenti</strong>. Phasellus tincidunt dignissim est eget mattis. Vestibulum lacinia <del>condimentum tellus, non vestibulum erat dapibus</del> quis. Aliquam arcu nibh, viverra adipiscing eleifend quis, pretium vitae ipsum.</p>
+
+<p>Curabitur turpis ante, <span style="color: #993300;">congue ac dapibus quis, vehicula ac orci.</span> Nunc luctus neque non massa porta sed pharetra ante accumsan. <a href="http://google.com/">Nam suscipit</a> risus quis libero convallis viverra. Ut at arcu enim, vel pharetra dolor.</p>
+<h3>Donec at massa ante, sagittis fermentum urna.</h3><blockquote>
+<p>Mauris volutpat est id massa volutpat lacinia. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In in nisl mauris. In aliquet pretium nisl, vel convallis neque cursus vitae. Curabitur id mauris in urna gravida ornare.</p>
+
+<p>[caption id="attachment_719" align="alignleft" width="150" caption="Ibanez AGB140 Bass"]<img class="size-thumbnail wp-image-719" title="Ibanez AGB140 Bass" src="http://www.mrclay.org/wp-content/uploads/2010/04/agb140-e1271773766573-150x150.jpg" alt="Ibanez AGB140 Bass" width="150" height="150">[/caption]</p>
+
+<p>Aenean <a href="http://google.com/">aliquet cursus purus sed gravida. Cras auctor euismod justo, ac dictum purus facilisis dignissim.</a> Quisque facilisis porta sem, ac suscipit quam molestie nec. Pellentesque quis hendrerit enim. Vivamus tempor erat diam. Sed eu felis nunc. Cras posuere lorem commodo turpis mollis sagittis. Mauris lobortis nunc felis.</p>
+
+<p>Maecenas elit lorem, varius sed condimentum ac, cursus et magna. Nam ut massa id augue consectetur porttitor eleifend in nunc. Curabitur cursus varius dictum. Vestibulum vel justo et neque tempus placerat a vel sapien.</p>
+</blockquote>
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus enim ante, mattis eget imperdiet nec, <a href="http://google.com/">pharetra </a>vel velit. Sed at euismod nibh. Praesent lacus tellus, posuere et convallis a, mollis et tellus.</p>
+<pre><code>&lt;?php
+class DataTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider provider
+ */
+ public function testAdd($a, $b, $c)
+ {
+ $this-&gt;assertEquals($c, $a + $b);
+ }
+
+ public function provider()
+ {
+ return array(
+ array(0, 0, 0),
+ array(0, 1, 1),
+ array(1, 0, 1),
+ array(1, 1, 3)
+ );
+ }
+}</code></pre><ul><li>Suspendisse potenti. Phasellus tincidunt dignissim est eget mattis.</li>
+ <li>Vestibulum lacinia condimentum tellus, non vestibulum erat dapibus quis.</li>
+ <li>Aliquam arcu nibh, <a href="http://google.com/">viverra</a> adipiscing eleifend quis, pretium vitae ipsum.</li>
+ <li>Curabitur turpis ante, congue ac <a href="http://google.com/">dapibus quis</a>, vehicula ac orci.</li>
+</ul>
+<p>Nunc luctus neque non massa porta sed pharetra ante accumsan. Nam suscipit risus quis libero convallis viverra. Ut at arcu enim, vel pharetra dolor. Donec at massa ante, sagittis fermentum urna.</p>
+
+<p><object width="480" height="390"><param name="movie" value="http://www.youtube.com/v/zW9YOMaVTFI?fs=1&amp;hl=en_US"><param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="always"><embed type="application/x-shockwave-flash" width="480" height="390" src="http://www.youtube.com/v/zW9YOMaVTFI?fs=1&amp;hl=en_US" allowfullscreen="true" allowscriptaccess="always"></embed></object></p>
diff --git a/engine/tests/test_files/output/autop/typical-post.in.html b/engine/tests/test_files/output/autop/typical-post.in.html
new file mode 100644
index 000000000..6e4984cc4
--- /dev/null
+++ b/engine/tests/test_files/output/autop/typical-post.in.html
@@ -0,0 +1,89 @@
+<h2>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h2>
+<img class="alignright size-thumbnail wp-image-905" title="Surest Things mixing session in Adobe Audition" src="http://www.mrclay.org/wp-content/uploads/2010/09/surestThings_audition-150x150.png" alt="screenshot of Audition mixing session" width="150" height="150" />Vivamus enim ante, <em>mattis eget imperdiet nec, pharetra vel velit.</em> Sed at euismod nibh. Praesent lacus tellus, <a href="http://google.com/">posuere et convallis</a> a, <strong>mollis et tellus. Suspendisse potenti</strong>. Phasellus tincidunt dignissim est eget mattis. Vestibulum lacinia <del>condimentum tellus, non vestibulum erat dapibus</del> quis. Aliquam arcu nibh, viverra adipiscing eleifend quis, pretium vitae ipsum.
+
+Curabitur turpis ante, <span style="color: #993300;">congue ac dapibus quis, vehicula ac orci.</span> Nunc luctus neque non massa porta sed pharetra ante accumsan. <a href="http://google.com/">Nam suscipit</a> risus quis libero convallis viverra. Ut at arcu enim, vel pharetra dolor.
+<h3>Donec at massa ante, sagittis fermentum urna.</h3>
+<blockquote>Mauris volutpat est id massa volutpat lacinia. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In in nisl mauris. In aliquet pretium nisl, vel convallis neque cursus vitae. Curabitur id mauris in urna gravida ornare.
+
+[caption id="attachment_719" align="alignleft" width="150" caption="Ibanez AGB140 Bass"]<img class="size-thumbnail wp-image-719" title="Ibanez AGB140 Bass" src="http://www.mrclay.org/wp-content/uploads/2010/04/agb140-e1271773766573-150x150.jpg" alt="Ibanez AGB140 Bass" width="150" height="150" />[/caption]
+
+Aenean <a href="http://google.com/">aliquet cursus purus sed gravida. Cras auctor euismod justo, ac dictum purus facilisis dignissim.</a> Quisque facilisis porta sem, ac suscipit quam molestie nec. Pellentesque quis hendrerit enim. Vivamus tempor erat diam. Sed eu felis nunc. Cras posuere lorem commodo turpis mollis sagittis. Mauris lobortis nunc felis.
+
+Maecenas elit lorem, varius sed condimentum ac, cursus et magna. Nam ut massa id augue consectetur porttitor eleifend in nunc. Curabitur cursus varius dictum. Vestibulum vel justo et neque tempus placerat a vel sapien.</blockquote>
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus enim ante, mattis eget imperdiet nec, <a href="http://google.com/">pharetra </a>vel velit. Sed at euismod nibh. Praesent lacus tellus, posuere et convallis a, mollis et tellus.
+
+<pre><code>&lt;?php
+class DataTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider provider
+ */
+ public function testAdd($a, $b, $c)
+ {
+ $this-&gt;assertEquals($c, $a + $b);
+ }
+
+ public function provider()
+ {
+ return array(
+ array(0, 0, 0),
+ array(0, 1, 1),
+ array(1, 0, 1),
+ array(1, 1, 3)
+ );
+ }
+}</code></pre>
+<ul>
+ <li>Suspendisse potenti. Phasellus tincidunt dignissim est eget mattis.</li>
+ <li>Vestibulum lacinia condimentum tellus, non vestibulum erat dapibus quis.</li>
+ <li>Aliquam arcu nibh, <a href="http://google.com/">viverra</a> adipiscing eleifend quis, pretium vitae ipsum.</li>
+ <li>Curabitur turpis ante, congue ac <a href="http://google.com/">dapibus quis</a>, vehicula ac orci.</li>
+</ul>
+Nunc luctus neque non massa porta sed pharetra ante accumsan. Nam suscipit risus quis libero convallis viverra. Ut at arcu enim, vel pharetra dolor. Donec at massa ante, sagittis fermentum urna.
+
+<object width="480" height="390"><param name="movie" value="http://www.youtube.com/v/zW9YOMaVTFI?fs=1&amp;hl=en_US" /><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><embed type="application/x-shockwave-flash" width="480" height="390" src="http://www.youtube.com/v/zW9YOMaVTFI?fs=1&amp;hl=en_US" allowfullscreen="true" allowscriptaccess="always"></embed></object>
+
+<h2>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h2>
+<img class="alignright size-thumbnail wp-image-905" title="Surest Things mixing session in Adobe Audition" src="http://www.mrclay.org/wp-content/uploads/2010/09/surestThings_audition-150x150.png" alt="screenshot of Audition mixing session" width="150" height="150" />Vivamus enim ante, <em>mattis eget imperdiet nec, pharetra vel velit.</em> Sed at euismod nibh. Praesent lacus tellus, <a href="http://google.com/">posuere et convallis</a> a, <strong>mollis et tellus. Suspendisse potenti</strong>. Phasellus tincidunt dignissim est eget mattis. Vestibulum lacinia <del>condimentum tellus, non vestibulum erat dapibus</del> quis. Aliquam arcu nibh, viverra adipiscing eleifend quis, pretium vitae ipsum.
+
+Curabitur turpis ante, <span style="color: #993300;">congue ac dapibus quis, vehicula ac orci.</span> Nunc luctus neque non massa porta sed pharetra ante accumsan. <a href="http://google.com/">Nam suscipit</a> risus quis libero convallis viverra. Ut at arcu enim, vel pharetra dolor.
+<h3>Donec at massa ante, sagittis fermentum urna.</h3>
+<blockquote>Mauris volutpat est id massa volutpat lacinia. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In in nisl mauris. In aliquet pretium nisl, vel convallis neque cursus vitae. Curabitur id mauris in urna gravida ornare.
+
+[caption id="attachment_719" align="alignleft" width="150" caption="Ibanez AGB140 Bass"]<img class="size-thumbnail wp-image-719" title="Ibanez AGB140 Bass" src="http://www.mrclay.org/wp-content/uploads/2010/04/agb140-e1271773766573-150x150.jpg" alt="Ibanez AGB140 Bass" width="150" height="150" />[/caption]
+
+Aenean <a href="http://google.com/">aliquet cursus purus sed gravida. Cras auctor euismod justo, ac dictum purus facilisis dignissim.</a> Quisque facilisis porta sem, ac suscipit quam molestie nec. Pellentesque quis hendrerit enim. Vivamus tempor erat diam. Sed eu felis nunc. Cras posuere lorem commodo turpis mollis sagittis. Mauris lobortis nunc felis.
+
+Maecenas elit lorem, varius sed condimentum ac, cursus et magna. Nam ut massa id augue consectetur porttitor eleifend in nunc. Curabitur cursus varius dictum. Vestibulum vel justo et neque tempus placerat a vel sapien.</blockquote>
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus enim ante, mattis eget imperdiet nec, <a href="http://google.com/">pharetra </a>vel velit. Sed at euismod nibh. Praesent lacus tellus, posuere et convallis a, mollis et tellus.
+
+<pre><code>&lt;?php
+class DataTest extends PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider provider
+ */
+ public function testAdd($a, $b, $c)
+ {
+ $this-&gt;assertEquals($c, $a + $b);
+ }
+
+ public function provider()
+ {
+ return array(
+ array(0, 0, 0),
+ array(0, 1, 1),
+ array(1, 0, 1),
+ array(1, 1, 3)
+ );
+ }
+}</code></pre>
+<ul>
+ <li>Suspendisse potenti. Phasellus tincidunt dignissim est eget mattis.</li>
+ <li>Vestibulum lacinia condimentum tellus, non vestibulum erat dapibus quis.</li>
+ <li>Aliquam arcu nibh, <a href="http://google.com/">viverra</a> adipiscing eleifend quis, pretium vitae ipsum.</li>
+ <li>Curabitur turpis ante, congue ac <a href="http://google.com/">dapibus quis</a>, vehicula ac orci.</li>
+</ul>
+Nunc luctus neque non massa porta sed pharetra ante accumsan. Nam suscipit risus quis libero convallis viverra. Ut at arcu enim, vel pharetra dolor. Donec at massa ante, sagittis fermentum urna.
+
+<object width="480" height="390"><param name="movie" value="http://www.youtube.com/v/zW9YOMaVTFI?fs=1&amp;hl=en_US" /><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><embed type="application/x-shockwave-flash" width="480" height="390" src="http://www.youtube.com/v/zW9YOMaVTFI?fs=1&amp;hl=en_US" allowfullscreen="true" allowscriptaccess="always"></embed></object> \ No newline at end of file
diff --git a/engine/tests/test_files/output/autop/wp-welcome.exp.html b/engine/tests/test_files/output/autop/wp-welcome.exp.html
new file mode 100644
index 000000000..2f612e3dd
--- /dev/null
+++ b/engine/tests/test_files/output/autop/wp-welcome.exp.html
@@ -0,0 +1,22 @@
+
+<p>Welcome to WordPress! This post contains important information. After you read it, you can make it private to hide it from visitors but still have the information handy for future reference.</p>
+
+<p>First things first:</p>
+<ul><li><a href="%1%24s" title="Subscribe to the WordPress mailing list for Release Notifications">Subscribe to the WordPress mailing list for release notifications</a></li>
+</ul>
+<p>As a subscriber, you will receive an email every time an update is available (and only then). This will make it easier to keep your site up to date, and secure from evildoers.<br />When a new version is released, <a href="%2%24s" title="If you are already logged in, this will take you directly to the Dashboard">log in to the Dashboard</a> and follow the instructions.<br />Upgrading is a couple of clicks!</p>
+
+<p>Then you can start enjoying the WordPress experience:</p>
+<ul><li>Edit your personal information at <a href="%3%24s" title="Edit settings like your password, your display name and your contact information">Users &#8250; Your Profile</a></li>
+ <li>Start publishing at <a href="%4%24s" title="Create a new post">Posts &#8250; Add New</a> and at <a href="%5%24s" title="Create a new page">Pages &#8250; Add New</a></li>
+ <li>Browse and install plugins at <a href="%6%24s" title="Browse and install plugins at the official WordPress repository directly from your Dashboard">Plugins &#8250; Add New</a></li>
+ <li>Browse and install themes at <a href="%7%24s" title="Browse and install themes at the official WordPress repository directly from your Dashboard">Appearance &#8250; Add New Themes</a></li>
+ <li>Modify and prettify your website&#8217;s links at <a href="%8%24s" title="For example, select a link structure like: http://example.com/1999/12/post-name">Settings &#8250; Permalinks</a></li>
+ <li>Import content from another system or WordPress site at <a href="%9%24s" title="WordPress comes with importers for the most common publishing systems">Tools &#8250; Import</a></li>
+ <li>Find answers to your questions at the <a href="%10%24s" title="The official WordPress documentation, maintained by the WordPress community">WordPress Codex</a></li>
+</ul>
+<p>To keep this post for reference, <a href="%11%24s" title="Click to edit the content and settings of this post">click to edit it</a>, go to the Publish box and change its Visibility from Public to Private.</p>
+
+<p>Thank you for selecting WordPress. We wish you happy publishing!</p>
+
+<p>PS. Not yet subscribed for update notifications? <a href="%1%24s" title="Subscribe to the WordPress mailing list for Release Notifications">Do it now!</a></p>
diff --git a/engine/tests/test_files/output/autop/wp-welcome.in.html b/engine/tests/test_files/output/autop/wp-welcome.in.html
new file mode 100644
index 000000000..338ede73f
--- /dev/null
+++ b/engine/tests/test_files/output/autop/wp-welcome.in.html
@@ -0,0 +1,25 @@
+Welcome to WordPress! This post contains important information. After you read it, you can make it private to hide it from visitors but still have the information handy for future reference.
+
+First things first:
+<ul>
+ <li><a href="%1$s" title="Subscribe to the WordPress mailing list for Release Notifications">Subscribe to the WordPress mailing list for release notifications</a></li>
+</ul>
+As a subscriber, you will receive an email every time an update is available (and only then). This will make it easier to keep your site up to date, and secure from evildoers.
+When a new version is released, <a href="%2$s" title="If you are already logged in, this will take you directly to the Dashboard">log in to the Dashboard</a> and follow the instructions.
+Upgrading is a couple of clicks!
+
+Then you can start enjoying the WordPress experience:
+<ul>
+ <li>Edit your personal information at <a href="%3$s" title="Edit settings like your password, your display name and your contact information">Users &#8250; Your Profile</a></li>
+ <li>Start publishing at <a href="%4$s" title="Create a new post">Posts &#8250; Add New</a> and at <a href="%5$s" title="Create a new page">Pages &#8250; Add New</a></li>
+ <li>Browse and install plugins at <a href="%6$s" title="Browse and install plugins at the official WordPress repository directly from your Dashboard">Plugins &#8250; Add New</a></li>
+ <li>Browse and install themes at <a href="%7$s" title="Browse and install themes at the official WordPress repository directly from your Dashboard">Appearance &#8250; Add New Themes</a></li>
+ <li>Modify and prettify your website&#8217;s links at <a href="%8$s" title="For example, select a link structure like: http://example.com/1999/12/post-name">Settings &#8250; Permalinks</a></li>
+ <li>Import content from another system or WordPress site at <a href="%9$s" title="WordPress comes with importers for the most common publishing systems">Tools &#8250; Import</a></li>
+ <li>Find answers to your questions at the <a href="%10$s" title="The official WordPress documentation, maintained by the WordPress community">WordPress Codex</a></li>
+</ul>
+To keep this post for reference, <a href="%11$s" title="Click to edit the content and settings of this post">click to edit it</a>, go to the Publish box and change its Visibility from Public to Private.
+
+Thank you for selecting WordPress. We wish you happy publishing!
+
+PS. Not yet subscribed for update notifications? <a href="%1$s" title="Subscribe to the WordPress mailing list for Release Notifications">Do it now!</a>
diff --git a/engine/tests/test_files/output/autop/wpautop-fails.exp.html b/engine/tests/test_files/output/autop/wpautop-fails.exp.html
new file mode 100644
index 000000000..d018db4ff
--- /dev/null
+++ b/engine/tests/test_files/output/autop/wpautop-fails.exp.html
@@ -0,0 +1,31 @@
+
+<p>paragraph</p>
+
+<p>paragraph</p>
+<div class="whatever"><blockquote>
+<p>paragraph</p>
+</blockquote>
+<p>line</p>
+</div>
+<p>paragraph</p>
+<ul><li>line</li>
+<li>paragraph
+
+paragraph</li>
+</ul>
+<p>paragraph<br />line<br />line</p>
+<pre>Honor
+this whitespace
+</pre>
+<p>paragraph</p>
+<style><!--
+Do not alter!
+--></style>
+<p>paragraph <!-- do not alter --></p>
+<dl><dt>term</dt> <dd>paragraph
+
+<a href="xx"> <img src="yy"></a>
+
+paragraph</dd> </dl><div><a href="xx"> <img src="yy"></a></div>
+<p>Hello <a href="link"><br /><br />World</a></p>
+<p id="abc">Paragraph</p><div>Line</div> \ No newline at end of file
diff --git a/engine/tests/test_files/output/autop/wpautop-fails.in.html b/engine/tests/test_files/output/autop/wpautop-fails.in.html
new file mode 100644
index 000000000..9aa24be59
--- /dev/null
+++ b/engine/tests/test_files/output/autop/wpautop-fails.in.html
@@ -0,0 +1,41 @@
+
+paragraph
+
+paragraph <div class="whatever"><blockquote>
+ paragraph
+ </blockquote>
+ line
+</div>
+
+paragraph
+<ul>
+<li>line</li>
+<li>paragraph
+
+paragraph</li>
+</ul>
+paragraph
+line<br>
+ line
+<pre>Honor
+this whitespace
+</pre>
+paragraph
+<style><!--
+Do not alter!
+--></style>
+paragraph <!-- do not alter -->
+<dl> <dt>term</dt> <dd>paragraph
+
+<a href="xx"> <img src="yy" /> </a>
+
+paragraph</dd> </dl>
+<div><a href="xx"> <img src="yy" /> </a></div>
+
+Hello <a href="link">
+
+World</a>
+
+<p id="abc">Paragraph</p>
+
+<div>Line</div> \ No newline at end of file
diff --git a/engine/tests/test_files/output/autop/wysiwyg-test.exp.html b/engine/tests/test_files/output/autop/wysiwyg-test.exp.html
new file mode 100644
index 000000000..1f23d6154
--- /dev/null
+++ b/engine/tests/test_files/output/autop/wysiwyg-test.exp.html
@@ -0,0 +1,51 @@
+
+<p>&nbps;<br />&#8820;</p>
+<h1>h1</h1>
+<p>Paragraph <a href="http://google.com/">link</a> <strong>Bold</strong> <em>italic</em> <em><strong>bolditalic</strong></em>&nbsp;<span style="background-color: #ffff00; "></span></p>
+<h2>h2</h2>
+<p>Paragraph <span style="font-size: xx-small;">size1</span> <span style="font-size: x-small;">size2</span> <span style="font-size: medium;">size4</span></p>
+<h3>h3</h3>
+<p>Paragraph <span style="text-decoration: underline;">underline</span> <span style="text-decoration: line-through;">strikethrough</span> <span style="color: #ff0000;">color</span> <span style="background-color: #ffff00; ">background</span></p>
+<blockquote>
+<p>Blockquoted paragraph</p>
+</blockquote>
+<p>Paragraph following blockquote</p>
+<ul><li>Unordered</li>
+ <li>List</li>
+</ul>
+<p>Paragraph between lists</p>
+<ol><li>Ordered</li>
+ <li>List</li>
+</ol>
+<p>Paragraph between lists</p>
+<ul><li>OL list</li>
+ <li>nested
+ <ol><li>inside a</li>
+ <li>UL list</li>
+ </ol></li>
+</ul>
+<p>Paragraph between lists</p>
+<table border="0"><tbody><tr></tr><tr><td>Table with</td>
+ <td></td>
+ </tr><tr><td></td>
+ <td>border=0</td>
+ </tr></tbody></table>
+<p>Paragraph</p>
+<ol><li>UL list</li>
+ <li>nested
+ <ul><li>inside a</li>
+ <li>OL list</li>
+ </ul></li>
+</ol>
+<p>Paragraph between tables</p>
+<table border="1" cellpadding="5"><tbody><tr><td>Table with border=1</td>
+ <td></td>
+ </tr><tr><td></td>
+ <td>cellpadding = 5</td>
+ </tr></tbody></table>
+<p>Paragraph between tables</p>
+<table border="2"><tbody><tr><td>Table with</td>
+ <td></td>
+ </tr><tr><td></td>
+ <td>border=2</td>
+ </tr></tbody></table> \ No newline at end of file
diff --git a/engine/tests/test_files/output/autop/wysiwyg-test.in.html b/engine/tests/test_files/output/autop/wysiwyg-test.in.html
new file mode 100644
index 000000000..733b0e2ec
--- /dev/null
+++ b/engine/tests/test_files/output/autop/wysiwyg-test.in.html
@@ -0,0 +1,79 @@
+&nbps;
+&#8820;
+<h1>h1</h1>
+Paragraph <a href="http://google.com/">link</a> <strong>Bold</strong> <em>italic</em> <em><strong>bolditalic</strong></em>&nbsp;<span style="background-color: #ffff00; "></span>
+<h2>h2</h2>
+Paragraph <span style="font-size: xx-small;">size1</span> <span style="font-size: x-small;">size2</span> <span style="font-size: medium;">size4</span>
+<h3>h3</h3>
+Paragraph <span style="text-decoration: underline;">underline</span> <span style="text-decoration: line-through;">strikethrough</span> <span style="color: #ff0000;">color</span> <span style="background-color: #ffff00; ">background</span>
+<blockquote>Blockquoted paragraph</blockquote>
+Paragraph following blockquote
+<ul>
+ <li>Unordered</li>
+ <li>List</li>
+</ul>
+Paragraph between lists
+<ol>
+ <li>Ordered</li>
+ <li>List</li>
+</ol>
+Paragraph between lists
+<ul>
+ <li>OL list</li>
+ <li>nested
+ <ol>
+ <li>inside a</li>
+ <li>UL list</li>
+ </ol></li>
+</ul>
+Paragraph between lists
+<table border="0">
+ <tbody>
+ <tr>
+ </tr>
+ <tr>
+ <td>Table with</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>border=0</td>
+ </tr>
+ </tbody>
+</table>
+Paragraph
+<ol>
+ <li>UL list</li>
+ <li>nested
+ <ul>
+ <li>inside a</li>
+ <li>OL list</li>
+ </ul>
+ </li>
+</ol>
+Paragraph between tables
+<table border="1" cellpadding="5">
+ <tbody>
+ <tr>
+ <td>Table with border=1</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>cellpadding = 5</td>
+ </tr>
+ </tbody>
+</table>
+Paragraph between tables
+<table border="2">
+ <tbody>
+ <tr>
+ <td>Table with</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>border=2</td>
+ </tr>
+ </tbody>
+</table> \ No newline at end of file
diff --git a/engine/tests/test_files/plugin_17/manifest.xml b/engine/tests/test_files/plugin_17/manifest.xml
new file mode 100644
index 000000000..706734265
--- /dev/null
+++ b/engine/tests/test_files/plugin_17/manifest.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin_manifest>
+ <field key="author" value="Anyone" />
+ <field key="version" value="1.0" />
+ <field key="description" value="A 1.7-style manifest." />
+ <field key="website" value="http://www.elgg.org/" />
+ <field key="copyright" value="(C) Elgg Foundation 2011" />
+ <field key="license" value="GNU General Public License version 2" />
+ <field key="elgg_version" value="2009030702" />
+</plugin_manifest> \ No newline at end of file
diff --git a/engine/tests/test_files/plugin_17/start.php b/engine/tests/test_files/plugin_17/start.php
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/engine/tests/test_files/plugin_17/start.php
diff --git a/engine/tests/test_files/plugin_18/manifest.xml b/engine/tests/test_files/plugin_18/manifest.xml
new file mode 100644
index 000000000..c8b407511
--- /dev/null
+++ b/engine/tests/test_files/plugin_18/manifest.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
+ <name>Test Manifest</name>
+ <author>Anyone</author>
+ <version>1.0</version>
+ <blurb>A concise description.</blurb>
+ <description>A longer, more interesting description.</description>
+ <website>http://www.elgg.org/</website>
+ <repository>https://github.com/Elgg/Elgg</repository>
+ <bugtracker>https://github.com/elgg/elgg/issues</bugtracker>
+ <donations>http://elgg.org/supporter.php</donations>
+ <copyright>(C) Elgg Foundation 2011</copyright>
+ <license>GNU General Public License version 2</license>
+
+ <requires>
+ <type>elgg_version</type>
+ <version>3009030802</version>
+ <comparison>lt</comparison>
+ </requires>
+
+ <requires>
+ <type>elgg_release</type>
+ <version>1.8-svn</version>
+ </requires>
+
+ <screenshot>
+ <description>Fun things to do 1</description>
+ <path>graphics/plugin_ss1.png</path>
+ </screenshot>
+
+ <screenshot>
+ <description>Fun things to do 2</description>
+ <path>graphics/plugin_ss2.png</path>
+ </screenshot>
+
+ <category>Admin</category>
+
+ <category>ServiceAPI</category>
+
+ <requires>
+ <type>php_extension</type>
+ <name>gd</name>
+ </requires>
+
+ <requires>
+ <type>php_ini</type>
+ <name>short_open_tag</name>
+ <value>off</value>
+ </requires>
+
+ <requires>
+ <type>php_extension</type>
+ <name>made_up</name>
+ <version>1.0</version>
+ </requires>
+
+ <requires>
+ <type>plugin</type>
+ <name>fake_plugin</name>
+ <version>1.0</version>
+ </requires>
+
+ <requires>
+ <type>plugin</type>
+ <name>profile</name>
+ <version>1.0</version>
+ </requires>
+
+ <requires>
+ <type>plugin</type>
+ <name>profile_api</name>
+ <version>1.3</version>
+ <comparison>lt</comparison>
+ </requires>
+
+ <requires>
+ <type>priority</type>
+ <priority>after</priority>
+ <plugin>profile</plugin>
+ </requires>
+
+ <conflicts>
+ <type>plugin</type>
+ <name>profile_api</name>
+ <version>1.0</version>
+ </conflicts>
+
+ <provides>
+ <type>plugin</type>
+ <name>profile_api</name>
+ <version>1.3</version>
+ </provides>
+
+ <provides>
+ <type>php_extension</type>
+ <name>big_math</name>
+ <version>1.0</version>
+ </provides>
+
+ <suggests>
+ <type>plugin</type>
+ <name>facebook_connect</name>
+ <version>1.0</version>
+ </suggests>
+
+ <activate_on_install>true</activate_on_install>
+
+</plugin_manifest>
diff --git a/engine/tests/test_files/plugin_18/start.php b/engine/tests/test_files/plugin_18/start.php
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/engine/tests/test_files/plugin_18/start.php
diff --git a/engine/tests/test_files/xxe/external_entity.txt b/engine/tests/test_files/xxe/external_entity.txt
new file mode 100644
index 000000000..536aca34d
--- /dev/null
+++ b/engine/tests/test_files/xxe/external_entity.txt
@@ -0,0 +1 @@
+secret \ No newline at end of file
diff --git a/engine/tests/test_files/xxe/request.xml b/engine/tests/test_files/xxe/request.xml
new file mode 100644
index 000000000..4390f9db2
--- /dev/null
+++ b/engine/tests/test_files/xxe/request.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<!DOCTYPE foo [
+<!ELEMENT methodName ANY >
+<!ENTITY xxe SYSTEM "%s" >
+]>
+<methodCall>
+ <methodName>test&xxe;test</methodName>
+</methodCall>
diff --git a/engine/tests/test_skeleton.php b/engine/tests/test_skeleton.php
index 474cffce0..5a5de89bb 100644
--- a/engine/tests/test_skeleton.php
+++ b/engine/tests/test_skeleton.php
@@ -5,7 +5,7 @@
* Plugin authors: copy this file to your plugin's test directory. Register an Elgg
* plugin hook and function similar to:
*
- * register_plugin_hook('unit_test', 'system', 'my_new_unit_test');
+ * elgg_register_plugin_hook_handler('unit_test', 'system', 'my_new_unit_test');
*
* function my_new_unit_test($hook, $type, $value, $params) {
* $value[] = "path/to/my/unit_test.php";
@@ -14,8 +14,6 @@
*
* @package Elgg
* @subpackage Test
- * @author Curverider Ltd
- * @link http://elgg.org/
*/
class ElggCoreSkeletonTest extends ElggCoreUnitTest {
@@ -51,9 +49,6 @@ class ElggCoreSkeletonTest extends ElggCoreUnitTest {
parent::__destruct();
}
- /**
- * A basic test that will be called and fail.
- */
public function testFailure() {
$this->assertTrue(FALSE);
}
diff --git a/engine/tests/ui/submenu.php b/engine/tests/ui/submenu.php
deleted file mode 100644
index 745364890..000000000
--- a/engine/tests/ui/submenu.php
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-/**
- * 1.8 submenu test.
- *
- * Submenu needs to be able to support being added out of order.
- * Children can be added before parents.
- * Children of parents never defined are never shown.
- *
- * Test against:
- * different contexts
- * different groups
- * old add_submenu_item() wrapper.
- *
- */
-
-require_once('../../start.php');
-
-$url = "{$CONFIG->url}engine/tests/ui/submenu.php";
-
-$items = array(
- array(
- 'text' => 'Upper level 1',
- 'href' => "$url?upper_level_1",
- 'id' => 'ul1'
- ),
- array(
- 'text' => 'CD (No link)',
- 'parent_id' => 'cup',
- 'id' => 'cd',
- ),
- array(
- 'text' => 'Sub CD',
- 'href' => "$url?sub_cd",
- 'parent_id' => 'cd'
- ),
- array(
- 'text' => 'Cup',
- 'href' => "$url?cup",
- 'id' => 'cup'
- ),
- array(
- 'text' => 'Phone',
- 'href' => "$url?phone",
- 'id' => 'phone',
- 'parent_id' => 'cup'
- ),
- array(
- 'text' => 'Wallet',
- 'href' => "$url?wallet",
- 'id' => 'wallet',
- 'parent_id' => 'phone'
- ),
- array(
- 'text' => 'Upper level',
- 'href' => "$url?upper_level",
- 'id' => 'ul'
- ),
- array(
- 'text' => 'Sub Upper level',
- 'href' => "$url?sub_upper_level",
- 'parent_id' => 'ul'
- ),
- array(
- 'text' => 'Root',
- 'href' => $url,
- ),
-
- array(
- 'text' => 'I am an orphan',
- 'href' => 'http://google.com',
- 'parent_id' => 'missing_parent'
- ),
-
- array(
- 'text' => 'JS Test',
- 'href' => 'http://elgg.org',
- 'vars' => array('js' => 'onclick="alert(\'Link to \' + $(this).attr(\'href\') + \'!\'); return false;"')
- )
-);
-
-foreach ($items as $item) {
- elgg_add_submenu_item($item, 'main');
-}
-
-add_submenu_item('Old Onclick Test', 'http://elgg.com', NULL, TRUE);
-add_submenu_item('Old Selected Test', 'http://elgg.com', NULL, '', TRUE);
-
-
-elgg_add_submenu_item(array('text' => 'Not Main Test', 'href' => "$url?not_main_test"), 'not_main', 'new_menu');
-elgg_add_submenu_item(array('text' => 'Not Main C Test', 'href' => "$url?not_main_c_test"), 'not_main', 'new_menu');
-
-elgg_add_submenu_item(array('text' => 'All test', 'href' => "$url?all"), 'all');
-
-//set_context('not_main');
-
-$body = elgg_view_layout('one_column_with_sidebar', 'Look right.');
-page_draw('Submenu Test', $body);