social_group.module in Open Social 8.4
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.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.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;
/**
* 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 $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\group\Entity\GroupInterface $group */
$group = $variables['group'];
$variables['joined'] = FALSE;
$variables['closed_group'] = FALSE;
$variables['cta'] = '';
$variables['closed_group_lock'] = FALSE;
$account = \Drupal::currentUser();
$group_type_id = $group
->getGroupType()
->id();
if ($group_type_id == 'closed_group') {
$variables['closed_group_lock'] = TRUE;
}
// 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)) {
$variables['group_operations_url'] = Url::fromRoute('entity.group.join', [
'group' => $group
->id(),
]);
}
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 (!empty($group->field_group_image->entity)) {
$variables['group_hero_styled_image_url'] = ImageStyle::load('social_xx_large')
->buildUrl($group->field_group_image->entity
->getFileUri());
}
// 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.
$variables['group_members'] = count($group
->getMembers());
}
/**
* 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();
}
}
/**
* 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') {
// 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, he 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'][] = 'group_membership.roles.permissions';
$build['#cache']['contexts'][] = 'group_membership.audience';
$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_content';
// 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['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');
}
}
}
/**
* 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.
$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) {
$group = _social_group_get_current_group();
// 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);
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']);
}
/**
* 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_content']
->setListBuilderClass('Drupal\\social_group\\Controller\\SocialGroupContentListBuilder');
$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'][] = 'group_membership.roles.permissions';
}
/**
* 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_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();
}
if ($block
->getPluginId() == 'views_exposed_filter_block:newest_groups-page_all_groups' && $operation == 'view' && $account
->isAnonymous()) {
$group_types = 0;
/** @var \Drupal\group\Entity\GroupTypeInterface $group_type */
foreach (GroupType::loadMultiple() as $group_type) {
$group_types += (int) $group_type
->getAnonymousRole()
->hasPermission('view group');
}
if ($group_types < 2) {
return AccessResult::forbidden();
}
}
$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 he 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();
}
/**
* 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 group memberships for a certain user.
*
* @param int $uid
* The UID for which we fetch the groups he 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.
if ($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) {
// 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;
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 */
$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;
}
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';
/** @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.
*/
function social_group_social_user_account_header_create_links($context) {
return [
'add_group' => [
'#type' => 'link',
'#attributes' => [
'title' => new TranslatableMarkup('Create New Group'),
],
'#title' => new TranslatableMarkup('New Group'),
'#weight' => 500,
] + Url::fromRoute('entity.group.add_page')
->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(),
];
}