social_private_message.module in Open Social 10.0.x
Same filename and directory in other branches
- 8.9 modules/social_features/social_private_message/social_private_message.module
- 8 modules/social_features/social_private_message/social_private_message.module
- 8.2 modules/social_features/social_private_message/social_private_message.module
- 8.3 modules/social_features/social_private_message/social_private_message.module
- 8.4 modules/social_features/social_private_message/social_private_message.module
- 8.5 modules/social_features/social_private_message/social_private_message.module
- 8.6 modules/social_features/social_private_message/social_private_message.module
- 8.7 modules/social_features/social_private_message/social_private_message.module
- 8.8 modules/social_features/social_private_message/social_private_message.module
- 10.3.x modules/social_features/social_private_message/social_private_message.module
- 10.1.x modules/social_features/social_private_message/social_private_message.module
- 10.2.x modules/social_features/social_private_message/social_private_message.module
The Social Privagte Message module.
File
modules/social_features/social_private_message/social_private_message.moduleView source
<?php
/**
* @file
* The Social Privagte Message module.
*/
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
use Drupal\private_message\Entity\PrivateMessageThreadInterface;
use Drupal\social_private_message\DeletedUser;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\ViewExecutable;
/**
* Implements hook_element_info_alter().
*/
function social_private_message_element_info_alter(array &$info) {
if (isset($info['text_format']['#process'])) {
$info['text_format']['#process'][] = 'social_private_message_filter_process_format';
}
}
/**
* Remove ability of selecting format on private message (use plain_text only).
*/
function social_private_message_filter_process_format($element) {
// Fields listed here will have plain_text format only.
$plain_text_fields = [
'edit-message-0',
];
if ($element['#type'] == 'text_format' && in_array($element['#id'], $plain_text_fields)) {
$element['#format'] = 'plain_text';
$element['format']['format']['#access'] = FALSE;
$element['format']['format']['#value'] = 'plain_text';
$element['format']['help']['#access'] = FALSE;
$element['format']['format']['#options'] = [
'plain_text' => 'Plain Text',
];
}
return $element;
}
/**
* Implements hook_form_FORMID_alter().
*/
function social_private_message_form_private_message_thread_delete_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// Redirect cancel to our inbox.
$form['actions']['cancel']['#url'] = Url::fromRoute('social_private_message.inbox');
// Redirect delete to our inbox.
$form['actions']['submit']['#submit'][] = 'social_private_message_thread_delete_redirect';
}
/**
* Implements hook_form_FORMID_alter().
*/
function social_private_message_form_private_message_add_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['members']['widget']['target_id']['#description'] = t('The member(s) of the private message thread. Add multiple members by seperating them with a comma.');
// Add CTRL/CMD + Enter is submit.
$form['#attached']['library'][] = 'social_post/keycode-submit';
$form['#attached']['library'][] = 'social_private_message/validator';
$form['#attached']['drupalSettings']['social_private_message']['validator'] = t('Please select an item in the list.');
// Determine if the form is an PMT edit form.
$form_is_edit_form = $form_state
->has('thread_members');
// There's an alternative submit. We need to change its value.
foreach ($form['actions'] as $key => &$actions) {
// Must be pretty sure it's a submit button.
if (!(is_array($actions) && isset($actions['#submit']) && is_array($actions['#submit']))) {
continue;
}
// Key must have submit in it.
if (strpos($key, 'submit-') === FALSE) {
continue;
}
$actions['#value'] = t('Send');
$actions['#submit'][] = 'social_private_message_redirect';
unset($actions['#ajax']);
if (!($form_is_edit_form && \Drupal::routeMatch()
->getRouteName() == 'entity.private_message_thread.canonical' && \Drupal::routeMatch()
->getParameter('private_message_thread')
->get('members')
->count() != count($form_state
->get('thread_members')))) {
continue;
}
$callbacks = $actions['#submit'];
$actions['#submit'] = [];
foreach ($callbacks as $callback) {
if ($callback == '::save') {
$actions['#submit'][] = '_social_private_message_members';
}
$actions['#submit'][] = $callback;
}
}
// Alter the users widget.
$form['members']['widget']['#required'] = TRUE;
$form['members']['widget']['#description'] = '';
// Add the required tag.
$form['members']['widget']['#attributes']['required'] = 'required';
// Unset the empty and anonymous option.
unset($form['members']['widget']['#options']['0']);
unset($form['members']['widget']['#options']['_none']);
// Unset current user.
unset($form['members']['widget']['#options'][Drupal::currentUser()
->id()]);
// Alter the message widget.
// Add the required tag.
$form['message']['widget']['#attributes']['required'] = 'required';
// Normal submit button shoud say 'Next'.
$form['actions']['submit']['#value'] = t('Send');
$form['actions']['submit']['#submit'][] = 'social_private_message_redirect';
// Add form class based on form type (create/edit).
if ($form_is_edit_form) {
$form_class = 'message__thread_create';
// In edit mode, remove the label from the message field.
$form['message']['widget'][0]['#title'] = '';
}
else {
$form_class = 'message__thread_edit';
}
$form['#attributes']['class'][] = $form_class;
}
/**
* Redirects the form to the inbox.
*
* @param array $form
* The form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form_state.
*/
function social_private_message_thread_delete_redirect(array $form, FormStateInterface $form_state) {
// Set a nice message.
\Drupal::messenger()
->addStatus(t('Your message has been deleted.'));
// Force redirect to the inbox.
$url = Url::fromRoute('social_private_message.inbox');
$form_state
->setRedirectUrl($url);
// Unset cache tags for user profiles.
$build_info = $form_state
->getBuildInfo();
if (isset($build_info['callback_object'])) {
/** @var \Drupal\private_message\Form\PrivateMessageThreadDeleteForm $pm_thread_form */
$pm_thread_form = $build_info['callback_object'];
$pm_thread = $pm_thread_form
->getEntity();
/** @var \Drupal\private_message\Entity\PrivateMessageThread $pm_thread */
if ($pm_thread) {
$members = $pm_thread
->getMembers();
$cache_tags = [];
foreach ($members as $member) {
/** @var \Drupal\user\Entity\User $member */
if ($member) {
foreach ($member
->getCacheTagsToInvalidate() as $cache_tag) {
$cache_tags[] = $cache_tag;
}
}
}
\Drupal::service('cache_tags.invalidator')
->invalidateTags($cache_tags);
}
}
}
/**
* Redirects the form to the inbox.
*
* @param array $form
* The form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form_state.
*/
function social_private_message_redirect(array $form, FormStateInterface $form_state) {
// Set a nice message.
\Drupal::messenger()
->addStatus(t('Your message has been created.'));
// Force redirect to the inbox.
$url = Url::fromRoute('social_private_message.inbox');
$form_state
->setRedirectUrl($url);
}
/**
* Add deleted users to list of thread members.
*
* @param array $form
* The form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form_state.
*/
function _social_private_message_members(array $form, FormStateInterface $form_state) {
$only_exist_members = $form_state
->get('thread_members');
$members_with_deleted = \Drupal::routeMatch()
->getParameter('private_message_thread')
->get('members')
->getValue();
$thread_members = [];
foreach ($members_with_deleted as $value) {
$found = FALSE;
/** @var \Drupal\user\UserInterface $member */
foreach ($only_exist_members as $member) {
if ($value['target_id'] == $member
->id()) {
$found = TRUE;
break;
}
}
if ($found) {
$thread_members[] = $member;
}
else {
$thread_members[] = new DeletedUser($value['target_id']);
}
}
$form_state
->set('thread_members', $thread_members);
}
/**
* Implements hook_js_alter().
*/
function social_private_message_js_alter(&$javascript, AttachedAssetsInterface $assets) {
// Remove Js coming from the private_message module.
if (isset($javascript['modules/contrib/private_message/js/private_message_inbox_block.js'])) {
unset($javascript['modules/contrib/private_message/js/private_message_inbox_block.js']);
}
}
/**
* Implements hook_entity_view_alter().
*/
function social_private_message_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
// Threads.
if ($entity
->getEntityTypeId() === 'private_message_thread') {
/** @var \Drupal\private_message\Entity\PrivateMessageThread $thread */
$thread =& $entity;
$only_exist_members = $thread
->getMembers();
// View mode inbox.
if ($build['#view_mode'] === 'inbox') {
$members_with_deleted = $thread
->get('members')
->getValue();
$members_string = [];
$display_name = '';
foreach (array_keys($members_with_deleted) as $key) {
if (isset($only_exist_members[$key])) {
/** @var \Drupal\user\UserInterface $member */
$member = $only_exist_members[$key];
if (\Drupal::currentUser()
->id() === $member
->id()) {
unset($members_with_deleted[$key], $only_exist_members[$key]);
}
else {
$user_profile = \Drupal::entityTypeManager()
->getStorage('profile')
->loadByUser($member, 'profile');
$content = \Drupal::entityTypeManager()
->getViewBuilder('profile')
->view($user_profile, 'name_raw');
$members_string[] = $content;
}
}
else {
$members_string[] = t('Deleted user');
}
}
// Count the amount of members.
$member_count = count($members_with_deleted);
$profile_picture = [];
if ($member_count === 1) {
$recipient = end($only_exist_members);
if ($recipient instanceof UserInterface) {
$user_profile = \Drupal::entityTypeManager()
->getStorage('profile')
->loadByUser($recipient, 'profile');
$display_name = \Drupal::entityTypeManager()
->getViewBuilder('profile')
->view($user_profile, 'name_raw');
}
else {
// Make this an array, since we no longer render in here.
$display_name = [
'#markup' => t('Deleted user'),
];
$recipient = User::load(1);
}
// Load compact notification view mode of the attached profile.
if ($recipient instanceof User) {
$storage = \Drupal::entityTypeManager()
->getStorage('profile');
if (!empty($storage)) {
$user_profile = $storage
->loadByUser($recipient, 'profile');
if ($user_profile) {
$content = \Drupal::entityTypeManager()
->getViewBuilder('profile')
->view($user_profile, 'compact_notification');
// Add to a new field, so twig can render it.
$profile_picture = $content;
}
}
}
}
// Add either the profile picture or the group picture.
if ($member_count > 1) {
$build['members']['#markup'] = '<div class="avatar-icon avatar-group-icon avatar-group-icon--medium"></div>';
// Add members names.
$build['membernames'] = $members_string;
$build['membernames']['#prefix'] = '<strong>';
$build['membernames']['#suffix'] = '</strong>';
}
elseif (!empty($profile_picture && $display_name)) {
$build['members'] = $profile_picture;
// Add members name.
$build['membernames'] = $display_name;
$build['membernames']['#prefix'] = '<strong>';
$build['membernames']['#suffix'] = '</strong>';
}
}
elseif ($build['#view_mode'] === 'full') {
$socialPrivateMessageService = \Drupal::service('social_private_message.service');
$socialPrivateMessageService
->updateLastThreadCheckTime($entity);
$build['#prefix'] = '';
$build['#suffix'] = '';
if ($display
->getComponent('private_message_form') && count($only_exist_members) == 1) {
$build['private_message_form'] = NULL;
}
}
}
elseif ($entity
->getEntityTypeId() === 'private_message') {
// View mode inbox.
if ($build['#view_mode'] === 'inbox') {
// Remove prefix and suffix.
$build['#prefix'] = '';
$build['#suffix'] = '';
}
elseif ($build['#view_mode'] === 'full') {
$wrapper_class = NULL;
/** @var \Drupal\private_message\Entity\PrivateMessage $entity */
if (\Drupal::currentUser()
->id() === $entity
->getOwnerId()) {
$wrapper_class = 'message__by-me';
// Current user is 'You'.
$build['owner'][0]['#plain_text'] = t('You');
}
elseif (empty($entity
->getOwner())) {
$wrapper_class = 'message__deleted';
}
if (!empty($wrapper_class)) {
$build['#prefix'] = '<div class="' . $wrapper_class . '">' . $build['#prefix'];
$build['#suffix'] = '</div>' . $build['#suffix'];
}
}
}
}
/**
* Implements hook_thread_view_alter().
*/
function social_private_message_thread_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
$current_user = Drupal::currentUser();
if ($display
->getComponent('delete_link') && $current_user
->hasPermission('use private messaging system') && $current_user
->hasPermission('delete private messages thread')) {
$url = Url::fromRoute('entity.private_message_thread.delete_form', [
'private_message_thread' => $entity
->id(),
]);
$build['delete_link'] = [
'#prefix' => '',
'#suffix' => '',
'#type' => 'link',
'#url' => $url,
'#title' => t('Delete thread'),
];
}
else {
unset($build['delete_link']);
}
if (!$current_user
->hasPermission('use private messaging system') || !$current_user
->hasPermission('reply to private messages thread')) {
unset($build['private_message_form']);
}
// Also add the back to inbox link
// but just the link since it's a drop down with icon.
$build['back_to_inbox']['#markup'] = Url::fromRoute('social_private_message.inbox')
->toString();
}
/**
* Implements hook_preprocess_field().
*/
function social_private_message_preprocess_field(&$variables) {
if (strpos($variables['entity_type'], 'private_message') === FALSE) {
return;
}
$element =& $variables['element'];
if (isset($element['#view_mode']) && $element['#view_mode'] == 'full' && $variables['field_name'] == 'message') {
$variables['view_mode'] = 'full';
if (empty($element['#object']
->getOwner())) {
foreach ($variables['items'] as &$item) {
$item['content'] = t("This message was deleted together with the user’s account.");
}
}
}
}
/**
* Implements hook_user_cancel().
*/
function social_private_message_user_cancel($edit, $account, $method) {
if ($method == 'user_cancel_reassign') {
\Drupal::database()
->update('private_messages')
->fields([
'message__value' => ' ',
])
->condition('owner', $account
->id())
->execute();
}
}
/**
* Implements hook_menu_local_actions_alter().
*/
function social_private_message_menu_local_actions_alter(&$local_actions) {
unset($local_actions['private_message.private_message_add']);
}
/**
* When the user creates a message, mark this thread as read for the author.
*
* Implements hook_ENTITY_TYPE_insert().
*/
function social_private_message_private_message_thread_insert(EntityInterface $entity) {
$socialPrivateMessageService = Drupal::service('social_private_message.service');
$socialPrivateMessageService
->updateLastThreadCheckTime($entity);
}
/**
* Implements hook_ENTITY_TYPE_update().
*/
function social_private_message_pm_thread_delete_time_update(EntityInterface $entity) {
$socialPrivateMessageService = Drupal::service('social_private_message.service');
$socialPrivateMessageService
->deleteUserDataThreadInfo($entity);
}
/**
* Implements hook_user_cancel_methods_alter().
*/
function social_private_message_user_cancel_methods_alter(&$methods) {
$methods['user_cancel_reassign']['title'] = t('Delete your account, delete private messages, and anonymize all other content.');
}
/**
* Implements hook_activity_send_email_notifications_alter().
*/
function social_private_message_activity_send_email_notifications_alter(array &$items, array $email_message_templates) {
// If our create_private_message template is enabled for email then we add it
// to the "Message to Me" section.
if (isset($email_message_templates['create_private_message'])) {
$items['message_to_me']['templates'][] = 'create_private_message';
}
}
/**
* Implements hook_social_user_account_header_items().
*
* Adds the Private Message button and indicator to the account header block if
* it's enabled by the site manager.
*/
function social_private_message_social_user_account_header_items(array $context) {
if (\Drupal::config('social_user.navigation.settings')
->get('display_social_private_message_icon') !== TRUE) {
return [];
}
// We require a logged in user for this button.
if (empty($context['user']) || !$context['user']
->isAuthenticated() || !$context['user']
->hasPermission('use private messaging system')) {
return [];
}
// Fetch the amount of unread items.
$num_account_messages = \Drupal::service('social_private_message.service')
->updateUnreadCount();
return [
'messages' => [
'#type' => 'account_header_element',
'#wrapper_attributes' => [
'class' => [
'desktop',
],
],
'#title' => new TranslatableMarkup('Inbox'),
'#url' => Url::fromRoute('social_private_message.inbox'),
'#icon' => $num_account_messages > 0 ? 'mail' : 'mail_outline',
'#label' => new TranslatableMarkup('Inbox'),
'#notification_count' => $num_account_messages,
],
];
}
/**
* Implements hook_social_user_account_header_account_links().
*
* Adds the mobile indicator for private messages under the profile icon menu.
*/
function social_private_message_social_user_account_header_account_links(array $context) {
if (\Drupal::config('social_user.navigation.settings')
->get('display_social_private_message_icon') !== TRUE) {
return [];
}
// We require a logged in user for this indicator.
if (empty($context['user']) || !$context['user']
->isAuthenticated()) {
return [];
}
// Fetch the amount of unread items.
$num_account_messages = \Drupal::service('social_private_message.service')
->updateUnreadCount();
// Default icon values.
$label_classes = 'hidden';
// Override icons when there are unread items.
if ($num_account_messages > 0) {
$label_classes = 'badge badge-accent badge--pill';
}
return [
'messages_mobile' => [
'#type' => 'link',
'#wrapper_attributes' => [
'class' => [
'mobile',
],
],
'#weight' => 200,
'#attributes' => [
'title' => new TranslatableMarkup('Inbox'),
],
'#title' => [
'#type' => 'inline_template',
'#template' => '<span>{% trans %}Inbox{% endtrans %}</span><span{{ attributes }}>{{ icon }}</span>',
'#context' => [
'attributes' => new Attribute([
'class' => $label_classes,
]),
'icon' => (string) $num_account_messages,
],
],
] + Url::fromRoute('social_private_message.inbox')
->toRenderArray(),
];
}
/**
* Implements hook_social_user_account_header_items().
*
* Adds an indicator to the user account menu on mobile.
*/
function social_private_message_social_user_account_header_items_alter(array &$menu_links, array $context) {
if (\Drupal::config('social_user.navigation.settings')
->get('display_social_private_message_icon') !== TRUE) {
return;
}
// We require a logged in user for this indicator.
if (empty($context['user']) || !$context['user']
->isAuthenticated()) {
return;
}
// If the account_box link was removed we have nothing to do.
if (!isset($menu_links['account_box'])) {
return;
}
// Fetch the amount of unread items.
$num_account_messages = \Drupal::service('social_private_message.service')
->updateUnreadCount();
if ($num_account_messages > 0) {
$menu_links['account_box']['#wrapper_attributes']['class'][] = 'has-alert';
}
}
/**
* Implements hook_views_data_alter().
*/
function social_private_message_views_data_alter(array &$data) {
$data['private_message_threads']['social_private_message_deleted_threads_filter'] = [
'title' => t('Filter deleted threads'),
'filter' => [
'title' => t('Filter deleted threads'),
'help' => t('Do not show threads a user deleted.'),
'field' => 'id',
'id' => 'social_private_message_deleted_threads',
],
];
}
/**
* Implements hook_views_query_alter().
*/
function social_private_message_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
if ($view
->id() == 'inbox') {
// Current user.
$current_user_id = \Drupal::currentUser()
->id();
// Add join definition.
$definition = [
'table' => 'pm_thread_delete_time',
'field' => 'id',
'left_table' => 'private_message_thread__last_delete_time',
'left_field' => 'last_delete_time_target_id',
'operator' => '=',
'extra' => 'pm_thread_delete_time.delete_time <= private_messages_private_message_thread__private_messages.created',
];
// Create a join stement from plugin.
$join = Drupal::service('plugin.manager.views.join')
->createInstance('standard', $definition);
// Add join to the query.
$query
->addRelationship('pm_thread_delete_time', $join, 'delete_time');
// Add some extra where statements.
$query
->addWhere(NULL, 'private_message_thread__members.members_target_id', $current_user_id, '=');
$query
->addWhere(NULL, 'pm_thread_delete_time.owner', $current_user_id, '=');
}
}
/**
* Implements hook_private_message_thread_access().
*/
function social_private_message_private_message_thread_access(PrivateMessageThreadInterface $entity, $operation, AccountInterface $account) {
if ($operation === 'delete') {
if ($account
->hasPermission('use private messaging system') && $account
->hasPermission('delete private messages thread')) {
return AccessResult::allowed();
}
return AccessResult::forbidden();
}
}
/**
* Implements hook_private_message_create_access().
*/
function social_private_message_private_message_create_access(AccountInterface $account, array $context, $entity_bundle) {
return AccessResult::forbiddenIf(!($account
->hasPermission('use private messaging system') && $account
->hasPermission('create private messages thread')));
}