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

Commit

Permalink
Merge pull request #483 from Gizra/og-group-member-list
Browse files Browse the repository at this point in the history
Add a cache tag to invalidate a group's user membership listings
  • Loading branch information
pfrenssen authored Apr 30, 2019
2 parents a68f578 + f2d4a1b commit 9d849da
Show file tree
Hide file tree
Showing 8 changed files with 646 additions and 1 deletion.
11 changes: 11 additions & 0 deletions config/schema/og.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,14 @@ condition.plugin.og_group_type:
type: sequence
sequence:
type: string

block.settings.og_member_count:
type: block_settings
label: 'Group member count block'
mapping:
count_blocked_users:
type: boolean
label: 'Count blocked users'
count_pending_users:
type: boolean
label: 'Count pending users'
32 changes: 32 additions & 0 deletions og.module
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,38 @@ function og_entity_type_alter(array &$entity_types) {
}
}

/**
* Implements hook_theme().
*/
function og_theme($existing, $type, $theme, $path) {
return [
'og_member_count' => [
'variables' => [
'count' => 0,
'membership_states' => [],
'group' => NULL,
],
],
];
}

/**
* Prepares variables for member count templates.
*
* Default template: og-member-count.html.twig.
*
* @param array $variables
* An associative array containing:
* - count: An integer representing the number of members in the group.
* - group: The group, which is a content entity.
*/
function template_preprocess_og_member_count(array &$variables) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $group */
$group = $variables['group'];

$variables['group_label'] = $group->label();
}

/**
* Invalidates group content cache tags for the groups this entity belongs to.
*
Expand Down
37 changes: 37 additions & 0 deletions src/Entity/OgMembership.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Drupal\og\Entity;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
Expand Down Expand Up @@ -482,6 +483,42 @@ public function save() {
return $result;
}

/**
* {@inheritdoc}
*/
protected function invalidateTagsOnSave($update) {
parent::invalidateTagsOnSave($update);

// A membership was created or updated: invalidate the membership list cache
// tags of its group. An updated membership may start to appear in a group's
// membership listings because it now meets those listings' filtering
// requirements. A newly created membership may start to appear in listings
// because it did not exist before.
$group = $this->getGroup();
if (!empty($group)) {
$tags = Cache::buildTags(OgMembershipInterface::GROUP_MEMBERSHIP_LIST_CACHE_TAG_PREFIX, $group->getCacheTagsToInvalidate());
Cache::invalidateTags($tags);
}
}

/**
* {@inheritdoc}
*/
protected static function invalidateTagsOnDelete(EntityTypeInterface $entity_type, array $entities) {
parent::invalidateTagsOnDelete($entity_type, $entities);

// A membership was deleted: invalidate the list cache tags of its group
// membership lists, so that any lists that contain the membership will be
// recalculated.
$tags = [];
foreach ($entities as $entity) {
if ($group = $entity->getGroup()) {
$tags = Cache::mergeTags(Cache::buildTags(OgMembershipInterface::GROUP_MEMBERSHIP_LIST_CACHE_TAG_PREFIX, $group->getCacheTagsToInvalidate()), $tags);
}
}
Cache::invalidateTags($tags);
}

