Skip to content

Commit

Permalink
Merge pull request #14206 from jeabakker/features
Browse files Browse the repository at this point in the history
Features
  • Loading branch information
jeabakker committed Nov 4, 2022
2 parents 6fea5ba + 4048eee commit 77487ed
Show file tree
Hide file tree
Showing 34 changed files with 535 additions and 316 deletions.
11 changes: 3 additions & 8 deletions actions/diagnostics/download.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@
$output = elgg_echo('diagnostics:header', [date('r'), elgg_get_logged_in_user_entity()->getDisplayName()]);
$output = elgg_trigger_event_results('diagnostics:report', 'system', [], $output);

header("Cache-Control: public");
header("Content-Description: File Transfer");
header('Content-disposition: attachment; filename=elggdiagnostic.txt');
header("Content-Type: text/plain");
header('Content-Length: ' . strlen($output));

echo $output;
exit();
return elgg_download_response($output, 'elggdiagnostic.txt', false, [
'Content-Type' => 'text/plain; charset=utf-8',
]);
2 changes: 2 additions & 0 deletions docs/appendix/upgrade-notes/4.x-to-5.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,8 @@ Removed functions
Removed events
~~~~~~~~~~~~~~

* ``access:collections:addcollection, collection`` use the ``create, access_collection`` sequence
* ``access:collections:deletecollection, collection`` use the ``delete, access_collection`` sequence
* ``prepare, breadcrumbs`` use ``register, menu:breadcrumbs``

Constants
Expand Down
29 changes: 19 additions & 10 deletions docs/guides/events-list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,9 @@ Access events
**access:collections:read, user** |results|
Filters an array of access IDs that the user ``$params['user_id']`` can see.

.. warning:: The handler needs to either not use parts of the API that use the access system (triggering the event again) or to ignore the second call. Otherwise, an infinite loop will be created.
.. warning::
The handler needs to either not use parts of the API that use the access system (triggering the event again) or
to ignore the second call. Otherwise, an infinite loop will be created.

**access:collections:write, user** |results|
Filters an array of access IDs that the user ``$params['user_id']`` can write to. In
Expand All @@ -481,19 +483,14 @@ Access events
"container_guid" (int) are provided. An empty entity value generally means the form is to
create a new object.

.. warning:: The handler needs to either not use parts of the API that use the access system (triggering the event again) or to ignore the second call. Otherwise, an infinite loop will be created.
.. warning::
The handler needs to either not use parts of the API that use the access system (triggering the event again) or
to ignore the second call. Otherwise, an infinite loop will be created.

**access:collections:write:subtypes, user** |results|
Returns an array of access collection subtypes to be used when retrieving access collections owned by a user as part of
the ``elgg_get_write_access_array()`` function.

**access:collections:addcollection, collection** |results|
Triggered after an access collection ``$params['collection_id']`` is created.

**access:collections:deletecollection, collection** |results|
Triggered before an access collection ``$params['collection_id']`` is deleted.
Return false to prevent deletion.

**access:collections:add_user, collection** |results|
Triggered before adding user ``$params['user_id']`` to collection ``$params['collection_id']``.
Return false to prevent adding.
Expand All @@ -502,10 +499,19 @@ Access events
Triggered before removing user ``$params['user_id']`` to collection ``$params['collection_id']``.
Return false to prevent removal.

**create, access_collection** |sequence|
Triggered during the creation of an ``ElggAccessCollection``.

**delete, access_collection** |sequence|
Triggered during the deletion of an ``ElggAccessCollection``.

**get_sql, access** |results|
Filters SQL clauses restricting/allowing access to entities and annotations.

**The event is triggered regardless if the access is ignored**. The handlers may need to check if access is ignored and return early, if appended clauses should only apply to access controlled contexts.
.. note::
**The event is triggered regardless if the access is ignored**.
The handlers may need to check if access is ignored and return early, if appended clauses should only apply to
access controlled contexts.

``$return`` value is a nested array of ``ands`` and ``ors``.

Expand All @@ -519,6 +525,9 @@ Access events
* ``guid_column`` - column in the main table referencing the GUID of the entity
* ``enabled_column`` - column in the main table referencing the enabled status of the entity
* ``query_builder`` - an instance of the ``QueryBuilder``

**update, access_collection** |sequence|
Triggered during the update of an ``ElggAccessCollection``.

.. _guides/events-list#permissions:

