social_group_invite.module in Open Social 8.9
Same filename and directory in other branches
- 10.3.x modules/social_features/social_group/modules/social_group_invite/social_group_invite.module
- 10.0.x modules/social_features/social_group/modules/social_group_invite/social_group_invite.module
- 10.1.x modules/social_features/social_group/modules/social_group_invite/social_group_invite.module
- 10.2.x modules/social_features/social_group/modules/social_group_invite/social_group_invite.module
The Social Invite group module.
File
modules/social_features/social_group/modules/social_group_invite/social_group_invite.moduleView source
<?php
/**
* @file
* The Social Invite group module.
*/
use Drupal\block\Entity\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\Exception\MissingDataException;
use Drupal\Core\Url;
use Drupal\ginvite\GroupInvitation as GroupInvitationWrapper;
use Drupal\ginvite\Plugin\GroupContentEnabler\GroupInvitation;
use Drupal\group\Entity\GroupContent;
use Drupal\group\Entity\GroupInterface;
use Drupal\group\Entity\GroupRoleInterface;
use Drupal\group\Entity\GroupTypeInterface;
use Drupal\social_group\Entity\Group;
/**
* Implements hook_menu_local_actions_alter().
*/
function social_group_invite_menu_local_actions_alter(&$local_actions) {
// Remove "Invite members" button.
if (isset($local_actions['ginvite.invitation.bulk'])) {
unset($local_actions['ginvite.invitation.bulk']);
}
// Remove "Invite member" button.
if (isset($local_actions['ginvite.invite_member'])) {
unset($local_actions['ginvite.invite_member']);
}
}
/**
* Implements hook_menu_local_tasks_alter().
*/
function social_group_invite_menu_local_tasks_alter(&$data, $route_name, RefinableCacheableDependencyInterface $cacheability) {
// Add tasks on these route for invite Groups/Events.
if ($route_name === 'view.social_group_user_invitations.page_1') {
$tabs_to_remove = [
'social_user.groups',
'social_user.stream',
'social_user.topics',
'social_user.events',
'social_profile.information',
'profile.user_page:profile',
'entity.user.canonical',
'entity.user.edit_form',
];
foreach ($tabs_to_remove as $task_name) {
if (!empty($data['tabs'][0][$task_name])) {
unset($data['tabs'][0][$task_name]);
}
}
}
// Else we remove our custom group task.
// This will render for example on the user profile etc.
if ($route_name !== 'view.social_group_user_invitations.page_1' && $route_name !== 'view.user_event_invites.page_user_event_invites') {
if (!empty($data['tabs'][0]['social_group_invite.user_groups'])) {
unset($data['tabs'][0]['social_group_invite.user_groups']);
}
}
}
/**
* Implements hook_block_access().
*/
function social_group_invite_block_access(Block $block, $operation, AccountInterface $account) {
$route_name = \Drupal::routeMatch()
->getRouteName();
$routes_to_check = [
'entity.group_content.add_form',
'entity.group_content.delete_form',
];
// Only when on the confirm page of removing or adding invites
// we remove the block for tasks and heros,the cancel button
// will allow users to go back.
if (in_array($route_name, $routes_to_check)) {
$block_id = $block
->getPluginId();
/** @var \Drupal\group\Entity\GroupContent $group_content */
$group_content = \Drupal::routeMatch()
->getParameter('group_content');
// Only if we are sure it's a group invitation.
if (NULL !== $group_content && NULL !== $group_content
->getGroupContentType() && $group_content
->getGroupContentType()
->getContentPluginId() === 'group_invitation') {
// This is a list of the blocks that this function cares about,
// if we're being called for a different block we exit early.
$hide_blocks = [
'group_hero_block',
'local_tasks_block',
];
if (!in_array($block_id, $hide_blocks)) {
return AccessResult::neutral();
}
// Remove the blocks.
return AccessResult::forbidden();
}
}
}
/**
* Implements hook_preprocess_HOOK().
*/
function social_group_invite_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);
}
// See function social_group_preprocess_views_view(&$variables).
// We have to override the local actions block.
// and render our own block instance in the view for placement.
// hook_theme_registry_alter will ensure our hooks is invoked later.
// That is also why hook_menu_local_actions_alter won't work.
// Get current group so we can build correct links.
if ($view
->id() === 'group_manage_members') {
if (_social_group_invite_current_type_enabled_invites()) {
$entity = \Drupal::entityTypeManager()
->getStorage('block')
->load('socialinviteactionsblock');
if (NULL !== $entity) {
$block_content = \Drupal::entityTypeManager()
->getViewBuilder('block')
->view($entity);
if (!empty($block_content)) {
$variables['header']['actions'] = $block_content;
}
}
}
else {
$entity = \Drupal::entityTypeManager()
->getStorage('block')
->load('socialblue_local_actions');
$variables['header']['actions'] = \Drupal::entityTypeManager()
->getViewBuilder('block')
->view($entity);
}
}
// Implement button to go back to the group for our custom view.
if ($variables['view']
->id() === 'social_group_invitations') {
$group = _social_group_get_current_group();
$variables['more'] = [
'#title' => t('Back to group'),
'#type' => 'link',
'#url' => Url::fromRoute('entity.group.canonical', [
'group' => $group
->id(),
]),
'#attributes' => [
'class' => [
'btn',
'btn-default',
'btn-raised',
'waves-effect',
],
],
];
}
}
/**
* Implements hook_preprocess_HOOK().
*/
function social_group_invite_preprocess_views_view_field(&$variables) {
$view = $variables['view'];
// Alter some group invite fields for better representation in the
// custom social overview.
if ($view
->id() === 'social_group_invitations') {
// For invite status we want to show the readable name.
if ($variables['field']->field === 'invitation_status' && !empty($variables['row']->_entity)) {
/** @var \Drupal\group\Entity\GroupContent $entity */
$entity = $variables['row']->_entity;
// If we have a invite we check it's status.
$field_value = _social_group_get_group_invite_value($entity, 'invitation_status');
if (NULL !== $field_value) {
// Field values are always returned as string,
// so lets typecast them for now.
$current_status = (int) $field_value;
switch ($current_status) {
case GroupInvitation::INVITATION_PENDING:
$output = t('Pending reply');
break;
case GroupInvitation::INVITATION_ACCEPTED:
$output = t('Accepted and joined');
break;
case GroupInvitation::INVITATION_REJECTED:
$output = t('Declined');
break;
default:
$output = $variables['output'];
break;
}
$variables['output'] = $output;
}
}
// If user accepted the invite, don't render the actions.
if ($variables['field']->field === 'dropbutton' && !empty($variables['row']->_entity)) {
$entity = $variables['row']->_entity;
$field_value = _social_group_get_group_invite_value($entity, 'invitation_status');
// Field values are always returned as string,
// so lets typecast them for now.
if (NULL !== $field_value) {
$current_status = (int) $field_value;
if ($current_status === GroupInvitation::INVITATION_ACCEPTED) {
$variables['output'] = '';
}
}
}
}
}
/**
* Return a group invites field value if it exists.
*
* @param \Drupal\group\Entity\GroupContent $entity
* Group invitation.
* @param string $field_name
* The field name.
*
* @return string|null
* The field value or NULL if there isn't any.
*/
function _social_group_get_group_invite_value(GroupContent $entity, $field_name) {
$field_value = NULL;
if ($entity
->hasField($field_name)) {
try {
/** @var \Drupal\Core\TypedData\ListInterface $field */
$field = $entity
->get($field_name)
->first();
if (NULL !== $field && !empty($field
->getValue())) {
$field_value = $field
->getValue()['value'];
}
} catch (MissingDataException $e) {
}
}
return $field_value;
}
/**
* Check if current group it's group_type has invites enabled.
*
* @return bool
* TRUE if it's enabled.
*/
function _social_group_invite_current_type_enabled_invites() {
$enabled = FALSE;
$group = _social_group_get_current_group();
// Check if group type has content plugin enabled.
if ($group instanceof GroupInterface) {
$group_type = $group
->getGroupType();
if ($group_type
->hasContentPlugin('group_invitation')) {
$enabled = TRUE;
}
}
return $enabled;
}
/**
* Implements hook_theme_registry_alter().
*/
function social_group_invite_theme_registry_alter(&$theme_registry) {
// Unfortunately the preprocess functions aren't ordered by module weight.
// Changing module weight doesn't work, also with dependency set to
// social_group this should be dealt with but isnt.
// So we enforce our preprocess after social_group.
if (!empty($theme_registry['views_view']['preprocess functions'])) {
$current_key = array_search('social_group_invite_preprocess_views_view', $theme_registry['views_view']['preprocess functions'], FALSE);
unset($theme_registry['views_view']['preprocess functions'][$current_key]);
// Give it a new key all the way at the end.
$theme_registry['views_view']['preprocess functions'][] = 'social_group_invite_preprocess_views_view';
}
}
/**
* Implements hook_preprocess_HOOK().
*/
function social_group_invite_preprocess_page_title(&$variables) {
// Add count of pending invites to the page title for a group.
if (\Drupal::routeMatch()
->getParameter('view_id') === 'social_group_invitations' && !empty(\Drupal::routeMatch()
->getParameter('group'))) {
$group = _social_group_get_current_group();
if (!empty($group
->label())) {
$loader = \Drupal::service('ginvite.invitation_loader');
$count = count($loader
->loadByProperties([
'gid' => $group
->id(),
]));
$title = \Drupal::translation()
->formatPlural($count, '1 membership invite for group: @group_name', '@count membership invites for group: @group_name', [
'@group_name' => $group
->label(),
]);
$variables['title'] = $title;
$variables['#cache']['tags'][] = 'group_content_list:group:' . $group
->id();
$variables['#cache']['tags'][] = 'group_content_list:plugin:group_invitation:group:' . $group
->id();
}
}
// Add count of pending invites to the page title for a user.
if (\Drupal::routeMatch()
->getParameter('view_id') === 'social_group_user_invitations' && !empty(\Drupal::routeMatch()
->getParameter('user'))) {
$loader = \Drupal::service('ginvite.invitation_loader');
$count = count($loader
->loadByUser());
$translation = \Drupal::translation()
->formatPlural($count, '1 group invite', '@count group invites');
$variables['title'] = $translation;
$user = \Drupal::routeMatch()
->getParameter('user');
if (is_string($user)) {
$variables['#cache']['tags'][] = 'group_content_list:entity:' . $user;
$variables['#cache']['tags'][] = 'group_content_list:plugin:group_invitation:entity:' . $user;
}
}
}
/**
* Implements hook_form_alter().
*/
function social_group_invite_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// We need to write a custom form alter, since the ginvite module
// has it's own routes, with no group context that loads the
// join form. Decline is done differently. Due to the fact on joining
// users need to fill in their membership fields.
$social_group_types = [
'open_group',
'closed_group',
'public_group',
];
// Also include any added optional group types.
\Drupal::moduleHandler()
->alter('social_group_types', $social_group_types);
$join_forms = [];
foreach ($social_group_types as $social_group_type) {
$join_forms[] = "group_content_{$social_group_type}-group_membership_group-join_form";
}
// Perform alterations on joining / leaving groups.
if (in_array($form_id, $join_forms)) {
// Add custom redirect form submit.
$form['actions']['submit']['#submit'][] = '_social_group_invite_action_form_submit';
}
}
/**
* Form submit for group invite join form.
*/
function _social_group_invite_action_form_submit($form, FormStateInterface $form_state) {
$invite_content = \Drupal::routeMatch()
->getParameter('group_content');
$group = '';
if ($invite_content instanceof GroupContent) {
$group = $invite_content
->getGroup();
}
if (is_string($invite_content)) {
$group_content = GroupContent::load($invite_content);
$group = $group_content
->getGroup();
}
// Redirect user back to the group they joined.
if ($group instanceof Group) {
$form_state
->setRedirect('entity.group.canonical', [
'group' => $group
->id(),
[],
]);
}
}
/**
* A specific Group Type Role, will get default invite permissions.
*
* @param \Drupal\group\Entity\GroupRoleInterface $role
* The role that we will add the default permissions to.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
function social_group_invite_set_default_permissions_for_role_on_group_type(GroupRoleInterface $role) {
$role
->grantPermissions([
'delete any invitation',
'delete own invitations',
'invite users to group',
'view group invitations',
]);
$role
->save();
}
/**
* A specific Group Type, where will assign invite permissions.
*
* @param \Drupal\group\Entity\GroupTypeInterface $group_type
* The Group type role that we will add the default permissions to.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
function social_group_invite_set_default_permissions_for_group_type(GroupTypeInterface $group_type) {
/** @var \Drupal\group\Entity\GroupRoleInterface $roles */
$roles = $group_type
->getRoles();
// Make sure this is done for managers and admins.
if (!empty($roles[$group_type
->id() . '-group_manager'])) {
$role = $roles[$group_type
->id() . '-group_manager'];
social_group_invite_set_default_permissions_for_role_on_group_type($role);
}
if (!empty($roles[$group_type
->id() . '-group_admin'])) {
$role = $roles[$group_type
->id() . '-group_admin'];
social_group_invite_set_default_permissions_for_role_on_group_type($role);
}
}
/**
* Implements hook_preprocess_activity().
*/
function social_group_invite_preprocess_activity(&$variables) {
$activity = $variables['elements']['#activity'];
$related_entity = $activity
->getRelatedEntity();
// If the related entity is a piece of GroupContent and coming from the
// ginvite group_invitation plugin, lets link the entity to the group itself.
if (!empty($related_entity) && $related_entity instanceof GroupContent && $related_entity
->getGroupContentType()
->getContentPluginId() === 'group_invitation') {
$group = $related_entity
->getGroup();
if ($group instanceof Group) {
$variables['full_url'] = $group
->urlInfo('canonical');
}
}
}
/**
* Implements hook_preprocess_group().
*/
function social_group_invite_preprocess_group(&$variables) {
/** @var \Drupal\group\Entity\GroupInterface $group */
$group = $variables['group'];
$group_type = $group
->getGroupType();
$variables['user_is_invited'] = FALSE;
// Only for groups that have invites enabled.
if (!$group_type
->hasContentPlugin('group_invitation')) {
return;
}
// Only for LU.
$account = \Drupal::currentUser();
if ($account
->isAnonymous()) {
return;
}
// Check if the user (entity_id) has a pending invite for the group.
$properties = [
'entity_id' => $account
->id(),
'gid' => $group
->id(),
'invitation_status' => GroupInvitation::INVITATION_PENDING,
];
/** @var \Drupal\ginvite\GroupInvitationLoaderInterface $loader */
$loader = \Drupal::service('ginvite.invitation_loader');
$invitations = $loader
->loadByProperties($properties);
// We have pending invites, let's build a button to accept or decline one.
if ($invitations > 0) {
// Build routes.
$invitation = reset($invitations);
if ($invitation instanceof GroupInvitationWrapper) {
// Lets grab the group content so we can build the URL.
$group_content = $invitation
->getGroupContent();
if ($group_content instanceof GroupContent) {
$variables['user_is_invited'] = TRUE;
// It will become two links with a dropdown button.
$variables['group_invite_accept_operations_url'] = Url::fromRoute('ginvite.invitation.accept', [
'group_content' => $group_content
->id(),
]);
$variables['group_invite_decline_operations_url'] = Url::fromRoute('ginvite.invitation.decline', [
'group_content' => $group_content
->id(),
]);
$variables['#cache']['tags'][] = 'group_content_list:entity:' . $account
->id();
$variables['#cache']['tags'][] = 'group_content_list:plugin:group_invitation:entity:' . $account
->id();
$variables['#cache']['contexts'][] = 'user';
}
}
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function social_group_invite_form_social_group_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$invite_config = \Drupal::config('social_group.settings')
->get('group_invite');
$form['group_invite'] = [
'#type' => 'details',
'#title' => t('Group invite settings'),
];
$default_subject = '[current-user:display-name] has invited you to join a group on [site:name].';
$form['group_invite']['invite_subject'] = [
'#type' => 'textfield',
'#title' => t('Subject'),
'#default_value' => $invite_config['invite_subject'] ?? $default_subject,
'#required' => TRUE,
];
$default_message = "Hi,<br /><br /> I would like to invite you to join my group [group:title] on [site:name].<br /><br />Kind regards,<br />[current-user:display-name]<br /><br />\r\n<table class=\"btn-wrapp\">\r\n\t<tbody>\r\n\t\t<tr>\r\n\t\t\t<td class=\"align-center\"><a class=\"btn-link btn-link-bg btn-link-one\" href=\"[group_content:register_link]\">View group</a></td>\r\n\t\t\t<td class=\"align-center\"><a class=\"btn-link btn-link-bg btn-link-one\" href=\"[site:url]\">About [site:name]</a></td>\r\n\t\t</tr>\r\n\t</tbody>\r\n</table>";
$form['group_invite']['invite_message'] = [
'#type' => 'textarea',
'#title' => t('Message'),
'#default_value' => $invite_config['invite_message'] ?? $default_message,
'#required' => TRUE,
];
$form['actions']['submit']['#submit'][] = '_social_group_invite_settings_submit';
}
/**
* Sets settings for invite mail message.
*/
function _social_group_invite_settings_submit($form, FormStateInterface $form_state) {
\Drupal::configFactory()
->getEditable('social_group.settings')
->set('group_invite.invite_subject', $form_state
->getValue('invite_subject'))
->set('group_invite.invite_message', $form_state
->getValue('invite_message'))
->save();
}
/**
* Implements hook_mail_alter().
*/
function social_group_invite_mail_alter(&$message) {
if ($message['id'] === 'ginvite_invite') {
$params = $message['params'];
$token_service = \Drupal::token();
$language_manager = \Drupal::languageManager();
$langcode = $message['langcode'];
$language = $language_manager
->getLanguage($langcode);
$original_language = $language_manager
->getConfigOverrideLanguage();
$language_manager
->setConfigOverrideLanguage($language);
// Load group invite configuration.
$group_config = \Drupal::config('social_group.settings');
$invite_settings = $group_config
->get('group_invite');
// Alter message and subject if it configured.
if (!is_null($invite_settings) && isset($invite_settings['invite_subject'], $invite_settings['invite_message'])) {
$invitation_subject = $invite_settings['invite_subject'];
$invitation_body = $invite_settings['invite_message'];
unset($params['existing_user']);
$body = $token_service
->replace($invitation_body, $params);
$subject = $token_service
->replace($invitation_subject, $params);
$message['subject'] = $subject;
$message['body'][0] = $body;
}
$language_manager
->setConfigOverrideLanguage($original_language);
}
}