/**
* {@inheritdoc}
*/
Expand Down
2 changes: 1 addition & 1 deletion src/MembershipManagerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public function getGroups(EntityInterface $entity, $group_type_id = NULL, $group
* Returns the number of groups associated with a given group content entity.
*
* Do not use this to retrieve the group membership count for a user entity.
* Use count(Og::GetEntityGroups()) instead.
* Use count(\Drupal\og\MembershipManager::getUserGroupIds()) instead.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The group content entity for which to count the associated groups.
Expand Down
5 changes: 5 additions & 0 deletions src/OgMembershipInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ interface OgMembershipInterface extends ContentEntityInterface, EntityOwnerInter
*/
const REQUEST_FIELD = 'og_membership_request';

/**
* The prefix that is used to identify group membership list cache tags.
*/
const GROUP_MEMBERSHIP_LIST_CACHE_TAG_PREFIX = 'og-group-membership-list';

/**
* Gets the membership creation timestamp.
*
Expand Down
166 changes: 166 additions & 0 deletions src/Plugin/Block/MemberCountBlock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

namespace Drupal\og\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\og\MembershipManagerInterface;
use Drupal\og\OgContextInterface;
use Drupal\og\OgMembershipInterface;
use Drupal\og\OgRoleInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Provides a block that shows the number of members in the current group.
*
* This block is mainly intended to demonstrate the group membership list cache
* tag but can also be used to show the number of members on group pages. The
* way the text is displayed can be changed by overriding the Twig template.
*
* @Block(
* id = "og_member_count",
* admin_label = @Translation("Group member count")
* )
*/
class MemberCountBlock extends BlockBase implements ContainerFactoryPluginInterface {

/**
* The OG context provider.
*
* @var \Drupal\og\OgContextInterface
*/
protected $ogContext;

/**
* The membership manager.
*
* @var \Drupal\og\MembershipManagerInterface
*/
protected $membershipManager;

/**
* Constructs a MemberCountBlock object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param string $plugin_definition
* The plugin implementation definition.
* @param \Drupal\og\OgContextInterface $og_context
* The OG context provider.
* @param \Drupal\og\MembershipManagerInterface $membership_manager
* The membership manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, OgContextInterface $og_context, MembershipManagerInterface $membership_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);

$this->ogContext = $og_context;
$this->membershipManager = $membership_manager;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('og.context'),
$container->get('og.membership_manager')
);
}

/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'count_blocked_users' => FALSE,
'count_pending_users' => FALSE,
];
}

/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$form['count_blocked_users'] = [
'#type' => 'checkbox',
'#title' => $this->t('Count blocked users'),
'#default_value' => $this->configuration['count_blocked_users'],
];

$form['count_pending_users'] = [
'#type' => 'checkbox',
'#title' => $this->t('Count pending users'),
'#default_value' => $this->configuration['count_pending_users'],
];

return $form;
}

/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
foreach (array_keys($this->defaultConfiguration()) as $setting) {
$this->configuration[$setting] = $form_state->getValue($setting);
}
}

/**
* {@inheritdoc}
*/
public function build() {
// Do not render anything if there is no group in the current context.
$group = $this->ogContext->getGroup();
if (empty($group)) {
return [];
}

$states = [OgMembershipInterface::STATE_ACTIVE];

if ($this->configuration['count_blocked_users']) {
$states[] = OgMembershipInterface::STATE_BLOCKED;
}

if ($this->configuration['count_pending_users']) {
$states[] = OgMembershipInterface::STATE_PENDING;
}

$membership_ids = $this->membershipManager->getGroupMembershipIdsByRoleNames($group, [OgRoleInterface::AUTHENTICATED], $states);

return [
'#theme' => 'og_member_count',
'#count' => count($membership_ids),
'#group' => $group,
'#membership_states' => $states,
];
}

/**
* {@inheritdoc}
*/
public function getCacheTags() {
$tags = parent::getCacheTags();

$group = $this->ogContext->getGroup();
if (!empty($group)) {
$tags = Cache::mergeTags(Cache::buildTags(OgMembershipInterface::GROUP_MEMBERSHIP_LIST_CACHE_TAG_PREFIX, $group->getCacheTagsToInvalidate()), $tags);
}

return $tags;
}

/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return Cache::mergeContexts(parent::getCacheContexts(), ['og_group_context']);
}

}
22 changes: 22 additions & 0 deletions templates/og-member-count.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{#
/**
* @file
* Default theme implementation to show the member count of a group.
*
* Available variables:
* - count: The number of members in the group.
* - membership_states: An array of membership states included in the count.
* - group_label: The group label.
*
* @see \Drupal\og\Plugin\Block\MemberCountBlock
*
* @ingroup themeable
*/
#}
<p>
{% trans %}
{{ group_label }} has 1 member.
{% plural count %}
{{ group_label }} has {{ count }} members.
{% endtrans %}
</p>
Loading

0 comments on commit 9d849da

Please sign in to comment.