Expand Down
209 changes: 65 additions & 144 deletions engine/classes/Elgg/Database/AccessCollections.php
Original file line number Diff line number Diff line change
Expand Up @@ -407,180 +407,101 @@ public function canEdit(int $collection_id, int $user_guid = null) {
* Access colletions allow plugins and users to create granular access
* for entities.
*
* Triggers event 'access:collections:addcollection', 'collection'
* Triggers event sequence 'create', 'access_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 string $subtype The subtype indicates the usage of the acl
* @param \ElggAccessCollection $acl the access collection to create
*
* @return int|false The collection ID if successful and false on failure.
* @return bool
*/
public function create(string $name, int $owner_guid = 0, string $subtype = null): ?int {
$name = trim($name);
if (empty($name)) {
return null;
public function create(\ElggAccessCollection $acl): bool {
if (!empty($acl->id)) {
return $this->update($acl);
}

if (isset($subtype)) {
$subtype = trim($subtype);
if (strlen($subtype) > 255) {
$this->getLogger()->error("The subtype length for access collections cannot be greater than 255");
return null;

return $this->events->triggerSequence('create', $acl->getType(), $acl, function (\ElggAccessCollection $acl) {
$insert = Insert::intoTable(self::TABLE_NAME);
$insert->values([
'name' => $insert->param($acl->name, ELGG_VALUE_STRING),
'subtype' => $insert->param($acl->subtype, ELGG_VALUE_STRING),
'owner_guid' => $insert->param($acl->owner_guid, ELGG_VALUE_GUID),
]);

$id = $this->db->insertData($insert);
if (empty($id)) {
return false;
}
}

if ($owner_guid === 0) {
$owner_guid = $this->session->getLoggedInUserGuid();
}

$insert = Insert::intoTable(self::TABLE_NAME);
$insert->values([
'name' => $insert->param($name, ELGG_VALUE_STRING),
'subtype' => $insert->param($subtype, ELGG_VALUE_STRING),
'owner_guid' => $insert->param($owner_guid, ELGG_VALUE_GUID),
]);

$id = $this->db->insertData($insert);
if (empty($id)) {
return null;
}

$this->access_cache->clear();

$event_params = [
'collection_id' => $id,
'name' => $name,
'subtype' => $subtype,
'owner_guid' => $owner_guid,
];

// @todo https://github.com/Elgg/Elgg/issues/10823
if (!$this->events->triggerResults('access:collections:addcollection', 'collection', $event_params, true)) {
$this->delete($id);
return null;
}

return $id;
}

/**
* Renames an access collection
*
* @param int $collection_id ID of the collection
* @param string $name The name of the collection
*
* @return bool
*/
public function rename(int $collection_id, string $name): bool {

$update = Update::table(self::TABLE_NAME);
$update->set('name', $update->param($name, ELGG_VALUE_STRING))
->where($update->compare('id', '=', $collection_id, ELGG_VALUE_ID));

if ($this->db->updateData($update, true)) {

$acl->id = $id;

$this->access_cache->clear();

return true;
}

return false;
});
}

/**
* Updates the membership in an access collection.
*
* @warning Expects a full list of all members that should
* be part of the access collection
* Update an existing access collection
*
* @note This will run all events associated with adding or removing
* members to access collections.
* @param \ElggAccessCollection $acl the access collection to update
*
* @param int $collection_id ID of the collection.
* @param array $new_members Array of member entities or GUIDs
* @return bool
*/
public function update($collection_id, array $new_members = []) {
$acl = $this->get($collection_id);

if (!$acl instanceof \ElggAccessCollection) {
return false;
public function update(\ElggAccessCollection $acl): bool {
if (empty($acl->id)) {
return $this->create($acl);
}

$to_guid = function($elem) {
if (empty($elem)) {
return 0;
}
if (is_object($elem)) {
return (int) $elem->guid;

return $this->events->triggerSequence('update', $acl->getType(), $acl, function (\ElggAccessCollection $acl) {
$update = Update::table(self::TABLE_NAME);
$update->set('name', $update->param($acl->name, ELGG_VALUE_STRING))
->set('subtype', $update->param($acl->subtype, ELGG_VALUE_STRING))
->set('owner_guid', $update->param($acl->owner_guid, ELGG_VALUE_GUID))
->where($update->compare('id', '=', $acl->id, ELGG_VALUE_ID));

if (!$this->db->updateData($update)) {
return false;
}
return (int) $elem;
};

$current_members = [];
$new_members = array_map($to_guid, $new_members);

$current_members_batch = $this->getMembers($collection_id, [
'batch' => true,
'limit' => false,
'callback' => false,
]);

foreach ($current_members_batch as $row) {
$current_members[] = $to_guid($row);
}

$remove_members = array_diff($current_members, $new_members);
$add_members = array_diff($new_members, $current_members);

$result = true;

foreach ($add_members as $guid) {
$result = $result && $this->addUser($guid, $collection_id);
}

foreach ($remove_members as $guid) {
$result = $result && $this->removeUser($guid, $collection_id);
}

$this->access_cache->clear();

return $result;

$this->access_cache->clear();

return true;
});
}

/**
* Deletes a collection and its membership information
*
* @param int $collection_id ID of the collection
* @param \ElggAccessCollection $acl the access collection to update
*
* @return bool
*/
public function delete(int $collection_id): bool {
$params = [
'collection_id' => $collection_id,
];

// @todo https://github.com/Elgg/Elgg/issues/10823
if (!$this->events->triggerResults('access:collections:deletecollection', 'collection', $params, true)) {
public function delete(\ElggAccessCollection $acl): bool {
if (empty($acl->id)) {
return false;
}

// Deleting membership doesn't affect result of deleting ACL.
$delete_membership = Delete::fromTable(self::MEMBERSHIP_TABLE_NAME);
$delete_membership->where($delete_membership->compare('access_collection_id', '=', $collection_id, ELGG_VALUE_ID));

$this->db->deleteData($delete_membership);

$delete = Delete::fromTable(self::TABLE_NAME);
$delete->where($delete->compare('id', '=', $collection_id, ELGG_VALUE_ID));

$result = $this->db->deleteData($delete);

$this->access_cache->clear();

return (bool) $result;
return $this->events->triggerSequence('delete', $acl->getType(), $acl, function (\ElggAccessCollection $acl) {
$delete = Delete::fromTable(self::TABLE_NAME);
$delete->where($delete->compare('id', '=', $acl->id, ELGG_VALUE_ID));

if (!$this->db->deleteData($delete)) {
return false;
}

// cleanup access collection membership (doesn't affect the result of deleting the ACL)
$delete_membership = Delete::fromTable(self::MEMBERSHIP_TABLE_NAME);
$delete_membership->where($delete_membership->compare('access_collection_id', '=', $acl->id, ELGG_VALUE_ID));

$this->db->deleteData($delete_membership);

// clear cache
$this->access_cache->clear();

return true;
});
}

/**
Expand Down

0 comments on commit 77487ed

Please sign in to comment.