social_group.module in Open Social 8.7
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.8 modules/social_features/social_group/social_group.module
- 10.3.x modules/social_features/social_group/social_group.module
- 10.0.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\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\group\Entity\Group;
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\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,
],
],
];
}
/**
* 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) {
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function social_group_form_group_public_group_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$group = $form_state
->getFormObject()
->getEntity();
$form['group_type'] = SocialGroupAddForm::create(\Drupal::getContainer())
->getGroupTypeElement();
$form['group_type']['#default_value'] = $group
->bundle();
// 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()) {
$form['group_type']['#disabled'] = TRUE;
}
else {
$form['group_type']['#prefix'] = '<div id="group-type-result"></div>';
$group_type_element['#ajax'] = [
'callback' => '_social_group_inform_group_type_selection',
'effect' => 'fade',
'event' => 'change',
];
}
// Disable all group types that can't be edited. Because they don't have
// a visibility.
foreach ($form['group_type']['#options'] as $type => $label) {
if (\Drupal::service('social_group.helper_service')
->getDefaultGroupVisibility($type) === NULL) {
$form['group_type'][$type] = [
'#disabled' => TRUE,
];
}
}
$form['#group_children']['group_type'] = 'group_content';
}
/**
* 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 TRUE;
}
/**
* 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\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();
$group_settings_help = [
'#theme' => 'group_settings_help',
'#group_type' => $group
->getGroupType()
->label(),
'#join_method' => _social_group_get_join_methods($group),
'#allowed_visibility' => _social_group_get_allowed_visibility($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';
}
}
}
// Count number of group members.
$group_members_count = \Drupal::service('social_group.group_members_count');
$variables['group_members'] = $group_members_count
->getGroupMemberCount($group);
}
/**
* 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();
}
$group_settings_help = [
'#theme' => 'group_settings_help',
'#group_type' => $group
->getGroupType()
->label(),
'#join_method' => _social_group_get_join_methods($group),
'#allowed_visibility' => _social_group_get_allowed_visibility($group),
];
$variables['group_settings_help'] = \Drupal::service('renderer')
->renderPlain($group_settings_help);
}
/**
* 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' => t('Be added by group managers'),
];
break;
case 'public_group':
case 'open_group':
$join_methods = [
'direct' => t('Free to join'),
'added' => t('Be added by group managers'),
];
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) {
$value = $option['value'];
if ($value === 'direct') {
$join_methods['direct'] = t('Free to join');
}
if ($value === 'added') {
$join_methods['added'] = t('Be added by group managers');
}
}
break;
}
return $join_methods;
}
/**
* 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' => t('Group members'),
];
break;
case 'open_group':
$allowed_visibility = [
'community' => t('Community'),
];
break;
case 'public_group':
$allowed_visibility = [
'public' => t('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) {
$value = $option['value'];
if ($value === 'public') {
$allowed_visibility['public'] = t('Public');
}
if ($value === 'community') {
$allowed_visibility['community'] = t('Community');
}
if ($value === 'group') {
$allowed_visibility['group'] = t('Group members');
}
}
break;
}
return $allowed_visibility;
}
/**
* Implements hook_entity_insert().
*
* On a new group insert, from the type
* open_group or closed_group the Owner gets the group manager role by default.
*/
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();
}
}
}
/**
* 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';
list($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)) {
$form['help'] = [
'#type' => 'item',
'#markup' => t('By submitting this form you will become a member of the group. Please fill out any available fields to complete your membership information.'),
];
$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('Select a member');
$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';
}
if (in_array($form_id, $membership_add_forms)) {
$form['entity_id']['widget'][0]['target_id']['#title'] = t('Select members to add');
$form['entity_id']['widget'][0]['target_id']['#description'] = t('To add multiple members, separate each member with a comma ( , ).');
$form['entity_id']['widget'][0]['target_id']['#type'] = 'social_group_entity_autocomplete';
}
// 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';
}
}
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();
// Get the current group.
$group = _social_group_get_current_group();
// Set the default value in the form.
$group_type_element['#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['#ajax'] = [
'callback' => '_social_group_inform_group_type_selection',
'effect' => 'fade',
'event' => 'change',
];
}
$form['group_type'] = $group_type_element;
$form['#group_children']['group_type'] = 'group_settings';
// Disable all group types that can't be edited. Because they don't have
// a visibility.
foreach (array_keys($form['group_type']['#options']) as $type) {
if (\Drupal::service('social_group.helper_service')
->getDefaultGroupVisibility($type) === NULL) {
$form['group_type'][$type] = [
'#disabled' => TRUE,
];
}
}
$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';
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 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.');
drupal_set_message($text, 'info');
$alert = [
'#type' => 'status_messages',
];
$ajax_response = new AjaxResponse();
$ajax_response
->addCommand(new HtmlCommand('#group-type-result', $alert));
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.
// Can be empty for flexible groups.
$group = _social_group_get_current_group();
if (!empty($form['group_type'])) {
$default_type = $form['group_type']['#default_value'];
$new_type = $form_state
->getValue('group_type');
// 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);
}
}
// 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();
// For multiple Group Members that are valid we add multiple entities.
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'),
];
foreach ($form_state
->getValue('entity_id_new') as $key => $uid) {
$group
->addMember(User::load($uid['target_id']), $values);
}
}
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.
$form_state
->setRedirect('entity.group.canonical', [
'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 public options on visibility field.
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 (!$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);
foreach ($visibility_options as $visibility => $allowed) {
$element[$visibility]['#disabled'] = !$allowed;
}
}
}
/**
* 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']);
}
}
/**
* Implements hook_entity_type_alter().
*/
function social_group_entity_type_alter(array &$entity_types) {
/* @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
$entity_types['group']
->setListBuilderClass('Drupal\\social_group\\Controller\\SocialGroupListBuilder');
}
/**
* 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;
}
/**
* Get current Group entity from the route.
*
* @return \Drupal\group\Entity\GroupInterface
* 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) && !is_null($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] = isset($group) ? $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();
// 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 Open Social 4.2 and will be removed in one of the next major
* updates.
* 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('social_group_get_all_group_members() is deprecated in Open Social 4.2 and will be removed in
one of the next major updates. 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) {
$entity_types = [
'node',
];
if (in_array($entity_type
->id(), $entity_types)) {
if (isset($fields['groups'])) {
/* @var \Drupal\Core\Field\BaseFieldDefinition $fields['groups'] */
$fields['groups']
->setLabel(t('Group'));
$fields['groups']
->setDisplayOptions('form', [
'type' => 'options_select',
'settings' => [],
'weight' => 16,
]);
$fields['groups']
->setDisplayConfigurable('form', TRUE);
$fields['groups']
->setDescription('');
}
}
}
/**
* 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'])) {
$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 = (string) t('Group and visibility');
}
}
// 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\Entity\Node $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) {
/* @var \Drupal\node\Entity\Node $node */
$node = $form_state
->getFormObject()
->getEntity();
// 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.
$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'];
$user_can_create_groups = [];
// Get all available group types.
foreach (GroupType::loadMultiple() as $group_type) {
// When the user has permission to create a group of the current type, add
// this to the create group array.
if ($account
->hasPermission('create ' . $group_type
->id() . ' group')) {
$user_can_create_groups[$group_type
->id()] = $group_type;
}
if (count($user_can_create_groups) > 1) {
break;
}
}
// There's just one group this user can create.
if (count($user_can_create_groups) === 1) {
// When there is only one group allowed, add create the url to create a
// group of this type.
/** @var \Drupal\group\Entity\Group $allowed_group_type */
$allowed_group_type = reset($user_can_create_groups);
$route_add_group = Url::fromRoute('entity.group.add_form', [
'group_type' => $allowed_group_type
->id(),
]);
}
}
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') !== 1) {
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_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) {
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',
];
}