Skip to content
This repository was archived by the owner on Aug 18, 2024. It is now read-only.

[WIP] OG Access port #242

Closed
wants to merge 44 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1bfdfe9
Initial port
zerolab Mar 2, 2017
18ab411
Cleanup
zerolab Mar 2, 2017
72451e1
Add remove access fields on entity group/group content config changes
zerolab Mar 2, 2017
f08fe78
Remove install step
zerolab Mar 13, 2017
3a77001
Use core method for checking if node is published
zerolab Mar 13, 2017
5e1436c
Add access field toggle in the bundle form
zerolab Mar 13, 2017
5a801ab
Fix og_access realm (#1)
svendecabooter May 15, 2017
690235a
Fix crash on add forms for bundles of content entities that implement…
joachim-n Sep 21, 2017
d3c36c3
Fixup! Fix crash on add forms for bundles of content entities that im…
joachim-n Sep 21, 2017
f1dc41b
Lint
zerolab Sep 22, 2017
98f0c38
Fix bundle form alter class filename
zerolab Nov 16, 2017
202c31c
#242 Improvements for og_access port. (#2)
tatarbj Jul 4, 2018
ac43c9d
Update og_access/og_access.info.yml
MPParsley Mar 26, 2019
4cf5b4b
Update og_access/src/Plugin/OgFields/OgAccessField.php
MPParsley Mar 26, 2019
66cbcbf
Update og_access/src/Plugin/OgFields/OgContentAccessField.php
MPParsley Mar 26, 2019
50fde53
Fixed fatal error StringTranslationTrait not found
MPParsley Apr 9, 2019
40acefa
Fixed ArgumentCountError: Too few arguments for OgAccessBundleFormAlt…
MPParsley Apr 9, 2019
645f40a
Update og_access/src/Plugin/OgFields/OgContentAccessField.php
MPParsley Apr 9, 2019
babf336
Update og_access/src/Plugin/OgFields/OgContentAccessField.php
MPParsley Apr 9, 2019
71f636f
Update og_access/src/Plugin/OgFields/OgAccessField.php
MPParsley Apr 9, 2019
7e0b216
Update og_access/src/Plugin/OgFields/OgContentAccessField.php
MPParsley Apr 9, 2019
eb548eb
Update og_access/src/Plugin/OgFields/OgAccessField.php
MPParsley Apr 9, 2019
4ab462a
Rebuild node access permissions (#3)
MPParsley Apr 18, 2019
3b63ae7
Update og_access/og_access.module
MPParsley Aug 9, 2019
60197b6
Update og_access/og_access.module
MPParsley Aug 21, 2019
1342e72
Merge pull request #7 from Gizra/8.x-1.x
MPParsley Sep 10, 2019
29de2b3
Fixed deprecation
MPParsley Sep 10, 2019
4ed5950
Merge pull request #8 from Gizra/8.x-1.x
pfrenssen Jan 29, 2020
dc3eb79
Merge branch '8.x-1.x' into og_access
MPParsley Jul 20, 2020
39925fb
Merge branch '8.x-1.x' into og_access
MPParsley Aug 4, 2020
14f23d3
Merge branch '8.x-1.x' into og_access
MPParsley Aug 10, 2020
1ab9466
Add strict types
MPParsley Aug 10, 2020
67dd034
Merge branch '8.x-1.x' into og_access
MPParsley Jan 19, 2021
127db3a
Apply suggestions from code review
MPParsley Jan 19, 2021
ebace9d
Merge branch '8.x-1.x' into og_access
MPParsley Apr 20, 2021
57f47d0
Update og_access/og_access.module
MPParsley Apr 20, 2021
e635a75
Add support for Drupal 9
MPParsley Apr 22, 2021
a8389e9
Merge branch '8.x-1.x' into og_access
MPParsley Apr 22, 2021
d59a0ee
Update og_access/og_access.module
MPParsley Apr 22, 2021
d64c11d
Merge branch '8.x-1.x' into og_access
MPParsley Sep 15, 2021
246aadd
Merge branch '8.x-1.x' into og_access
MPParsley Sep 30, 2021
208c0a3
Merge branch '8.x-1.x' into og_access
MPParsley Feb 11, 2022
fec3974
Merge branch '8.x-1.x' into og_access
MPParsley Aug 25, 2022
2887506
Merge branch '8.x-1.x' into og_access
MPParsley Aug 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions og_access/og_access.info.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: Organic Groups access control
description: "Enable access control for private and public groups and group content."
package: Organic Groups

core: 8.x
type: module

dependencies:
- og
- og_ui
239 changes: 239 additions & 0 deletions og_access/og_access.module
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
<?php

/**
* @file
* Enable access control for private and public groups and group content.
*
* @TODO handle visibility change
* @TODO set group content visibility default to that of the group
* @TODO move grants/access to service
*/

use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
use Drupal\Core\Entity\BundleEntityFormBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\node\NodeInterface;
use Drupal\og\Og;
use Drupal\og_access\OgAccessBundleFormAlter;

/**
* The access realm of group member.
*/
define('OG_ACCESS_REALM', 'og_access');

/**
* Group public access field.
*/
define('OG_ACCESS_FIELD', 'group_access');

/**
* Group public access field.
*/
define('OG_ACCESS_CONTENT_FIELD', 'group_content_access');

/**
* Public group/group content access.
*/
define('OG_ACCESS_PUBLIC', 0);

/**
* Private group/group content access.
*/
define('OG_ACCESS_PRIVATE', 1);

/**
* Implements hook_node_grants().
*/
function og_access_node_grants(AccountInterface $account, $op) {
if ($op != 'view') {
return [];
}

/** @var \Drupal\og\MembershipManager $membership_manager */
$membership_manager = \Drupal::service('og.membership_manager');
if ($groups = $membership_manager->getUserGroups($account)) {
foreach ($groups as $group_type => $entity_groups) {
/** @var \Drupal\core\Entity\EntityInterface $group */
foreach ($entity_groups as $group) {
$realm = OG_ACCESS_REALM . ':' . $group_type;
$grants[$realm][] = $group->id();
}
}
}

return !empty($grants) ? $grants : [];
}

/**
* Implements hook_node_access_records().
*/
function og_access_node_access_records(NodeInterface $node) {
if (!$node->isPublished()) {
// Node is unpublished, so we don't allow every group member to see it.
return [];
}

// The group IDs, that in case access is granted, will be recorded.
$gids = [];

if (Og::isGroup('node', $node->getType()) &&
$node->hasField(OG_ACCESS_FIELD) &&
!empty($node->{OG_ACCESS_FIELD}) && $node->{OG_ACCESS_FIELD}->value) {
// Private group.
$gids['node'][] = $node->id();
}

if ($node->hasField(OG_ACCESS_CONTENT_FIELD) &&
!empty($node->get(OG_ACCESS_CONTENT_FIELD))) {
$content_access = $node->get(OG_ACCESS_CONTENT_FIELD)->value;
}
else {
$content_access = OG_ACCESS_PUBLIC;
}

switch ($content_access) {
case OG_ACCESS_PUBLIC:
// Skip non-group content nodes.
if (!Og::isGroupContent('node', $node->getType())) {
break;
}

$has_private = FALSE;
/** @var \Drupal\og\OgGroupAudienceHelper $audience_helper */
$audience_helper = \Drupal::service('og.group_audience_helper');
foreach ($audience_helper->getAllGroupAudienceFields('node', $node->getType()) as $field_name => $field) {
foreach ($node->get($field_name)->referencedEntities() as $group) {
$list_gids[$group->getEntityTypeId()][] = $group->id();

if ($has_private) {
// We already know we have a private group, so we can avoid
// re-checking it.
continue;
}

if ($group->hasField(OG_ACCESS_FIELD) && !empty($group->get(OG_ACCESS_FIELD)) &&
$group->get(OG_ACCESS_FIELD)->value) {
$has_private = TRUE;
}
}
}
if ($has_private) {
$gids = array_merge_recursive($gids, $list_gids);
}
break;

case OG_ACCESS_PRIVATE:
$list_gids = [];
/** @var \Drupal\og\OgGroupAudienceHelper $audience_helper */
$audience_helper = \Drupal::service('og.group_audience_helper');
foreach ($audience_helper->getAllGroupAudienceFields('node', $node->getType()) as $field_name => $field) {
foreach ($node->get($field_name)->referencedEntities() as $group) {
$list_gids[$group->getEntityTypeId()][] = $group->id();
}
}

$gids = array_merge_recursive($gids, $list_gids);
break;
}

foreach ($gids as $group_type => $values) {
foreach ($values as $gid) {
$grants[] = [
'realm' => OG_ACCESS_REALM . ':' . $group_type,
'gid' => $gid,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
];
}
}

return !empty($grants) ? $grants : [];
}

/**
* Implements hook_form_alter().
*/
function og_access_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
if ($form_state->getFormObject() instanceof BundleEntityFormBase) {
(new OgAccessBundleFormAlter($form_state->getFormObject()->getEntity()))
->formAlter($form, $form_state);
}
}

/**
* Implements hook_entity_insert().
*/
function og_access_entity_insert(EntityInterface $entity) {
og_access_entity_type_save($entity);
}

/**
* Implements hook_entity_update().
*/
function og_access_entity_update(EntityInterface $entity) {
og_access_entity_type_save($entity);
}

/**
* Adds/removes the group and group content access fields.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity object.
*/
function og_access_entity_type_save(EntityInterface $entity) {
if (!$entity instanceof ConfigEntityBundleBase || !isset($entity->og_is_group)) {
return;
}

$bundle = $entity->id();
$definition = \Drupal::entityTypeManager()->getDefinition($entity->getEntityTypeId());
$entity_type_id = $definition->getBundleOf();

$enable_og_access = $entity->og_enable_access;

// Add/remove on the group itself.
$is_group = Og::isGroup($entity_type_id, $bundle);
if ($entity->og_is_group || $is_group) {
$field = FieldConfig::loadByName($entity_type_id, $bundle, OG_ACCESS_FIELD);
if (!$field && $enable_og_access) {
Og::createField(OG_ACCESS_FIELD, $entity_type_id, $bundle);
}
elseif ($field) {
if (!$enable_og_access || $is_group && !$entity->og_is_group) {
$field->delete();
}
}
}

// Add remove the relevant field to the group content bundle.
$is_group_content = Og::isGroupContent($entity_type_id, $bundle);
if ($entity->og_group_content_bundle || $is_group_content) {
$field = FieldConfig::loadByName($entity_type_id, $bundle, OG_ACCESS_CONTENT_FIELD);

if (!$field && $enable_og_access) {
Og::createField(OG_ACCESS_CONTENT_FIELD, $entity_type_id, $bundle);
}
elseif ($field) {
if (!$enable_og_access || $is_group_content && !$entity->og_group_content_bundle) {
$field->delete();
}
}
}
}

/**
* Implements hook_module_implements_alter().
*/
function og_access_module_implements_alter(&$implementations, $hook) {
if ($hook == 'form_alter') {
// Move our form alter after the og_ui one.
// @TODO remove once og_ui and og are merged.
$group = $implementations['og_access'];
unset($implementations['og_access']);
$implementations['og_access'] = $group;
}
}
97 changes: 97 additions & 0 deletions og_access/src/OgAccessBundleFormAlter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

namespace Drupal\og_access;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\og\Og;

/**
* Helper for og_access_form_alter().
*/
class OgAccessBundleFormAlter {

/**
* The entity bundle.
*
* @var string
*/
protected $bundle;

/**
* The entity type ID.
*
* @var string
*/
protected $entityTypeId;

/**
* The form entity which has been used for populating form element defaults.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $entity;

/**
* Construct a BundleFormAlter object.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity object.
*/
public function __construct(EntityInterface $entity) {
$this->entity = $entity;
}

/**
* This is a helper for og_ui_form_alter().
*
* @param array $form
* The form variable.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state object.
*/
public function formAlter(array &$form, FormStateInterface $form_state) {
// Example: node.
$this->entityTypeId = $this->entity->getEntityType()->getBundleOf();

// Example: article.
$this->bundle = $this->entity->id();

$form['og']['og_enable_access'] = [
'#type' => 'checkbox',
'#title' => t('Restrict access to group members'),
'#description' => t('Enable OG access control. Provides a new field that determines the group/group content visibility. Public groups can have member-only content. Any public group content belonging to a private group will be restricted to the members of that group only.'),
'#default_value' => $this->bundle ? $this->hasAccessControl() : FALSE,
'#states' => [
'visible' => [
[':input[name="og_is_group"]' => ['checked' => TRUE]],
[':input[name="og_group_content_bundle"]' => ['checked' => TRUE]],
],
],
];
}

/**
* Checks whether the existing bundle has OG access control enabled.
*
* @return bool
* True if the group bundle has the OG_ACCESS_FIELD field -OR-
* if the group content bundle has the OG_CONTENT_ACCESS_FIELD field.
* False otherwise.
*/
protected function hasAccessControl() {
$field_definitions = \Drupal::service('entity_field.manager')
->getFieldDefinitions($this->entityTypeId, $this->bundle);

if (Og::isGroup($this->entityTypeId, $this->bundle)) {
return isset($field_definitions[OG_ACCESS_FIELD]);
}

if (Og::isGroupContent($this->entityTypeId, $this->bundle)) {
return isset($field_definitions[OG_ACCESS_CONTENT_FIELD]);
}

return FALSE;
}

}
Loading