social_group.module in Open Social 10.0.x
Same filename and directory in other branches
- 8.9 modules/social_features/social_group/social_group.module
- 8 modules/social_features/social_group/social_group.module
- 8.2 modules/social_features/social_group/social_group.module
- 8.3 modules/social_features/social_group/social_group.module
- 8.4 modules/social_features/social_group/social_group.module
- 8.5 modules/social_features/social_group/social_group.module
- 8.6 modules/social_features/social_group/social_group.module
- 8.7 modules/social_features/social_group/social_group.module
- 8.8 modules/social_features/social_group/social_group.module
- 10.3.x modules/social_features/social_group/social_group.module
- 10.1.x modules/social_features/social_group/social_group.module
- 10.2.x modules/social_features/social_group/social_group.module
The Social group module.
File
modules/social_features/social_group/social_group.moduleView source
<?php
/**
* @file
* The Social group module.
*/
use Drupal\block\Entity\Block;
use Drupal\bootstrap\Bootstrap;
use Drupal\Component\Utility\Html;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultNeutral;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\group\Entity\GroupContentInterface;
use Drupal\group\Entity\GroupContentType;
use Drupal\group\Entity\GroupInterface;
use Drupal\group\Entity\GroupContent;
use Drupal\group\Entity\GroupType;
use Drupal\node\NodeInterface;
use Drupal\social_group\Controller\SocialGroupController;
use Drupal\social_group\Controller\SocialGroupListBuilder;
use Drupal\social_group\Element\SocialGroupEntityAutocomplete;
use Drupal\social_group\Entity\Access\SocialGroupAccessControlHandler;
use Drupal\social_group\Entity\Group;
use Drupal\social_group\Form\SocialGroupAddForm;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\cache\CachePluginBase;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\Plugin\views\row\EntityRow;
use Drupal\Core\Url;
use Drupal\Core\Cache\Cache;
use Drupal\image\Entity\ImageStyle;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\social_group\GroupContentVisibilityUpdate;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\views_bulk_operations\ViewsBulkOperationsBatch;
/**
* Implements hook_theme().
*/
function social_group_theme() {
return [
'group_settings_help' => [
'variables' => [
'group_type' => NULL,
'join_method' => NULL,
'allowed_visibility' => NULL,
],
],
];
}
/**
* Implements hook_entity_type_alter().
*/
function social_group_entity_type_alter(array &$entity_types) {
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
if (isset($entity_types['group'])) {
$entity_types['group']
->setClass(Group::class)
->setListBuilderClass(SocialGroupListBuilder::class)
->setAccessClass(SocialGroupAccessControlHandler::class);
}
}
/**
* Prepares variables for group settings help text templates.
*
* Default template: group-settings-help.html.twig.
*
* @param array $variables
* An associative array containing:
* - group_type: The group type.
* - join_method: The join methods.
* - allowed_visibility: The allowed visibilities.
*/
function template_preprocess_group_settings_help(array &$variables) {
}
/**
* Check if a User is able to edit a Group's GroupType.
*
* @return bool
* TRUE if a user may edit a existings groups group type.
*/
function social_group_group_type_permission_check() {
$user = \Drupal::currentUser();
// Get the Group object from the route.
$group = _social_group_get_current_group();
// Check if we have a default visibility, if we don't it must be a custom
// group type, we need to have a visibility in order to update group content.
// please see hook_social_group_default_visibility_alter.
if (\Drupal::service('social_group.helper_service')
->getDefaultGroupVisibility($group
->getGroupType()
->id()) === NULL) {
return FALSE;
}
// Otherwise return true when we are able to edit the current group type.
return $group instanceof GroupInterface && $user
->hasPermission('edit group types');
}
/**
* Get group overview route.
*
* @return array
* An array with route name and parameters and group.
*/
function _social_group_get_overview_route(GroupInterface $group) {
$route = [
'name' => 'view.groups.page_user_groups',
'parameters' => [
'user' => \Drupal::currentUser()
->id(),
],
];
\Drupal::moduleHandler()
->alter('social_group_overview_route', $route, $group);
return $route;
}
/**
* Form submit redirect for social groups.
*
* @param array $form
* Group add or group edit form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state interface.
*/
function _social_group_edit_submit_redirect(array $form, FormStateInterface $form_state) {
// Set redirect to the group about page.
$route_parameters = $form_state
->getRedirect()
->getRouteParameters();
if (!empty($route_parameters['group'])) {
$form_state
->setRedirect('view.group_information.page_group_about', $route_parameters);
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function social_group_form_entity_access_by_field_visibility_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// Add option to disable public group creation.
$form['#submit'][] = '_social_group_visibility_settings_submit';
}
/**
* Form submit for visibility settings.
*
* @param array $form
* Group add or group edit form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state interface.
*/
function _social_group_visibility_settings_submit(array $form, FormStateInterface $form_state) {
/** @var \Drupal\user\Entity\Role $role */
$role = Role::load('authenticated');
$current_permission = $role
->hasPermission('create public_group group');
$disable_public_visibility = $form_state
->getValue('disable_public_visibility');
if ($disable_public_visibility === 1 && $current_permission === TRUE) {
user_role_revoke_permissions($role
->id(), [
'create public_group group',
]);
}
if ($disable_public_visibility === 0 && $current_permission === FALSE) {
user_role_grant_permissions($role
->id(), [
'create public_group group',
]);
}
}
/**
* Load group content label and group label.
*
* @return array
* array of group labels.
*/
function _social_group_get_group_labels() {
// Load the entity label from the group content being handled.
$group_content = \Drupal::routeMatch()
->getParameter('group_content')
->label();
// Load group name.
$group = \Drupal::routeMatch()
->getParameter('group')
->label();
return [
$group_content,
$group,
];
}
/**
* Prepares variables for profile templates.
*
* Default template: profile.html.twig.
*
* @param array $variables
* An associative array containing:
* - elements: An array of elements to display in view mode.
* - profile: The profile object.
* - view_mode: View mode; e.g., 'full', 'teaser', etc.
*/
function social_group_preprocess_group(array &$variables) {
/** @var \Drupal\social_group\GroupStatistics $group_statistics */
$group_statistics = \Drupal::service('social_group.group_statistics');
/** @var \Drupal\group\Entity\GroupInterface $group */
$group = $variables['group'];
$variables['title'] = $group
->label();
$variables['joined'] = FALSE;
$variables['closed_group'] = FALSE;
$variables['cta'] = '';
$variables['closed_group_lock'] = $variables['secret_group_shield'] = FALSE;
$group_type_id = $group
->getGroupType()
->id();
if (!array_key_exists('direct', _social_group_get_join_methods($group))) {
$variables['closed_group_lock'] = TRUE;
}
$variables['group_type_id'] = $group_type_id;
$variables['group_type'] = $group
->getGroupType()
->label();
// From 10.0.0 onwards, we want to show group types differently.
// It will be the value chosen while adding the group instead of saying
// 'Flexible group'.
if ($group_type_id === 'flexible_group' && $group
->hasField('field_group_type') && !empty($term = $group
->get('field_group_type')->entity) && \Drupal::config('social_group.settings')
->get('social_group_type_required')) {
$variables['group_type'] = $term
->getName();
$variables['group_type_icon'] = $term
->get('field_group_type_icon')->value;
}
// Render the group settings help, gear icon with popover.
$group_settings_help = _social_group_render_group_settings_hero($group);
$variables['group_settings_help'] = \Drupal::service('renderer')
->renderPlain($group_settings_help);
$account = \Drupal::currentUser();
// Set joined to true for teaser when current logged in
// user is member of the group.
if ($group
->getMember($account)) {
$variables['joined'] = TRUE;
if ($group
->hasPermission('leave group', $account)) {
$variables['group_operations_url'] = Url::fromRoute('entity.group.leave', [
'group' => $group
->id(),
]);
}
}
elseif ($group
->hasPermission('join group', $account)) {
// @todo switch this to get URL from routes correctly.
$variables['group_operations_url'] = Url::fromRoute('entity.group.join', [
'group' => $group
->id(),
]);
if ($group_type_id === 'flexible_group') {
$join_methods = $group
->get('field_group_allowed_join_method')
->getValue();
$direct_option = in_array('direct', array_column($join_methods, 'value'), FALSE);
if (!$direct_option) {
$variables['group_operations_url'] = Url::fromRoute('entity.group.join', [
'group' => $group
->id(),
]);
$variables['closed_group'] = TRUE;
$variables['cta'] = t('Invitation only');
}
}
}
elseif ($group_type_id == 'closed_group' && !$group
->hasPermission('manage all groups', $account)) {
// Users can only be invited.
$variables['group_operations_url'] = Url::fromRoute('entity.group.join', [
'group' => $group
->id(),
]);
$variables['closed_group'] = TRUE;
$variables['cta'] = t('Invitation only');
}
// Add the hero styled image.
if ($group
->hasField('field_group_image') && !empty($group
->get('field_group_image')->entity)) {
// Fetch image style from field info.
$original_image_style = $variables['content']['field_group_image'][0]['#image_style'] ?? '';
$image_style = ImageStyle::load($original_image_style);
if ($image_style instanceof ImageStyle) {
$variables['group_hero_styled_image_url'] = $image_style
->buildUrl($group
->get('field_group_image')->entity
->getFileUri());
// Check if this style is considered small.
$overridden_image_style = Drupal::getContainer()
->get('social_group.hero_image')
->getGroupHeroImageStyle();
if ($overridden_image_style !== $original_image_style) {
$variables['group_hero_styled_image_url'] = ImageStyle::load($overridden_image_style)
->buildUrl($group
->get('field_group_image')->entity
->getFileUri());
}
}
}
// This should be determined regardless if there's an image or not.
if (Drupal::getContainer()
->get('social_group.hero_image')
->isSmall()) {
$variables['group_hero_small'] = TRUE;
}
// Add group edit url for management.
if ($group instanceof Group) {
// Get the current route name to check if
// the user is on the edit or delete page.
$route = \Drupal::routeMatch()
->getRouteName();
if (!in_array($route, [
'entity.group.edit_form',
'entity.group.delete_form',
])) {
if ($group
->access('update', $account)) {
$variables['group_edit_url'] = $group
->toUrl('edit-form')
->toString();
$variables['#cache']['contexts'][] = 'route.name';
}
}
// Ensure all groups get the group.type differentiating labels in teasers
// hero's and full nodes.
// But also if the allowed join method is there, this should be added.
$variables['#cache']['contexts'][] = 'group.type';
if ($group
->hasField('field_group_allowed_join_method') && !empty($group
->getFieldValue('field_group_allowed_join_method', 'value'))) {
$variables['#cache']['contexts'][] = 'social_group_join_method';
}
}
// Count number of group members.
$variables['group_members'] = $group_statistics
->getGroupMemberCount($group);
// Prepare variables for statistic block.
if ($variables['view_mode'] === 'statistic') {
// Add context, since we render join / invite only etc links in the block.
$variables['#cache']['contexts'][] = 'group';
$variables['#cache']['contexts'][] = 'user';
$about_url = Url::fromRoute('view.group_information.page_group_about', [
'group' => $group
->id(),
]);
$variables['about_url'] = $about_url;
if ($group
->getGroupType()
->hasContentPlugin('group_node:event')) {
$variables['group_events'] = $group_statistics
->getGroupNodeCount($group, 'event');
$variables['group_events_label'] = \Drupal::translation()
->formatPlural($variables['group_events'], 'event', 'events');
}
if ($group
->getGroupType()
->hasContentPlugin('group_node:topic')) {
$variables['group_topics'] = $group_statistics
->getGroupNodeCount($group, 'topic');
$variables['group_topics_label'] = \Drupal::translation()
->formatPlural($variables['group_topics'], 'topic', 'topics');
}
}
}
/**
* Implements hook_preprocess_HOOK().
*/
function social_group_preprocess_group__hero(array &$variables) {
/** @var \Drupal\group\Entity\GroupInterface $group */
$group = $variables['group'];
$account = \Drupal::currentUser();
if ($group
->bundle() == 'public_group' && $account
->isAnonymous()) {
$variables['group_operations_url'] = Url::fromRoute('user.register', [], [
'query' => [
'destination' => Url::fromRoute('entity.group.join', [
'group' => $group
->id(),
])
->toString(),
],
])
->toString();
}
// Render the group settings help, gear icon with popover.
$group_settings_help = _social_group_render_group_settings_hero($group);
$variables['group_settings_help'] = \Drupal::service('renderer')
->renderPlain($group_settings_help);
}
/**
* Renders the group settings based on available fields for the hero.
*
* @param \Drupal\group\Entity\GroupInterface $group
* the Group interface.
*
* @return array
* the actual tooltip render array.
*/
function _social_group_render_group_settings_hero(GroupInterface $group) {
$description = '';
// Optional after 10.0.x we can render the group visibility.
if ($group_visibility_option = _social_group_get_group_visibility($group)) {
// Wrap our chosen description in a container.
$description .= '<span class="title">' . t('Group visibility') . '</span>';
$description .= '<div class="group-visibility-details container-background">';
foreach ($group_visibility_option as $key => $group_visibility_string) {
$description .= $group_visibility_string;
}
$description .= '</div>';
}
// Optional after 9.x we can render the allowed content visibility.
if ($allowed_visibility_option = _social_group_get_allowed_visibility($group)) {
// Wrap our chosen description in a container.
$description .= '<span class="title">' . t('Group content visibility') . '</span>';
$description .= '<div class="group-visibility-details container-background">';
foreach ($allowed_visibility_option as $key => $allowed_visibility_string) {
$description .= $allowed_visibility_string;
}
$description .= '</div>';
}
// Optional after 9.x we can render the join methods.
if ($join_methods_option = _social_group_get_join_methods($group)) {
// Wrap our chosen description in a container.
$description .= '<span class="title">' . t('Join method') . '</span>';
$description .= '<div class="group-visibility-details container-background">';
foreach ($join_methods_option as $key => $join_methods_string) {
$description .= $join_methods_string;
}
$description .= '</div>';
}
return social_group_render_tooltip('group_hero', t('Access permissions'), $description);
}
/**
* Get the join methods of a group.
*
* @param \Drupal\group\Entity\GroupInterface $group
* the Group interface.
*
* @return array
* Returns join methods of a group.
*/
function _social_group_get_join_methods(GroupInterface $group) {
$group_type = $group
->getGroupType();
$group_type_id = $group_type
->id();
$join_methods = [];
// Get join method based on group type. TODO get it programmatically.
switch ($group_type_id) {
case 'secret_group':
case 'closed_group':
$join_methods['added'] = social_group_allowed_join_method_description('added');
break;
case 'public_group':
case 'open_group':
$join_methods['direct'] = social_group_allowed_join_method_description('direct');
$join_methods['added'] = social_group_allowed_join_method_description('added');
break;
case 'flexible_group':
// Try to retrieve join methods from Group directly.
$allowed_options = $group
->get('field_group_allowed_join_method')
->getValue();
foreach ($allowed_options as $option) {
// Lets grab the value from the selected radio item.
if (!empty($option['value']) && is_string($option['value'])) {
$join_methods[$option['value']] = social_group_allowed_join_method_description($option['value']);
}
}
break;
}
return $join_methods;
}
/**
* Get the group visibility label of a group.
*
* @param \Drupal\group\Entity\GroupInterface $group
* the Group interface.
* @param string $field_name
* The field name of the visibility field for a group type.
*
* @return array
* Returns the visibility options of a group.
*/
function _social_group_get_group_visibility(GroupInterface $group, $field_name = NULL) {
$group_type = $group
->getGroupType();
$group_type_id = $group_type
->id();
$group_visibility = [];
// Get join method based on group type.
switch ($group_type_id) {
case 'secret_group':
case 'closed_group':
case 'public_group':
case 'open_group':
return $group_visibility;
case 'flexible_group':
// By default we ship with the below field, lets grab its value(s).
if ($group
->hasField('field_flexible_group_visibility')) {
$visibility_values = $group
->get('field_flexible_group_visibility')
->getValue();
// Lets grab the rendered description for the group visibility.
if (!empty($visibility_values)) {
foreach ($visibility_values as $visibility_value) {
if (!empty($visibility_value['value']) && is_string($visibility_value['value'])) {
$group_visibility[$visibility_value['value']] = social_group_group_visibility_description($visibility_value['value']);
}
}
}
return $group_visibility;
}
// If the above field doesn't exist the user might have a different one
// so lets see if it's given as argument and get it's description.
if ($field_name !== NULL && $group
->hasField($field_name)) {
$visibility_values = $group
->get($field_name)
->getValue();
// Lets grab the rendered description for the group visibility.
if (!empty($visibility_values)) {
foreach ($visibility_values as $visibility_value) {
if (!empty($visibility_value['value']) && is_string($visibility_value['value'])) {
$group_visibility[$visibility_value['value']] = social_group_group_visibility_description($visibility_value['value']);
}
}
}
return $group_visibility;
}
break;
default:
// For non Open Social group types, we can allow developers to pass
// a field name to return a label instead.
if ($field_name !== NULL && $group
->hasField($field_name)) {
$group_visibility = $group
->get($field_name)
->getValue();
}
return $group_visibility;
}
return $group_visibility;
}
/**
* Get the allowed visibility of a group.
*
* @param Drupal\group\Entity\GroupInterface $group
* the Group interface.
*
* @return array
* Returns allowed visibility of a group.
*/
function _social_group_get_allowed_visibility(GroupInterface $group) {
$group_type = $group
->getGroupType();
$group_type_id = $group_type
->id();
$allowed_visibility = [];
// Get allowed visibility based on group type. TODO get it programmatically.
switch ($group_type_id) {
case 'secret_group':
case 'closed_group':
$allowed_visibility['group'] = social_group_allowed_visibility_description('group');
break;
case 'open_group':
$allowed_visibility['community'] = social_group_allowed_visibility_description('community');
break;
case 'public_group':
$allowed_visibility['public'] = social_group_allowed_visibility_description('public');
break;
case 'flexible_group':
// Try to retrieve allowed visibility from Group directly.
$allowed_options = $group
->get('field_group_allowed_visibility')
->getValue();
foreach ($allowed_options as $option) {
if (!empty($option['value']) && is_string($option['value'])) {
$allowed_visibility[$option['value']] = social_group_allowed_visibility_description($option['value']);
}
}
break;
}
return $allowed_visibility;
}
/**
* Returns a description array for the field_flexible_group_visibility options.
*
* @return string
* The render array containing the description.
*/
function social_group_group_visibility_description($key) {
$description = '';
// We need it to be specified otherwise we can't build the markup.
if (empty($key)) {
return $description;
}
// Add explanatory descriptive text after the icon.
switch ($key) {
case 'public':
$description = '<p>';
$description .= '<p><strong><svg class="icon-small"><use xlink:href="#icon-public"></use></svg></strong>';
$description .= '<strong>' . t('Public')
->render() . '</strong>';
$description .= ' - ' . t('All visitors of the platform can see this group')
->render();
$description .= '</p>';
break;
case 'community':
$description = '<p>';
$description .= '<strong><svg class="icon-small"><use xlink:href="#icon-community"></use></svg></strong>';
$description .= '<strong>' . t('Community')
->render() . '</strong>';
$description .= ' - ' . t('Only members who are logged in can see this group')
->render();
$description .= '</p>';
break;
case 'members':
$description = '<p>';
$description .= '<p><strong><svg class="icon-small"><use xlink:href="#icon-lock"></use></svg></strong>';
$description .= '<strong>' . t('Group members only (Secret)')
->render() . '</strong>';
$description .= ' - ' . t('Only group members can see this group')
->render();
$description .= '</p>';
break;
}
// Allow modules to provide their own markup for a given key in the
// group_visibility #options array.
\Drupal::moduleHandler()
->alter('social_group_group_visibility_description', $key, $description);
return $description;
}
/**
* Implements hook_entity_insert().
*/
function social_group_group_insert(GroupInterface $group) {
// @todo Remove this when https://www.drupal.org/node/2702743 lands and make.
// sure the settings will be implemented accordingly.
if ($group
->getGroupType()
->id() === 'open_group' || $group
->getGroupType()
->id() === 'closed_group' || $group
->getGroupType()
->id() === 'flexible_group') {
// Get the group owner.
$account = $group
->getOwner();
// Get membership.
$content = $group
->getMember($account)
->getGroupContent();
// Delete the initial created membership.
$content
->delete();
$grant_group_admin = FALSE;
// If the user has this permission inside a group.
if ($group
->hasPermission('manage all groups', $account)) {
// Then we grant this user de Group Admin role.
$grant_group_admin = TRUE;
}
// When a CM+ creates a group, it is given the group_manager role
// alongside the group_admin role to keep the full control over the group.
if ($grant_group_admin) {
// Delete the initial created membership.
$content
->delete();
$plugin = $group
->getGroupType()
->getContentPlugin('group_membership');
$values = [
'group_roles' => [
$group
->bundle() . '-group_admin',
$group
->bundle() . '-group_manager',
],
];
$group_content = GroupContent::create([
'type' => $plugin
->getContentTypeConfigId(),
'gid' => $group
->id(),
'entity_id' => $group
->getOwnerId(),
] + $values);
$group_content
->save();
}
else {
// Create a new membership.
$plugin = $group
->getGroupType()
->getContentPlugin('group_membership');
$values = [
'group_roles' => [
$group
->bundle() . '-group_manager',
],
];
$group_content = GroupContent::create([
'type' => $plugin
->getContentTypeConfigId(),
'gid' => $group
->id(),
'entity_id' => $group
->getOwnerId(),
] + $values);
$group_content
->save();
}
}
}
/**
* Returns a description array for the field_group_allowed_join_method options.
*
* @param string $key
* The join method key.
*
* @return string
* The render array containing the description.
*/
function social_group_allowed_join_method_description($key) {
$description = '';
// We need it to be specified otherwise we can't build the markup.
if (empty($key)) {
return $description;
}
// Add explanatory descriptive text after the icon.
switch ($key) {
case 'added':
$description = '<p><strong>' . t('Invite only')
->render() . '</strong>';
$description .= ' - ' . t('users can only join this group if they are added/invited by group managers.')
->render();
$description .= '</p>';
break;
case 'direct':
$description = '<p><strong>' . t('Open to join')
->render() . '</strong>';
$description .= ' - ' . t('users can join this group without approval.')
->render();
$description .= '</p>';
break;
case 'request':
$description = '<p><strong>' . t('Request to join')
->render() . '</strong>';
$description .= ' - ' . t('users can "request to join" this group which group managers approve/decline.')
->render();
$description .= '</p>';
break;
}
// Allow modules to provide their own markup for a given key in the
// join method #options array.
\Drupal::moduleHandler()
->alter('social_group_allowed_join_method_description', $key, $description);
return $description;
}
/**
* Implements hook_entity_view_alter().
*/
function social_group_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
// For group entities.
if ($entity instanceof Group) {
// Add cache contexts for some view modes.
switch ($build['#view_mode']) {
case 'hero':
// Add cache contexts for group type & permissions.
// We need join / leave for the CTA and also update permissions
// for the button to edit a group.
$build['#cache']['contexts'][] = 'group.type';
$build['#cache']['contexts'][] = 'user.group_permissions';
$build['#cache']['tags'][] = 'group_block:' . $entity
->id();
break;
case 'teaser':
$build['#cache']['contexts'][] = 'user';
break;
}
}
}
/**
* Implements hook_form_alter().
*/
function social_group_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$social_group_types = [
'open_group',
'closed_group',
'public_group',
];
\Drupal::moduleHandler()
->alter('social_group_types', $social_group_types);
$join_forms = [];
$leave_forms = [];
$membership_add_forms = [];
$membership_edit_forms = [];
$membership_delete_forms = [];
$group_forms = [];
foreach ($social_group_types as $social_group_type) {
$join_forms[] = "group_content_{$social_group_type}-group_membership_group-join_form";
$leave_forms[] = "group_content_{$social_group_type}-group_membership_group-leave_form";
$membership_add_forms[] = "group_content_{$social_group_type}-group_membership_add_form";
$membership_edit_forms[] = "group_content_{$social_group_type}-group_membership_edit_form";
$membership_delete_forms[] = "group_content_{$social_group_type}-group_membership_delete_form";
$group_forms['edit'][] = "group_{$social_group_type}_edit_form";
$group_forms['add'][] = "group_{$social_group_type}_add_form";
$group_forms['delete'][] = "group_{$social_group_type}_delete_form";
}
$action_forms = array_merge($join_forms, $leave_forms, $membership_delete_forms, $membership_add_forms);
$membership_forms = array_merge($membership_add_forms, $membership_edit_forms);
// Perform alterations on joining / leaving groups.
if (in_array($form_id, $membership_delete_forms)) {
$form['actions']['submit']['#submit'][] = '_social_membership_delete_form_submit';
[
$name,
$group,
] = _social_group_get_group_labels();
// Give a better title, description and submit button value.
$form['#title'] = t('Remove %name from %group', [
'%name' => $name,
'%group' => $group,
]);
$form['actions']['submit']['#value'] = t('Remove');
$form['description']['#markup'] = t('Are you sure you want to remove %name from %group?', [
'%name' => $name,
'%group' => $group,
]);
}
// Set some helpful text on the group join form now it's there.
if (in_array($form_id, $join_forms)) {
$markup = t('By submitting this form you will become a member of the group. As a member of the group you will begin to receive notifications about changes and interactions within the group. Your profile will also be included in group membership.');
// When the user is accepting an invite, change the text to something more
// meaningful.
if (\Drupal::routeMatch()
->getRouteName() === 'ginvite.invitation.accept') {
$markup = t('Are you sure to accept the invitation and join this group? You can leave this group at any time.');
}
$form['help'] = [
'#type' => 'item',
'#markup' => $markup,
];
$form['path']['#type'] = 'hidden';
}
// Perform alterations on joining / leaving groups.
if (in_array($form_id, $action_forms)) {
// Add cancel option on join and leave form.
$form['actions']['cancel'] = [
'#type' => 'submit',
'#value' => t('Cancel'),
'#submit' => [
'_social_group_cancel_join_leave_form',
],
'#limit_validation_errors' => [],
];
$form['actions']['submit']['#submit'][] = '_social_group_action_form_submit';
}
if (in_array($form_id, $membership_forms) && Drupal::currentUser()
->id() != 1) {
// Change titles on membership forms.
$form['entity_id']['widget'][0]['target_id']['#title'] = t('Find people by name');
$form['group_roles']['widget']['#title'] = t('Group roles');
// Remove the 'group_admin' role in a generic way
// for all (future) group types.
foreach ($form['group_roles']['widget']['#options'] as $key => $value) {
// Hide the submission for the Group Admin role.
if (strpos($key, 'group_admin') != FALSE) {
unset($form['group_roles']['widget']['#options'][$key]);
}
}
$form['path']['#type'] = 'hidden';
}
// Change the form when adding members directly in groups.
if (in_array($form_id, $membership_add_forms, TRUE) && \Drupal::routeMatch()
->getRouteName() !== 'grequest.group_request_membership_approve') {
// Lets add the new select 2 widget to add members to a group.
$form['entity_id']['widget'][0]['target_id'] = [
'#title' => t('Find people by name or email address'),
'#type' => 'select2',
'#multiple' => TRUE,
'#tags' => TRUE,
'#autocomplete' => TRUE,
'#select2' => [
'placeholder' => t('Jane Doe'),
'tokenSeparators' => [
',',
';',
],
],
'#selection_handler' => 'social',
'#target_type' => 'user',
'#element_validate' => [
'_social_group_unique_members',
],
];
// Group roles and the automated URL alias don't make sense here.
if (isset($form['group_roles'])) {
unset($form['group_roles']);
}
if (isset($form['path'])) {
unset($form['path']);
}
}
// Check if form is group content create form.
if (isset($form['#entity_type']) && $form['#entity_type'] === 'node') {
$group = _social_group_get_current_group();
if (!empty($group)) {
// Add custom submit handler just for redirect purposes.
// We don't want to override the form::save in group.
$form['actions']['submit']['#submit'][] = '_social_group_node_form_submit';
}
else {
// If node don't belong to any group as a group content plugin
// we don't need group visibility field.
if (method_exists($form_state
->getFormObject(), 'getEntity')) {
/** @var \Drupal\Core\Entity\EntityInterface $node */
$node = $form_state
->getFormObject()
->getEntity();
/** @var \Drupal\group\Plugin\GroupContentEnablerManagerInterface $gc_manager */
$gc_manager = \Drupal::service('plugin.manager.group_content_enabler');
if (!$gc_manager
->getGroupContentTypeIds('group_node:' . $node
->bundle())) {
unset($form['groups']);
}
}
}
}
if (in_array($form_id, $group_forms['add']) || in_array($form_id, $group_forms['edit']) || in_array($form_id, $group_forms['delete'])) {
// Add custom submit handler just for redirect purposes.
$form['actions']['submit']['#submit'][] = '_social_group_edit_submit_redirect';
if (in_array($form_id, $group_forms['add']) || in_array($form_id, $group_forms['edit'])) {
$form['path']['#type'] = 'hidden';
$form['actions']['submit']['#value'] = t('Save');
// Hide default title from Address field.
if (isset($form['field_group_address'])) {
$form['field_group_address']['widget'][0]['#title'] = '';
}
}
if (in_array($form_id, $group_forms['edit'])) {
$social_group_form = SocialGroupAddForm::create(\Drupal::getContainer());
$group_type_element = $social_group_form
->getGroupTypeElement(TRUE);
// Get the current group.
$group = _social_group_get_current_group();
// Set the default value in the form.
$group_type_element['widget']['#default_value'] = $group
->getGroupType()
->id();
// If user doesn't have permission to change group types disable it.
// Or if group types can't be edited due to visibility issues.
if (!social_group_group_type_permission_check()) {
$group_type_element['#disabled'] = TRUE;
}
else {
$group_type_element['#prefix'] = '<div id="group-type-result"></div>';
$group_type_element['widget']['#ajax'] = [
'callback' => '_social_group_inform_group_type_selection',
'effect' => 'fade',
'event' => 'change',
];
}
$form['group_type'] = $group_type_element;
// Disable all group types that can't be edited. Because they don't have
// a visibility.
foreach (array_keys($form['group_type']['widget']['#options']) as $type) {
if (\Drupal::service('social_group.helper_service')
->getDefaultGroupVisibility($type) === NULL) {
$form['group_type']['widget'][$type] = [
'#disabled' => TRUE,
];
}
}
$form['#fieldgroups']['group_settings']->children[] = 'group_type';
$form['#group_children']['group_type'] = 'group_settings';
$form['actions']['submit']['#submit'][] = '_social_group_type_edit_submit';
}
if (in_array($form_id, $group_forms['delete'])) {
// Add custom submit handler to delete all content of the group.
$group = _social_group_get_current_group();
$form['description']['#markup'] = t('Are you sure you want to delete your group "@group" along with all of the posts, events and topics inside this group? This action cannot be undone.', [
'@group' => $group
->label(),
]);
$form['actions']['cancel'] = [
'#type' => 'submit',
'#value' => t('Cancel'),
'#submit' => [
'_social_group_cancel_join_leave_form',
],
'#limit_validation_errors' => [],
];
array_unshift($form['actions']['submit']['#submit'], '_social_group_delete_group');
}
}
if (in_array($form_id, [
'group_flexible_group_edit_form',
'group_flexible_group_add_form',
])) {
$join_method_default_value = 'added';
// Ensure we have a better descriptive label.
if (array_key_exists('added', $form['field_group_allowed_join_method']['widget']['#options'])) {
$form['field_group_allowed_join_method']['widget']['#options']['added'] = t('Invite only');
}
if (array_key_exists('direct', $form['field_group_allowed_join_method']['widget']['#options'])) {
$form['field_group_allowed_join_method']['widget']['#options']['direct'] = t('Open to join');
}
// If directly exists it's becoming the default.
if (in_array('direct', $form['field_group_allowed_join_method']['widget']['#default_value'])) {
$join_method_default_value = 'direct';
}
elseif (in_array('request', $form['field_group_allowed_join_method']['widget']['#default_value'])) {
$join_method_default_value = 'request';
}
$form['field_group_allowed_join_method']['widget']['#type'] = 'radios';
$form['field_group_allowed_join_method']['widget']['#default_value'] = $join_method_default_value;
}
// Exposed Filter block on the all-groups overview.
if ($form['#id'] === 'views-exposed-form-newest-groups-page-all-groups' || $form['#id'] === 'views-exposed-form-search-groups-page-no-value' || $form['#id'] === 'views-exposed-form-search-groups-page') {
$account = \Drupal::currentUser();
if (!empty($form['type']['#options'])) {
foreach ($form['type']['#options'] as $type => $label) {
// All / Any we can skip they are optional translatable options
// and not group types.
if ($label instanceof TranslatableMarkup) {
continue;
}
if (!social_group_can_view_groups_of_type($type, $account)) {
unset($form['type']['#options'][$type]);
}
}
}
}
}
/**
* Function to validate entries against group members.
*/
function _social_group_unique_members($element, &$form_state, $complete_form) {
// Call the autocomplete function to make sure enrollees are unique.
SocialGroupEntityAutocomplete::validateEntityAutocomplete($element, $form_state, $complete_form, TRUE);
}
/**
* Function that gives information to a CM+ when they edit a Group Type.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An ajax command containing the informative text.
*/
function _social_group_inform_group_type_selection() {
$text = t('Please note that changing the group type will also change the
visibility of the group content and the way users can join the group.');
$ajax_response = new AjaxResponse();
$ajax_response
->addCommand(new HtmlCommand('#group-type-result', $text));
return $ajax_response;
}
/**
* Form submit for removing members from a group so we can clear caches.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
function _social_group_type_edit_submit($form, FormStateInterface $form_state) {
// Check if the group_type changed.
// Visibility be empty for flexible groups.
$group = _social_group_get_current_group();
if (!empty($form['group_type']) && \Drupal::service('social_group.helper_service')
->getDefaultGroupVisibility($group
->getGroupType()
->id()) != NULL) {
$default_type = $form['group_type']['widget']['#default_value'];
$new_type = $form_state
->getValue('group_type')[0]['value'];
// Update the Group entity and all it's content when the type changed.
if ($new_type !== $default_type) {
// Update the default visibility of all the content.
GroupContentVisibilityUpdate::batchUpdateGroupContentVisibility($group, $new_type);
}
}
if ($group instanceof GroupInterface) {
// Make sure we clear cache tags accordingly.
$cache_tags = _social_group_cache_tags($group);
foreach ($cache_tags as $cache_tag) {
Cache::invalidateTags([
$cache_tag,
]);
}
}
}
/**
* Form submit for removing members from a group so we can clear caches.
*/
function _social_membership_delete_form_submit($form, FormStateInterface $form_state) {
$group = _social_group_get_current_group();
if (is_object($group)) {
// Invalidate cache tags.
$cache_tags = _social_group_cache_tags($group);
foreach ($cache_tags as $cache_tag) {
Cache::invalidateTags([
$cache_tag,
]);
}
}
}
/**
* Form submit for group join / leave form.
*/
function _social_group_action_form_submit($form, FormStateInterface $form_state) {
$group = _social_group_get_current_group();
if (is_object($group)) {
// Invalidate cache tags.
$cache_tags = _social_group_cache_tags($group);
foreach ($cache_tags as $cache_tag) {
Cache::invalidateTags([
$cache_tag,
]);
}
// Get form that was submitted.
$complete_form = $form_state
->getCompleteForm();
if (in_array($complete_form['#form_id'], [
'group_content_' . $group
->bundle() . '-group_membership_group-join_form',
'group_content_' . $group
->bundle() . '-group_membership_add_form',
])) {
// Set redirect to group home page for people joining the group.
$form_state
->setRedirect('entity.group.canonical', [
'group' => $group
->id(),
[],
]);
// For adding people we redirect to the manage members page.
if ($complete_form['#form_id'] == 'group_content_' . $group
->bundle() . '-group_membership_add_form') {
// We passed the validation.
// Let's create the Group Content for all our members just like
// Group does it for creating a Group and adding its creator as member.
$values = [
'group_roles' => $form_state
->getValue('group_roles'),
];
$count = 0;
if (!empty($form_state
->getValue('entity_id_new'))) {
// For multiple Group Members that are valid we add multiple entities.
foreach ($form_state
->getValue('entity_id_new') as $key => $uid) {
$group
->addMember(User::load($uid['target_id']), $values);
$count++;
}
}
// Add nice messages.
if (!empty($count)) {
$message = \Drupal::translation()
->formatPlural($count, '@count new member joined the group.', '@count new members joined the group.');
\Drupal::messenger()
->addMessage($message, 'status');
}
$form_state
->setRedirect('view.group_manage_members.page_group_manage_members', [
'group' => $group
->id(),
[],
]);
}
}
else {
// Set redirect to the Group overview page
// when a user saves their profile.
$route = _social_group_get_overview_route($group);
$form_state
->setRedirect($route['name'], $route['parameters']);
}
}
}
/**
* This function checks if the user should get the admin role within a group.
*
* Also check if the user has already a membership.
* If so, update it with the new role.
*/
function _social_group_grant_admin_role($uid, $gid) {
$account = User::load($uid);
$group = Group::load($gid);
// Must be a valid account AND a valid group.
if ($account instanceof User && $group instanceof Group) {
// Must have manage all groups permission.
// Otherwise normal flow will be fine.
if (!$account
->hasPermission('manage all groups')) {
return;
}
// Check if the user is already a member in the group
// (could be in update mode here).
/** @var \Drupal\group\GroupMembership $membership */
$admin_role = $group
->bundle() . '-group_admin';
$membership = $group
->getMember($account);
// Check what roles are there.
$roles = [];
foreach ($membership
->getGroupContent()->group_roles as $group_role_ref) {
$roles[] = $group_role_ref->target_id;
}
// No admin? Add it.
if (!in_array($admin_role, $roles)) {
$membership
->getGroupContent()->group_roles[] = [
'target_id' => $admin_role,
];
$membership
->getGroupContent()
->save();
}
}
}
/**
* When creating a new group membership.
*
* @param \Drupal\group\Entity\GroupContentInterface $group_content
* The group content.
*/
function social_group_group_content_insert(GroupContentInterface $group_content) {
if ($group_content
->getEntity()
->getEntityTypeId() == 'user') {
if ($group_content
->bundle() == $group_content
->getGroup()
->bundle() . '-group_membership') {
_social_group_grant_admin_role($group_content
->getEntity()
->id(), $group_content
->getGroup()
->id());
}
}
}
/**
* When updating a group membership.
*
* @param \Drupal\group\Entity\GroupContentInterface $group_content
* The group content.
*/
function social_group_group_content_update(GroupContentInterface $group_content) {
if ($group_content
->getEntity()
->getEntityTypeId() == 'user') {
if ($group_content
->bundle() == $group_content
->getGroup()
->bundle() . '-group_membership') {
_social_group_grant_admin_role($group_content
->getEntity()
->id(), $group_content
->getGroup()
->id());
}
}
}
/**
* Form submit for membership edit form.
*
* @param array $form
* Membership edit form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state interface.
*/
function _social_group_membership_edit_form_submit(array $form, FormStateInterface $form_state) {
$group = _social_group_get_current_group();
if (is_object($group)) {
// Set redirect to the Group overview page when a user saves their profile.
$form_state
->setRedirect('entity.group_content.collection', [
'group' => $group
->id(),
[],
]);
}
}
/**
* Form cancel for join and leave form.
*
* @param array $form
* Join and leave form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state interface.
*/
function _social_group_cancel_join_leave_form(array $form, FormStateInterface $form_state) {
// Get form that was submitted.
$group = _social_group_get_current_group();
if (is_object($group)) {
// Set redirect only for cancel buttons. Just go back to the group page.
$form_state
->setRedirect('entity.group.canonical', [
'group' => $group
->id(),
[],
]);
}
}
/**
* Form submit for group content create form.
*
* @param array $form
* Group node form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state interface.
*/
function _social_group_node_form_submit(array $form, FormStateInterface $form_state) {
$nid = $form_state
->getValue('nid');
// Set redirect.
$form_state
->setRedirect('entity.node.canonical', [
'node' => $nid,
]);
}
/**
* Alter the visibility field within groups.
*
* Implements hook_field_widget_form_alter().
*/
function social_group_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
/** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
$field_definition = $context['items']
->getFieldDefinition();
// Unset the visibility field options which are not enabled.
if ($field_definition
->getType() == 'entity_access_field') {
$default_value = $context['items']
->getValue();
if (isset($default_value[0]['value'])) {
$element['#default_value'] = $default_value[0]['value'];
}
else {
$default_visibility = \Drupal::configFactory()
->get('entity_access_by_field.settings')
->get('default_visibility');
$element['#default_value'] = $default_visibility;
}
$entity = $form_state
->getFormObject()
->getEntity();
$current_group = _social_group_get_current_group();
if (!empty($current_group)) {
$group_type = $current_group
->getGroupType();
$group_type_id = $group_type
->id();
// If it's an existing entity we dont need to set the default
// rather follow the saved one.
if (!$entity
->id()) {
$element['#default_value'] = \Drupal::service('social_group.helper_service')
->getDefaultGroupVisibility($group_type_id);
}
}
else {
$group_type_id = NULL;
// This is not a group so lets disable this visibility option.
$element['group']['#disabled'] = TRUE;
}
$visibility_options = social_group_get_allowed_visibility_options_per_group_type($group_type_id, NULL, $entity, $current_group);
// Based on the allowed visibility options for a group type,
// we disable the options which are not valid for the group.
// Remember for flexible groups we can have multiple options.
foreach ($visibility_options as $visibility => $allowed) {
$element[$visibility]['#disabled'] = !$allowed;
// If the element is disabled, we also need to move it from
// the default value.
if (!$allowed && $element['#default_value'] === $visibility) {
$element['#default_value'] = NULL;
}
}
// This contains the group that is passed back from the form_state as
// chosen by selecting a group in the $form['groups'] select widget.
// With this we can ensure that on validation the content visibility
// field is disabling the correct values based on the selected group.
if (!empty($form_state
->getValue('groups'))) {
$group_id = NULL;
$group_value = $form_state
->getValue('groups');
if (is_array($group_value)) {
$group_id = $group_value[0]['target_id'];
}
if (is_string($group_value)) {
$group_id = (int) $group_value;
}
// Load the correct group.
$selected_groups = Group::loadMultiple([
$group_id,
]);
// Ensure we disable the correct visibility options.
foreach ($selected_groups as $current_group) {
if (!empty($current_group)) {
$group_type = $current_group
->getGroupType();
$group_type_id = $group_type
->id();
$visibility_options = social_group_get_allowed_visibility_options_per_group_type($group_type_id, NULL, NULL, $current_group);
foreach ($visibility_options as $visibility => $allowed) {
$element[$visibility]['#disabled'] = !$allowed;
// If the element is disabled, we also need to move it from
// the default value.
if (!$allowed && $element['#default_value'] === $visibility) {
$element['#default_value'] = NULL;
}
}
}
}
}
// For flexible groups it could potentially still be empty.
// But in any way, from the available options, if its empty
// lets choose the most safe one.
if ($element['#default_value'] === NULL) {
// So if public isn't disabled, it will become public.
if (!$element['public']['#disabled']) {
$element['#default_value'] = 'public';
}
// So if community isn't disabled, it will become community.
if (!$element['community']['#disabled']) {
$element['#default_value'] = 'community';
}
// And if members isn't disabled, it will become members.
if (!$element['group']['#disabled']) {
$element['#default_value'] = 'group';
}
}
}
}
/**
* Implements hook_views_post_render().
*
* Alter "Group Members" views. Replace user IDs with profile teasers.
*/
function social_group_views_post_render(ViewExecutable $view, &$output, CachePluginBase $cache) {
if ($view
->id() == 'group_members') {
if (!empty($output['#rows'][0]['#rows'])) {
foreach ($output['#rows'][0]['#rows'] as $key => $row) {
// Get Group membership content entity.
$group_content = $row['#group_content'];
// Get Profile.
$user_profile = _social_group_get_member_profile($group_content);
if ($user_profile) {
// Use teaser for page and small_teaser for block.
$view_mode = $view->current_display === 'block_newest_members' ? 'small_teaser' : 'teaser';
// Replace output with profile teaser.
$output['#rows'][0]['#rows'][$key] = \Drupal::entityTypeManager()
->getViewBuilder('profile')
->view($user_profile, $view_mode);
}
else {
// Remove output if user don't have profile (admin).
unset($output['#rows'][0]['#rows'][$key]);
}
}
}
}
}
/**
* Implements hook_menu_local_tasks_alter().
*/
function social_group_menu_local_tasks_alter(&$data, $route_name) {
// Rename Group "Related Entities" tab.
if (isset($data['tabs'][0]['group.content']['#link'])) {
$data['tabs'][0]['group.content']['#link']['title'] = t('Manage members');
$data['tabs'][0]['group.content']['#weight'] = 70;
}
// Remove the default View tab.
if (isset($data['tabs'][0]['group.view'])) {
unset($data['tabs'][0]['group.view']);
}
// Remove Edit tab. Edit will always go through Floating Edit Button.
if (isset($data['tabs'][0]['group.edit_form'])) {
unset($data['tabs'][0]['group.edit_form']);
}
$user = \Drupal::currentUser();
// Get the Group object from the route.
$group = Drupal::routeMatch()
->getParameter('group');
if ($group instanceof GroupInterface) {
/** @var \Drupal\group\Entity\GroupTypeInterface $group_type */
$group_type = $group
->getGroupType()
->id();
// Check if it's a closed group.
if ($group_type === 'closed_group') {
// And if the user is not user 1.
if ($user
->id() !== 1) {
if ($user
->hasPermission('manage all groups')) {
return;
}
// If the user is not an member of this group.
if (!$group
->getMember($user)) {
// Disable these local tasks.
$data['tabs'][0]['group.view'] = [];
if (!$group
->hasPermission('administer members', $user)) {
$data['tabs'][0]['group.content'] = [];
}
$data['tabs'][0]['social_group.events'] = [];
$data['tabs'][0]['social_group.topics'] = [];
}
}
}
}
}
/**
* Implements hook_local_tasks_alter().
*/
function social_group_local_tasks_alter(&$local_tasks) {
// Remove local delete task from group page.
unset($local_tasks['group.delete_form']);
$local_tasks['group.content']['route_name'] = 'view.group_manage_members.page_group_manage_members';
$local_tasks['group.content']['weight'] = 70;
}
/**
* Implements hook_menu_local_actions_alter().
*/
function social_group_menu_local_actions_alter(&$local_actions) {
// Remove "Add Relationship" button.
if (isset($local_actions['group_content.add_page'])) {
unset($local_actions['group_content.add_page']);
}
// Remove Create new entity in group.
if (isset($local_actions['group_content.create_page'])) {
unset($local_actions['group_content.create_page']);
}
}
/**
* Return user profile by given group membership content.
*
* @param \Drupal\group\Entity\GroupContent $group_content
* Group content entity.
*
* @return \Drupal\profile\Entity\Profile
* Returns the Profile entity for the member.
*/
function _social_group_get_member_profile(GroupContent $group_content) {
$user_profile = NULL;
// Get User entity.
$user_entity = $group_content
->getEntity();
if (!empty($user_entity)) {
// Get Profile storage.
$storage = \Drupal::entityTypeManager()
->getStorage('profile');
if (!empty($storage)) {
// Get Profile entity.
$user_profile = $storage
->loadByUser($user_entity, 'profile');
}
}
return $user_profile;
}
/**
* Gets current Group entity from the route.
*
* @param \Drupal\node\NodeInterface|null $node
* (optional) The node object or NULL.
*
* @return \Drupal\group\Entity\GroupInterface|null
* Returns the group object.
*/
function _social_group_get_current_group($node = NULL) {
$cache =& drupal_static(__FUNCTION__, []);
// For the same $node input, within the same request the return is always
// the same.
$nid = NULL;
if (is_null($node)) {
$nid = -1;
}
elseif ($node instanceof NodeInterface) {
$nid = $node
->id();
}
// If we have a cache key and it has a value, we're done early.
if (!is_null($nid) && isset($cache[$nid])) {
// Translate FALSE (so isset works) back to NULL.
return $cache[$nid] ?: NULL;
}
$group = \Drupal::routeMatch()
->getParameter('group');
if (!is_object($group) && !is_null($group)) {
$group = \Drupal::entityTypeManager()
->getStorage('group')
->load($group);
}
else {
$node = is_object($node) ? $node : \Drupal::routeMatch()
->getParameter('node');
if (is_object($node)) {
$node_entity = [
'target_type' => 'node',
'target_id' => $node
->id(),
];
$gid_from_entity = \Drupal::service('social_group.helper_service')
->getGroupFromEntity($node_entity);
if ($gid_from_entity !== NULL) {
$group = \Drupal::entityTypeManager()
->getStorage('group')
->load($gid_from_entity);
}
}
}
// If we have a cache key we store the value.
if (!is_null($nid)) {
// Translate NULL to FALSE so that isset works.
$cache[$nid] = $group ?? FALSE;
}
return $group;
}
/**
* Get group cache tags.
*
* @param \Drupal\group\Entity\GroupInterface $group
* The GroupInterface.
*
* @return array
* An array of cache tags related to groups.
*/
function _social_group_cache_tags(GroupInterface $group) {
// Group views.
$tags = [
'group_list',
'group_content_list',
'group_view',
'group_content_view',
];
// Add cache tags that are based on id.
$tags[] = 'group_hero:' . $group
->id();
// Add cache tags for the blocks.
$tags[] = 'group_block:' . $group
->id();
$current_user = \Drupal::currentUser();
if ($group_membership = $group
->getMember($current_user)) {
$group_content = $group_membership
->getGroupContent();
$tags[] = 'group_content:' . $group_content
->id();
}
return $tags;
}
/**
* Implements hook_block_view_BASE_BLOCK_ID_alter().
*
* Add Group cache context for system "Tabs" block.
*/
function social_group_block_view_local_tasks_block_alter(array &$build, BlockPluginInterface $block) {
$build['#cache']['contexts'][] = 'user.group_permissions';
}
/**
* Implements hook_block_build_alter().
*/
function social_group_block_build_alter(array &$build, BlockPluginInterface $block) {
if (!empty($block
->getPluginId()) && ($block
->getPluginId() === 'views_exposed_filter_block:newest_groups-page_all_groups' || $block
->getPluginId() === 'views_exposed_filter_block:search_groups-page')) {
// The Group Type filter has to listen to changes in
// group type role permissions. Using the role list ensures
// it's cleared correctly.
$build['#cache']['tags'][] = 'config:group_role_list';
}
}
/**
* Implements hook_preprocess_HOOK().
*/
function social_group_preprocess_profile(&$variables) {
$group = _social_group_get_current_group();
if ($group instanceof Group && $variables['elements']['#view_mode'] == 'teaser') {
$account = $variables['elements']['#profile']
->get('uid')->entity;
if (!($member = $group
->getMember($account))) {
return;
}
$roles = $member
->getRoles();
if (isset($roles[$group
->bundle() . '-group_manager'])) {
$variables['badges'] = [
[
'label' => t('Group manager'),
'classes' => [
'badge-secondary teaser__badge',
],
],
];
}
}
}
/**
* Implements hook_preprocess_HOOK().
*/
function social_group_preprocess_views_view(&$variables) {
/** @var \Drupal\views\ViewExecutable $view */
$view =& $variables['view'];
if ($view
->id() === 'group_manage_members') {
$entity = \Drupal::entityTypeManager()
->getStorage('block')
->load('socialblue_local_actions');
$variables['header']['actions'] = \Drupal::entityTypeManager()
->getViewBuilder('block')
->view($entity);
}
}
/**
* Implements hook_block_access().
*
* Check if the user is viewing a closed_group, check if the user is a member.
* If the user is not a member, the user has no access to view this block.
*/
function social_group_block_access(Block $block, $operation, AccountInterface $account) {
$block_id = $block
->getPluginId();
// Lets hide the hero block when adding members directly, it doesnt
// add anything there.
if ($operation === 'view' && $block_id === 'group_hero_block') {
$route_name = \Drupal::routeMatch()
->getRouteName();
$excluded_routes = [
'entity.group_content.add_form',
];
if (in_array($route_name, $excluded_routes, TRUE)) {
return AccessResult::forbidden();
}
}
// This is a list of the blocks that this function cares about, if we're being
// called for a different block we exit early.
$managed_blocks = [
'views_exposed_filter_block:newest_groups-page_all_groups',
'views_block:groups-block_user_groups',
'views_block:upcoming_events-upcoming_events_group',
'views_block:latest_topics-group_topics_block',
];
if (!in_array($block_id, $managed_blocks)) {
return AccessResult::neutral();
}
$group = _social_group_get_current_group();
$user = Drupal::currentUser();
// Check if there is a group set and if its an closed group.
if ($group && $group
->getGroupType()
->id() == 'closed_group' && $account
->id() != 1) {
if ($account
->hasPermission('manage all groups')) {
return AccessResult::neutral();
}
elseif (!$group
->getMember($user)) {
// If it is closed and the current user is not an member of this group,
// then it is not allowed to see these blocks.
$forbidden_blocks = [
'views_block:upcoming_events-upcoming_events_group',
'views_block:latest_topics-group_topics_block',
];
foreach ($forbidden_blocks as $forbidden_block) {
if ($operation == 'view' && $block
->getPluginId() == $forbidden_block) {
return AccessResult::forbidden();
}
}
}
}
// Check if we're alloweed to view the joined groups block.
if ($operation === 'view' && $block
->getPluginId() === 'views_block:groups-block_user_groups') {
// Here we're going to assume by default access is not granted.
$groupController = SocialGroupController::create(\Drupal::getContainer());
$access = $groupController
->myGroupAccess($account);
// If the 'myGroupAccess' returns 'AccessResultNeutral', we have to assume
// that access must be denied.
if ($access instanceof AccessResultNeutral) {
// Return forbidden, since access was not explicitly granted.
return AccessResult::forbidden();
}
return $access;
}
return AccessResult::neutral();
}
/**
* Determine the amount of group_types a user can see.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The user to check for.
*
* @return int
* The amount of group_types
*/
function _social_group_get_current_group_types(AccountInterface $account) {
$group_types = 0;
/** @var \Drupal\group\Entity\GroupTypeInterface $group_type */
foreach (GroupType::loadMultiple() as $group_type) {
$group_types += (int) social_group_can_view_groups_of_type($group_type
->id(), $account);
}
return $group_types;
}
/**
* Delete the group and all of its content.
*/
function _social_group_delete_group() {
// Get the group.
if ($group = _social_group_get_current_group()) {
$group_content_types = GroupContentType::loadByEntityTypeId('node');
$group_content_types = array_keys($group_content_types);
// Get all the node's related to the current group.
$query = \Drupal::database()
->select('group_content_field_data', 'gcfd');
$query
->addField('gcfd', 'entity_id');
$query
->condition('gcfd.gid', $group
->id());
$query
->condition('gcfd.type', $group_content_types, 'IN');
$query
->execute()
->fetchAll();
$entity_ids = $query
->execute()
->fetchAllAssoc('entity_id');
// Store all the node ids.
$nids = array_keys($entity_ids);
// Get all the posts from this group.
$query = \Drupal::database()
->select('post__field_recipient_group', 'pfrg');
$query
->addField('pfrg', 'entity_id');
$query
->condition('pfrg.field_recipient_group_target_id', $group
->id());
$query
->execute()
->fetchAll();
$post_ids = $query
->execute()
->fetchAllAssoc('entity_id');
// Store all the post entity ids.
$posts = array_keys($post_ids);
// Pass the $nids and $posts as 2 parameters in the operations.
// See /social_group/src/Controller/DeleteGroup.php for further process.
$batch = [
'title' => t('Deleting the group and all the content within the group...'),
'init_message' => t("Preparing to delete the group and all it's topic's, event's and post's..."),
'operations' => [
[
'\\Drupal\\social_group\\Controller\\DeleteGroup::deleteGroupAndContent',
[
$nids,
$posts,
],
],
],
'finished' => '\\Drupal\\social_group\\Controller\\DeleteGroup::deleteGroupAndContentFinishedCallback',
];
batch_set($batch);
}
}
/**
* Get all open groups.
*/
function social_group_get_all_open_groups() {
$query = Drupal::service('entity.query')
->get('group')
->condition('type', 'open_group');
return $query
->execute();
}
/**
* Get all open groups.
*/
function social_group_get_all_groups() {
$query = Drupal::service('entity.query')
->get('group')
->sort('type');
return $query
->execute();
}
/**
* Get all group memberships for a certain user.
*
* @param int $uid
* The UID for which we fetch the groups it is member of.
*
* @return array
* List of group IDs the user is member of.
*
* @deprecated in social:8.x-4.2 and is removed from social:8.x-9.6.
* This function is moved to the service social_group.helper_service, use
* getAllGroupsForUser() instead.
*
* @see https://www.drupal.org/node/3026220
*/
function social_group_get_all_group_members($uid) {
@trigger_error(__FUNCTION__ . ' is deprecated in social:8.x-4.2 and is removed from social:8.x-9.6. This function is moved to the service social_group.helper_service, use getAllGroupsForUser() instead. See https://www.drupal.org/node/3026220', E_USER_DEPRECATED);
$groups =& drupal_static(__FUNCTION__);
// Get the memberships for the user if they aren't known yet.
if (!isset($groups[$uid])) {
$group_content_types = GroupContentType::loadByEntityTypeId('user');
$group_content_types = array_keys($group_content_types);
$query = \Drupal::database()
->select('group_content_field_data', 'gcfd');
$query
->addField('gcfd', 'gid');
$query
->condition('gcfd.entity_id', $uid);
$query
->condition('gcfd.type', $group_content_types, 'IN');
$query
->execute()
->fetchAll();
$group_ids = $query
->execute()
->fetchAllAssoc('gid');
$groups[$uid] = array_keys($group_ids);
}
return $groups[$uid];
}
/**
* Implements hook_field_info_alter().
*/
function social_group_field_info_alter(&$info) {
if (isset($info['groups'])) {
$info['groups']['default widget'] = 'social_group_selector_widget';
}
}
/**
* Implements hook_entity_base_field_info_alter().
*/
function social_group_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
if ($entity_type
->id() === 'node' && isset($fields['groups'])) {
/** @var \Drupal\Core\Field\FieldDefinitionInterface[] $fields */
$fields['groups']
->setLabel(t('Group'))
->setDescription('')
->setSetting('handler', 'social')
->setDisplayOptions('form', [
'type' => 'options_select',
'settings' => [],
'weight' => 16,
])
->setDisplayConfigurable('form', TRUE);
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Make sure the correct group is saved when group is selected on node form.
*/
function social_group_form_node_form_alter(&$form, FormStateInterface $form_state) {
if (isset($form['#entity_type']) && $form['#entity_type'] === 'node') {
if (isset($form['groups'])) {
// Check if we are adding a translation or it's the default being editted.
/** @var \Drupal\Core\Entity\EntityForm $form_object */
$form_object = $form_state
->getFormObject();
/** @var \Drupal\social_node\Entity\Node $node */
$node = $form_object
->getEntity();
$is_original_language = (bool) $node
->getFieldValue('default_langcode', 'value');
// If it's not the original, we can safely say its a translation being
// added or editted. In that case we don't want users to override the
// group or visibility. We only allow those changes on the default as
// these can not change per language for now.
if (!$is_original_language) {
$form['field_content_visibility']['widget']['#disabled'] = TRUE;
$form['field_content_visibility']['widget']['#description'] = t('Changing visibility is disabled when translating.');
$form['groups']['widget']['#disabled'] = TRUE;
$form['groups']['widget']['#description'] = t('Changing groups is disabled when translating.');
}
$change_fieldgroup_titles = [
'group_topic_visibility',
'group_event_visibility',
];
foreach ($change_fieldgroup_titles as $fieldgroup_title) {
if (isset($form['#fieldgroups'][$fieldgroup_title])) {
$form['#fieldgroups'][$fieldgroup_title]->label = t('Access permissions')
->render();
}
}
// Lets remove the original submit function in favor of this submit.
foreach ($form['actions']['submit']['#submit'] as $submit_key => $submit_function) {
if ($submit_function === 'group_content_entity_submit') {
unset($form['actions']['submit']['#submit'][$submit_key]);
}
}
/** @var \Drupal\node\NodeInterface $node */
$node = $form_state
->getFormObject()
->getEntity();
// Store if the node is new or not.
$form['is_new'] = [
'#type' => 'value',
'#value' => $node
->isNew(),
];
$form['actions']['submit']['#submit'][] = 'social_group_save_group_from_node';
}
}
}
/**
* Form submit to save the group from a node form.
*
* @param array $form
* Node add or node edit form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state interface.
*/
function social_group_save_group_from_node(array $form, FormStateInterface $form_state) {
// Check if we are adding a translation or it's the default being editted.
/** @var \Drupal\Core\Entity\EntityForm $form_object */
$form_object = $form_state
->getFormObject();
/** @var \Drupal\social_node\Entity\Node $node */
$node = $form_object
->getEntity();
$is_original_language = (bool) $node
->getFieldValue('default_langcode', 'value');
// Check if the created node is new or updated.
$is_new = NULL !== $form_state
->getValue('is_new') ? $form_state
->getValue('is_new') : FALSE;
$original_groups = [];
$groups_to_add = [];
$groups_to_remove = [];
foreach ($form_state
->getValue('groups') as $new_group_key => $new_group) {
$groups_to_add[$new_group['target_id']] = $new_group['target_id'];
}
// The node already exist so lets also change the logic accordingly,
// only if there is already a group that needs to be removed.
if (!empty($form['groups']['widget']['#default_value']) && $form['#form_id'] === 'node_' . $node
->bundle() . '_edit_form') {
$original_groups = $form['groups']['widget']['#default_value'];
foreach ($original_groups as $original_group_key => $original_group) {
if (!in_array($original_group, $groups_to_add)) {
$groups_to_remove[$original_group] = $original_group;
}
else {
unset($groups_to_add[$original_group]);
}
}
}
// Now make sure the relevant GroupContent is removed or added.
// But only when we are on the original language, we don't want to update
// this when translating. The field is disabled for that scenario so no
// need to run this.
if ($is_original_language) {
$setGroupsForNodeService = \Drupal::service('social_group.set_groups_for_node_service');
$setGroupsForNodeService
->setGroupsForNode($node, $groups_to_remove, $groups_to_add, $original_groups, $is_new);
}
}
/**
* Get the allowed visibility options for a given group type.
*
* @param string|null $group_type_id
* The group type. Can be NULL to get visibility when it is not in a group.
* @param \Drupal\Core\Session\AccountInterface|null $account
* The account object that may have impact on the visibility options.
*
* @return array
* An array of visibility options for the given group type.
*/
function social_group_get_allowed_visibility_options_per_group_type($group_type_id, AccountInterface $account = NULL, $entity = NULL, $group = NULL) {
// Get the logged in user.
if ($account === NULL) {
$account = \Drupal::currentUser();
}
$visibility_options = [];
$visibility_options['public'] = FALSE;
$visibility_options['community'] = TRUE;
$visibility_options['group'] = FALSE;
switch ($group_type_id) {
case 'closed_group':
$visibility_options['community'] = FALSE;
$visibility_options['group'] = TRUE;
break;
case 'public_group':
$visibility_options['public'] = TRUE;
$visibility_options['community'] = FALSE;
break;
case 'open_group':
$visibility_options['public'] = FALSE;
$visibility_options['community'] = TRUE;
$visibility_options['group'] = FALSE;
break;
case 'secret_group':
$visibility_options['public'] = FALSE;
$visibility_options['community'] = FALSE;
$visibility_options['group'] = TRUE;
break;
case 'flexible_group':
if (empty($group)) {
$group = _social_group_get_current_group();
}
if ($group !== NULL) {
$visibility_options['public'] = FALSE;
$visibility_options['community'] = FALSE;
$visibility_options['group'] = FALSE;
// Try to retrieve allowed options from Group directly.
$allowed_options = $group
->get('field_group_allowed_visibility')
->getValue();
foreach ($allowed_options as $option) {
$value = $option['value'];
$visibility_options[$value] = TRUE;
}
}
break;
default:
$config = \Drupal::config('entity_access_by_field.settings');
$visibility_options['public'] = TRUE;
if ($config
->get('disable_public_visibility') === 1 && !$account
->hasPermission('override disabled public visibility')) {
// This is a new entity.
if (!$entity) {
$visibility_options['public'] = FALSE;
}
else {
/** @var \Drupal\node\Entity\Node $entity */
if ($entity
->hasField('field_content_visibility')) {
$current_visibility = $entity
->get('field_content_visibility')
->getString();
if ($current_visibility !== 'public') {
$visibility_options['public'] = FALSE;
}
}
}
}
$visibility_options['community'] = TRUE;
$visibility_options['group'] = FALSE;
break;
}
\Drupal::moduleHandler()
->alter('social_group_allowed_visibilities', $visibility_options, $group_type_id);
return $visibility_options;
}
/**
* Implements hook_views_query_alter().
*/
function social_group_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
if (\Drupal::currentUser()
->isAuthenticated()) {
return;
}
if (empty($view->rowPlugin) || !$view->rowPlugin instanceof EntityRow || $view->rowPlugin
->getEntityTypeId() != 'group') {
return;
}
$found = FALSE;
foreach ($query->where as &$conditions) {
foreach ($conditions['conditions'] as &$condition) {
if ($condition['field'] == 'groups_field_data.type') {
$found = TRUE;
break 2;
}
}
}
if (!$found) {
unset($condition);
$condition = [
'field' => 'groups_field_data.type',
'value' => [],
'operator' => 'not in',
];
}
$hiddens = $condition['operator'] == 'not in';
// Define the variable as array otherwise it can be interpreted as string
// And breaking the code below.
$condition['value'] = [];
/** @var \Drupal\group\Entity\GroupTypeInterface $group_type */
foreach (GroupType::loadMultiple() as $group_type) {
$new = !in_array($group_type
->id(), $condition['value']);
$permissions = $group_type
->getAnonymousRole()
->getPermissions();
$hidden = !in_array('view group', $permissions);
if ($new && $hiddens == $hidden) {
$condition['value'][] = $group_type
->id();
}
}
if (!$found) {
if (isset($conditions)) {
$conditions['conditions'][] = $condition;
}
else {
$query->where[] = [
'conditions' => [
$condition,
],
'type' => 'AND',
];
}
}
}
/**
* Implements hook_user_cancel_methods_alter().
*/
function social_group_user_cancel_methods_alter(&$methods) {
$methods['user_cancel_reassign']['title'] .= ' ' . t('Reassign your groups to the super administrator.');
}
/**
* Implements hook_social_user_account_header_create_links().
*
* Adds the "Create Group" link to the content creation menu.
* When the user can only create groups of one type, the user skips the
* group type selection page.
*/
function social_group_social_user_account_header_create_links($context) {
// Create url to create a group.
$route_add_group = Url::fromRoute('entity.group.add_page');
// If we have a user to work with, we can check if we should redirect it to
// the create group overview or a single group.
// If we do not have a user we'll default to the overview.
if (!empty($context['user'])) {
/** @var \Drupal\Core\Session\AccountInterface $user */
$account = $context['user'];
$route_add_group = \Drupal::service('social_group.helper_service')
->getGroupsToAddUrl($account) ?? $route_add_group;
}
return [
'add_group' => [
'#type' => 'link',
'#attributes' => [
'title' => new TranslatableMarkup('Create New Group'),
],
'#title' => new TranslatableMarkup('New Group'),
'#weight' => 500,
] + $route_add_group
->toRenderArray(),
];
}
/**
* Implements hook_social_user_account_header_items().
*
* Adds the My Groups button to the account header block if it's enabled by a
* site manager.
*/
function social_group_social_user_account_header_items(array $context) {
if (\Drupal::config('social_user.navigation.settings')
->get('display_my_groups_icon') !== TRUE) {
return [];
}
// We can't create a "My Groups" link without user.
if (empty($context['user'])) {
return [];
}
return [
'groups' => [
'#type' => 'account_header_element',
'#wrapper_attributes' => [
'class' => [
'desktop',
],
],
'#title' => new TranslatableMarkup('My Groups'),
'#url' => Url::fromRoute('view.groups.page_user_groups', [
'user' => $context['user']
->id(),
]),
'#icon' => 'group',
'#label' => new TranslatableMarkup('My Groups'),
],
];
}
/**
* Implements hook_social_user_account_header_account_links().
*
* Adds the "View my groups" link to the user menu.
*/
function social_group_social_user_account_header_account_links(array $context) {
// We require a user for this link.
if (empty($context['user']) || !$context['user'] instanceof AccountInterface) {
return [];
}
return [
'my_groups' => [
'#type' => 'link',
'#attributes' => [
'title' => new TranslatableMarkup('View my groups'),
],
'#title' => new TranslatableMarkup('My groups'),
'#weight' => 800,
] + Url::fromRoute('view.groups.page_user_groups', [
'user' => $context['user']
->id(),
])
->toRenderArray(),
];
}
/**
* Determine whether a user can see groups of a given type as an outsider.
*
* @param string $type
* The group type to check.
* @param \Drupal\Core\Session\AccountInterface $account
* The user to check for.
*
* @return bool
* Whether the user is allowed to view groups of the given type.
*/
function social_group_can_view_groups_of_type(string $type, AccountInterface $account) : bool {
$group_type = GroupType::load($type);
// If the group type doesn't exist then the user can't see the group type.
if ($group_type === FALSE || $group_type === NULL) {
return FALSE;
}
// Anonymous users have a special role.
if ($account
->isAnonymous()) {
/** @var \Drupal\group\Entity\GroupRoleInterface $role */
return $group_type
->getAnonymousRole()
->hasPermission('view group');
}
// The service that keeps platform roles in sync with group roles for
// outsiders.
/** @var \Drupal\group\GroupRoleSynchronizerInterface $group_role_synchroniser */
$group_role_synchroniser = \Drupal::service('group_role.synchronizer');
/** @var \Drupal\group\Entity\GroupRoleInterface[] $group_roles */
$group_roles = $group_type
->getRoles();
$user_roles = $account
->getRoles(TRUE);
// Authenticated is a locked Role,
// So first we check outsiders also easier on the performance :)
$outsider = $group_type
->getOutsiderRole();
if (NULL !== $outsider && $outsider
->hasPermission('view group')) {
return TRUE;
}
// If members can see it, we also need to show group types because
// we are not calculation memberships.
$member = $group_type
->getMemberRole();
if (NULL !== $member && $member
->hasPermission('view group')) {
return TRUE;
}
// Check each role this user has whether its group counterpart has the correct
// permission. This counts for the advanced group permissions.
// Admin, SM, CM.
foreach ($user_roles as $role) {
$group_role = $group_role_synchroniser
->getGroupRoleId($type, $role);
if ($group_roles[$group_role]
->hasPermission('view group')) {
return TRUE;
}
}
// The user has no role that can view the group type.
return FALSE;
}
/**
* Implements hook_entity_operation_alter().
*/
function social_group_entity_operation_alter(array &$operations, EntityInterface $entity) {
if ($entity
->getEntityTypeId() === 'group_content') {
$titles = [
'edit' => t('Edit'),
'delete' => t('Remove'),
'view' => t('View'),
];
foreach ($operations as $key => &$operation) {
if (isset($titles[$key])) {
$operation['title'] = $titles[$key];
}
}
}
}
/**
* Implements hook_default_route_group_tabs_alter().
*/
function social_group_social_group_default_route_tabs_alter(&$tabs) {
// Unset some tabs created by group.
unset($tabs['group.view'], $tabs['group.edit_form'], $tabs['group.content']);
}
/**
* Implements hook_social_path_manager_group_tabs_alter().
*/
function social_group_social_path_manager_group_tabs_alter(array &$tabs) {
// Unset some tabs created by group.
unset($tabs['group.view'], $tabs['group.edit_form'], $tabs['group.content']);
// Add manage members tab.
$tabs['social_group.membership'] = 'view.group_manage_members.page_group_manage_members';
}
/**
* Implements hook_batch_alter().
*/
function social_group_batch_alter(&$batch) {
if (!isset($batch['source_url'])) {
return;
}
$actions = [
'social_group_members_export_member_action',
'social_group_change_member_role_action',
'social_group_delete_group_content_action',
'social_group_send_email_action',
];
/** @var \Drupal\Core\Url $url */
$url =& $batch['source_url'];
if ($url
->getRouteName() === 'social_group_gvbo.views_bulk_operations.confirm' || $url
->getRouteName() === 'views_bulk_operations.confirm' || $url
->getRouteName() === 'views_bulk_operations.execute_batch' || $url
->getRouteName() === 'social_group_gvbo.views_bulk_operations.execute_configurable') {
// Get the action ID.
$action_id = _social_group_get_action_id($batch);
$batch['sets'][0]['results']['action'] = $action_id;
if (in_array($action_id, $actions, TRUE)) {
$batch['sets'][0]['finished'] = '_social_group_action_batch_finish';
}
}
}
/**
* Function to get the action id of a batch.
*
* @param array $batch
* The batch array.
*
* @return string
* Returns the batch action id.
*/
function _social_group_get_action_id(array &$batch) {
/** @var \Drupal\Core\Form\FormStateInterface $form_state */
$form_state =& $batch['form_state'];
$action_id = '';
if ($form_state instanceof FormStateInterface) {
$data = $form_state
->get('views_bulk_operations');
$action_id = $data['action_id'];
}
else {
foreach ($batch['sets'][0]['operations'] as $operations) {
if (empty($operations) || !is_array($operations)) {
break;
}
foreach ($operations as $operation) {
if (empty($operation) || !is_array($operation)) {
break;
}
foreach ($operation as $items) {
if (empty($items) || !is_array($items)) {
break;
}
if (!empty($items['action_id'])) {
$action_id = $items['action_id'];
break;
}
}
}
}
}
return $action_id;
}
/**
* Action batch finished callback.
*
* @param bool $success
* Was the process successfull?
* @param array $results
* Batch process results array.
* @param array $operations
* Performed operations array.
*/
function _social_group_action_batch_finish($success, array $results, array $operations) {
// When we do a bulk action on all the items in a view, across multiple pages,
// the saveList function needs to be called. So after pre-populating the list
// the actual action is performed on the entities.
if (!empty($results['view_id']) && !empty($results['display_id'])) {
ViewsBulkOperationsBatch::saveList(TRUE, $results, $operations);
return;
}
$operations = array_count_values($results['operations']);
$results_count = 0;
foreach ($operations as $count) {
$results_count += $count;
}
$hook = 'social_group_action_' . $results['action'] . '_finish';
foreach (\Drupal::moduleHandler()
->getImplementations($hook) as $module) {
$function = $module . '_' . $hook;
$messages = $function($success);
if (is_array($messages)) {
$fields = 0;
foreach ($messages as $type => $message) {
if (($type === 'singular' || $type === 'plural') && !empty($message) && is_string($message)) {
$fields++;
}
}
if ($fields === 2) {
$message = \Drupal::translation()
->formatPlural($results_count, $messages['singular'], $messages['plural']);
$type = $success ? MessengerInterface::TYPE_STATUS : MessengerInterface::TYPE_WARNING;
\Drupal::messenger()
->addMessage($message, $type);
}
}
}
}
/**
* Implements hook_social_event_action_ACTION_ID_finish().
*/
function social_group_social_group_action_social_group_members_export_member_action_finish($success) {
if ($success) {
return [
'singular' => '1 selected member has been exported successfully',
'plural' => '@count selected members have been exported successfully',
];
}
return [
'singular' => '1 selected member has not been exported',
'plural' => '@count selected members have not been exported',
];
}
/**
* Implements hook_social_event_action_ACTION_ID_finish().
*/
function social_group_social_group_action_social_group_delete_group_content_action_finish($success) {
if ($success) {
return [
'singular' => '1 selected member has been removed successfully',
'plural' => '@count selected members have been removed successfully',
];
}
return [
'singular' => '1 selected member has not been removed successfully',
'plural' => '@count selected members have been removed successfully',
];
}
/**
* Implements hook_social_event_action_ACTION_ID_finish().
*/
function social_group_social_group_action_social_group_change_member_role_action_finish($success) {
if ($success) {
return [
'singular' => 'The role of 1 selected member has been changed successfully',
'plural' => 'The role of @count selected members have been changed successfully',
];
}
return [
'singular' => 'The role of 1 selected member has not been changed successfully',
'plural' => 'The role of @count selected members have not been changed successfully',
];
}
/**
* Implements hook_social_event_action_ACTION_ID_finish().
*/
function social_group_social_group_action_social_group_send_email_action_finish($success) {
if ($success) {
// When the queue storage module is enabled the email is send in the
// background.
if (\Drupal::moduleHandler()
->moduleExists('social_queue_storage')) {
return [
'singular' => 'Your email will be send to 1 selected member',
'plural' => 'Your email will be send to @count selected members',
];
}
return [
'singular' => 'Your email has been sent to 1 selected member successfully',
'plural' => 'Your email has been sent to @count selected members successfully',
];
}
return [
'singular' => 'Your email has not been sent to 1 selected member successfully',
'plural' => 'Your email has not been sent to @count selected members successfully',
];
}
/**
* Implements hook_preprocess_field().
*/
function social_group_preprocess_field(&$variables) {
$formatter = $variables['element']['#formatter'];
if (in_array($formatter, [
'address_plain',
'address_default',
])) {
$entity = $variables['element']['#object'];
if ($entity && $entity instanceof GroupInterface) {
$social_group_settings = \Drupal::config('social_group.settings');
$address_visibility_settings = $social_group_settings
->get('address_visibility_settings');
if (isset($address_visibility_settings['street_code_private']) && !empty($address_visibility_settings['street_code_private'])) {
if (!$entity
->getMember(\Drupal::currentUser())) {
switch ($formatter) {
case 'address_plain':
if (isset($variables['items'][0]['content']['#address_line1'])) {
$variables['items'][0]['content']['#address_line1'] = NULL;
}
if (isset($variables['items'][0]['content']['#postal_code'])) {
$variables['items'][0]['content']['#postal_code'] = NULL;
}
break;
case 'address_default':
if (isset($variables['items'][0]['content']['address_line1']['#value'])) {
$variables['items'][0]['content']['address_line1']['#value'] = '';
}
if (isset($variables['items'][0]['content']['postal_code']['#value'])) {
$variables['items'][0]['content']['postal_code']['#value'] = '';
}
break;
}
}
}
}
}
}
/**
* Returns a new popover tooltip based on a descriptive text and field name.
*
* @param string $field_name
* The field name to create a unique id for.
* @param \Drupal\Core\StringTranslation\TranslatableMarkup $data_title
* The title of the pop-up.
* @param string $description
* A string containing the description, this needs to be rendered markup.
*
* @return array
* The render array containing the tooltip.
*/
function social_group_render_tooltip($field_name, TranslatableMarkup $data_title, $description) {
$build = [];
$id = Html::getUniqueId('tooltip-' . $field_name);
$icon = Bootstrap::glyphicon('question-sign');
$title = '';
// We special case this for the hero.
if ($field_name === 'group_hero') {
$data_title = t('Access permissions');
// For SKY we can render a bigger icon.
$icon_size = 'icon-small';
if (theme_get_setting('style') === 'sky') {
$icon_size = 'icon-medium';
$title = t('Access permissions');
}
$icon = [
'#type' => "inline_template",
'#template' => '<svg class="icon-white ' . $icon_size . '"><use xlink:href="#icon-cog"></use></svg>',
];
}
$build['toggle'] = [
'#type' => 'link',
'#url' => Url::fromUserInput("#{$id}"),
'#icon' => $icon,
'#title' => $title,
'#attributes' => [
'class' => [
'icon-before',
],
'data-toggle' => 'popover',
'data-container' => 'body',
'data-html' => 'true',
'data-placement' => 'bottom',
'data-title' => $data_title ?: '',
],
];
$build['requirements'] = [
'#type' => 'container',
'#theme_wrappers' => [
'container__file_upload_help',
],
'#attributes' => [
'id' => $id,
'class' => [
'hidden',
'help-block',
],
'aria-hidden' => 'true',
],
];
// As documented in Render API, Note that the value is passed through
// \Drupal\Component\Utility\Xss::filterAdmin(), which strips known XSS
// vectors while allowing a permissive list of HTML tags.
$build['requirements']['descriptions'] = [
'#markup' => $description,
'#allowed_tags' => [
'strong',
'span',
'svg',
'p',
'div',
'em',
'img',
'a',
'span',
'use',
],
];
$variables['popover'] = $build;
return $build;
}
/**
* Implements template_preprocess_form_element().
*/
function social_group_preprocess_fieldset(&$variables) {
// Make sure our flexible group visibility field renders a tooltip, since
// this field is rendered as fieldset with legend and radios as children
// we need to do it in this preprocess.
$element = $variables['element'];
if (!empty($element['#field_name']) && $element['#field_name'] === 'field_content_visibility') {
$description = '';
foreach ($element['#options'] as $key => $label) {
$description .= social_group_allowed_visibility_description($key);
}
// Render a specific tooltip based on a field name and description.
// This is done in the fieldset, next to the <legend>.
$variables['popover'] = social_group_render_tooltip('field_content_visibility', t('Visibility'), $description);
}
}
/**
* Returns a description array for the field_flexible_group_visibility options.
*
* @param string $key
* The join method key.
*
* @return string
* The render array containing the description.
*/
function social_group_allowed_visibility_description($key) {
$description = '';
// We need it to be specified otherwise we can't build the markup.
if (empty($key)) {
return $description;
}
// Add explanatory descriptive text after the icon.
switch ($key) {
case 'public':
// If we want to have the title, lets start with it, otherwise open our
// paragraph.
$description = '<p>';
$description .= '<strong><svg class="icon-small"><use xlink:href="#icon-public"></use></svg></strong>';
$description .= '<strong>' . t('Public')
->render() . '</strong>';
$description .= ' - ' . t('visible to all visitors to the platform.')
->render();
$description .= '</p>';
break;
case 'community':
// If we want to have the title, lets start with it, otherwise open our
// paragraph.
$description = '<p>';
$description .= '<strong><svg class="icon-small"><use xlink:href="#icon-community"></use></svg></strong>';
$description .= '<strong>' . t('Community')
->render() . '</strong>';
$description .= ' - ' . t('only visible to all logged-in users.')
->render();
$description .= '</p>';
break;
case 'group':
// If we want to have the title, lets start with it, otherwise open our
// paragraph.
$description = '<p>';
$description .= '<strong><svg class="icon-small"><use xlink:href="#icon-lock"></use></svg></strong>';
$description .= '<strong>' . t('Group members only')
->render() . '</strong>';
$description .= ' - ' . t('only visible to logged-in group members.')
->render();
$description .= '</p>';
break;
}
// Allow modules to provide their own markup for a given key in the
// group_visibility #options array.
\Drupal::moduleHandler()
->alter('social_group_content_visibility_description', $key, $description);
return $description;
}