You are here

social_profile_privacy.module in Open Social 8.6

The Social profile privacy module file.

File

modules/social_features/social_profile/modules/social_profile_privacy/social_profile_privacy.module
View source
<?php

/**
 * @file
 * The Social profile privacy module file.
 */
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\search_api\Query\ConditionGroup;
use Drupal\search_api\Query\QueryInterface;

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_profile_privacy_form_social_profile_admin_settings_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $config = \Drupal::config('social_profile_privacy.settings');
  $form['privacy'] += [
    '#prefix' => '<div id="privacy-settings">',
    '#suffix' => '</div>',
  ];

  // Add setting to hide Full Name for users without the `social profile privacy
  // always show full name` module.
  $form['privacy']['limit_search_and_mention'] = [
    '#type' => 'checkbox',
    '#title' => t('Limit search and mention'),
    '#description' => t("Enabling this setting causes users' full name to be hidden on the platform when the user has filled in their nickname. This setting won't hide the full name of users who didn't fill in a nickname. Users with the '%display_name' permission will still see the full name whenever available. Only users with the '%search_name' permission will find users using their full name through search or mentions.", [
      '%display_name' => t('View full name when restricted'),
      '%search_name' => t('View full name when restricted'),
    ]),
    '#default_value' => $config
      ->get('limit_search_and_mention'),
  ];
  $settings_forms = social_profile_privacy_get_settings_forms();
  foreach ($settings_forms as $settings_form_id => $settings_form_config) {
    $options = social_profile_privacy_form_display_options($settings_form_config['entity_type'], $settings_form_config['bundle']);
    $form_display_id = $form_state
      ->getValue($settings_form_id, $config
      ->get($settings_form_id));
    $form['privacy'][$settings_form_id] = [
      '#type' => 'select',
      '#title' => t('Privacy settings on display of @form_name', [
        '@form_name' => $settings_form_config['name'],
      ]),
      '#description' => t('Form mode on which users will be able to hide some groups of profile fields.'),
      '#options' => $options,
      '#empty_option' => t('- None -'),
      '#ajax' => [
        'callback' => 'social_profile_privacy_admin_settings_form_ajax',
        'wrapper' => 'privacy-settings',
      ],
      '#default_value' => $form_display_id,
    ];
    if ($form_display_id) {
      $form['privacy'][$settings_form_id . '_field_groups']['groups'] = [
        '#type' => 'fieldset',
        '#title' => t('Field groups'),
        '#description' => t('Profile field groups which users will be able to hide.'),
        '#tree' => TRUE,
        '#access' => !empty($form_display_id),
      ];
      $field_group_options = social_profile_privacy_form_field_group_options();
      if (!empty($field_group_options)) {
        $form['privacy'][$settings_form_id . '_field_groups']['groups'][$settings_form_id . '_field_groups'] = [
          '#type' => 'checkboxes',
          '#options' => $field_group_options,
          '#default_value' => $config
            ->get($settings_form_id) ? $config
            ->get($settings_form_id . '_field_groups') : [],
        ];
      }
      else {
        $form['privacy'][$settings_form_id . '_field_groups']['groups']['empty'] = [
          '#type' => 'item',
          '#markup' => t("This form mode doesn't contain any field groups."),
        ];
      }
      $form['privacy'][$settings_form_id . '_disclaimer'] = [
        '#type' => 'fieldset',
        '#title' => t('Disclaimer'),
        '#description' => t('Fieldset with this title and text will be displayed in the profile form.'),
        '#tree' => TRUE,
      ];
      $form['privacy'][$settings_form_id . '_disclaimer']['title'] = [
        '#type' => 'textfield',
        '#title' => t('Title'),
        '#default_value' => $config
          ->get($settings_form_id . '_disclaimer.title'),
      ];
      $form['privacy'][$settings_form_id . '_disclaimer']['text'] = [
        '#type' => 'text_format',
        '#default_value' => $config
          ->get($settings_form_id . '_disclaimer.text.value'),
        '#format' => $config
          ->get($settings_form_id . '_disclaimer.text.format'),
      ];
    }
  }
  $form['#submit'][] = 'social_profile_privacy_admin_settings_form_submit';
}

/**
 * The submit function for social_profile_admin_settings_form().
 *
 * To save configuration of fields groups available to hiding.
 */
function social_profile_privacy_admin_settings_form_submit($form, FormStateInterface $form_state) {
  $data = [
    'limit_search_and_mention' => $form_state
      ->getValue('limit_search_and_mention'),
  ];
  $settings_forms = social_profile_privacy_get_settings_forms();
  foreach ($settings_forms as $settings_form_id => $settings_form) {
    $data[$settings_form_id] = $form_state
      ->getValue($settings_form_id);
    if ($form_state
      ->getValue($settings_form_id) !== NULL) {
      $data[$settings_form_id . '_field_groups'] = $form_state
        ->getValue([
        'groups',
        $settings_form_id . '_field_groups',
      ]);
      $data[$settings_form_id . '_disclaimer'] = $form_state
        ->getValue($settings_form_id . '_disclaimer');
    }
  }
  \Drupal::configFactory()
    ->getEditable('social_profile_privacy.settings')
    ->setData($data)
    ->save();
}

/**
 * Implements hook_form_alter().
 */
function social_profile_privacy_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Check with preg_match because of the form id might be different
  // for adding or editing profile.
  if (preg_match('/^profile_profile_\\w+_form$/', $form_id) || preg_match('/^user_form$/', $form_id)) {
    $config = \Drupal::config('social_profile_privacy.settings');
    $field_groups = social_profile_privacy_get_field_groups_for_form_state($form_state, $config);
    $field_groups_labels = social_profile_privacy_form_field_group_options();
    if (!empty($field_groups)) {
      $settings_form_id = $field_groups['settings_form_id'];
      $profile = $form_state
        ->getFormObject()
        ->getEntity();
      $account = \Drupal::routeMatch()
        ->getParameter('user');
      $uid = $account ? $account
        ->id() : NULL;
      if (empty($uid)) {
        $uid = $profile
          ->get('uid')->target_id;
      }
      if (!empty($uid)) {
        $user_data = \Drupal::service('user.data');
        $values = $user_data
          ->get('social_profile_privacy', $uid, 'private_info');
        foreach ($field_groups['field_groups'] as $id) {
          if (array_key_exists($id, $form['#fieldgroups'])) {
            $form[$id . '_visible'] = [
              '#type' => 'checkbox',
              '#title' => t('Show on my profile'),
              '#weight' => -100,
              '#default_value' => isset($values[$id]) ? $values[$id] : TRUE,
              '#attributes' => [
                'data-switch' => TRUE,
              ],
            ];
            $form['#group_children'][$id . '_visible'] = $id;
            $disclaimer_weight = $form['#fieldgroups'][$id]->weight + 1;
          }
          elseif (isset($form['profile_privacy'])) {
            if (array_key_exists($id, $field_groups_labels)) {
              $form['profile_privacy'][$id . '_visible'] = [
                '#type' => 'checkbox',
                '#title' => t('Show "@field_group" on my profile', [
                  '@field_group' => $field_groups_labels[$id],
                ]),
                '#weight' => -10,
                '#default_value' => isset($values[$id]) ? $values[$id] : TRUE,
                '#attributes' => [
                  'data-switch' => TRUE,
                ],
              ];
            }
          }
        }
        if ($value = $config
          ->get($settings_form_id . '_disclaimer.text.value')) {
          if (isset($form['profile_privacy'])) {
            if (!empty($config
              ->get($settings_form_id . '_disclaimer.title'))) {
              $title = '<strong>' . $config
                ->get($settings_form_id . '_disclaimer.title') . ':</strong> ';
            }
            else {
              $title = '';
            }
            $form['profile_privacy']['disclaimer'] = [
              '#type' => 'markup',
              '#markup' => $title . check_markup($value, $config
                ->get($settings_form_id . '_disclaimer.text.format')),
              '#weight' => -100,
            ];
          }
          else {
            $form['disclaimer'] = [
              '#type' => 'fieldset',
              '#title' => $config
                ->get($settings_form_id . '_disclaimer.title'),
              'text' => [
                '#type' => 'markup',
                '#markup' => check_markup($value, $config
                  ->get($settings_form_id . '_disclaimer.text.format')),
              ],
              '#weight' => isset($disclaimer_weight) ? $disclaimer_weight : 99,
            ];
          }
        }
      }
      $form['actions']['submit']['#submit'][] = 'social_profile_privacy_profile_form_submit';
    }
  }
}

/**
 * Ajax callback for social_profile_admin_settings_form().
 */
function social_profile_privacy_admin_settings_form_ajax($form, FormStateInterface $form_state) {
  return $form['privacy'];
}

/**
 * Additional submit function to save settings of hidden fields.
 */
function social_profile_privacy_profile_form_submit($form, FormStateInterface $form_state) {
  if (!empty($form_state
    ->getValue('uid'))) {
    $uid = $form_state
      ->getValue('uid');
  }
  if (!isset($uid)) {
    $uid = $form_state
      ->getFormObject()
      ->getEntity()
      ->get('uid')->target_id;
  }
  $config = \Drupal::config('social_profile_privacy.settings');
  $field_groups = social_profile_privacy_get_field_groups_for_form_state($form_state, $config);
  $user_data = \Drupal::service('user.data');
  $existing_user_data = $user_data
    ->get('social_profile_privacy', $uid, 'private_info');
  $data = $existing_user_data ? $existing_user_data : [];
  foreach ($field_groups['field_groups'] as $group => $group_setting) {
    if (!empty($form_state
      ->getValue('profile_privacy'))) {
      $profile_privacy_values = $form_state
        ->getValue('profile_privacy');
      if (isset($profile_privacy_values[$group . '_visible'])) {
        $data[$group] = (bool) $profile_privacy_values[$group . '_visible'];
      }
    }
    else {
      if ($form_state
        ->getValue($group . '_visible') !== NULL) {
        $data[$group] = (bool) $form_state
          ->getValue($group . '_visible');
      }
    }
  }
  $user_data
    ->set('social_profile_privacy', $uid, 'private_info', $data);
}

/**
 * Returns fields the names that marked as hidden.
 *
 * @param int $uid
 *   Identifier of a user.
 *
 * @return array
 *   Array with the names of fields that marked as hidden.
 */
function social_profile_privacy_private_fields_list($uid) {
  $fields =& drupal_static(__FUNCTION__, []);
  if (isset($fields[$uid])) {
    return $fields[$uid];
  }
  $user_data = \Drupal::service('user.data');
  $values = $user_data
    ->get('social_profile_privacy', $uid, 'private_info');
  $config = \Drupal::config('social_profile_privacy.settings');
  $form_display = \Drupal::entityTypeManager()
    ->getStorage('entity_form_display')
    ->load('profile.profile.default');
  if (!$form_display) {
    return [];
  }
  $third_party_settings = $form_display
    ->get('third_party_settings');
  $field_groups_privacy_configurable = [];
  $settings_forms = social_profile_privacy_get_settings_forms();
  foreach ($settings_forms as $settings_form_id => $settings) {
    $field_groups_in_settings_form = $config
      ->get($settings_form_id . '_field_groups');
    if ($field_groups_in_settings_form !== NULL) {
      foreach ($field_groups_in_settings_form as $field_group_id => $field_group_value) {
        if ($field_group_value != FALSE) {
          $field_groups_privacy_configurable[$field_group_id] = $field_group_value;
        }
      }
    }
  }
  if (isset($third_party_settings['field_group'])) {
    $fields[$uid] = [];
    foreach ($third_party_settings['field_group'] as $id => $data) {
      if (isset($values[$id]) && empty($values[$id])) {

        // Only add field if it is enabled in one of the configurations.
        if (in_array($id, $field_groups_privacy_configurable)) {
          $fields[$uid] = array_merge($fields[$uid], $data['children']);
        }
      }
    }
  }
  return $fields[$uid];
}

/**
 * Implements hook_entity_field_access().
 */
function social_profile_privacy_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
  if ($operation == 'view' && $field_definition
    ->getTargetEntityTypeId() == 'profile' && $items !== NULL) {
    $uid = $items
      ->getEntity()
      ->get('uid')->target_id;
    $fields = social_profile_privacy_private_fields_list($uid);

    // If owner.
    $access = $uid == $account
      ->id();

    // If field is not hidden.
    $access = $access || !in_array($field_definition
      ->getName(), $fields);

    // If user has access to view hidden fields.
    $access = $access || $account
      ->hasPermission('social profile privacy view hidden fields');
    $access_result = AccessResult::forbiddenIf(!$access);
    return $access_result
      ->cachePerUser();
  }
  return AccessResult::neutral();
}

/**
 * Get the list of settings forms which support our profile privacy config.
 *
 * @return array
 *   Returns an array containing the settings form.
 */
function social_profile_privacy_get_settings_forms() {
  return [
    'user_form_display' => [
      'name' => t('user form'),
      'entity_type' => 'user',
      'bundle' => 'user',
    ],
    'profile_form_display' => [
      'name' => t('profile form'),
      'entity_type' => 'profile',
      'bundle' => 'profile',
    ],
  ];
}

/**
 * Get the list of privacy field groups enabled for the given form state.
 *
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state.
 * @param \Drupal\Core\Config\ImmutableConfig $config
 *   The config.
 *
 * @return array|null
 *   Returns an array with the settings form id and field groups or NULL.
 */
function social_profile_privacy_get_field_groups_for_form_state(FormStateInterface $form_state, ImmutableConfig $config) {
  $storage = $form_state
    ->getStorage();
  if (isset($storage['form_display'])) {

    /** @var \Drupal\Core\Entity\Entity\EntityFormDisplay $form_display */
    $form_display = $storage['form_display'];
    $active_form_display_id = $form_display
      ->getOriginalId();
    $settings_forms = social_profile_privacy_get_settings_forms();
    foreach ($settings_forms as $settings_form_id => $settings_form) {
      if ($config
        ->get($settings_form_id) === $active_form_display_id) {
        $field_groups = $config
          ->get($settings_form_id . '_field_groups');
        if (!empty($field_groups)) {
          return [
            'settings_form_id' => $settings_form_id,
            'field_groups' => $field_groups,
          ];
        }
      }
    }
  }
  return NULL;
}

/**
 * Returns array keyed with form display id and form mode label.
 *
 * @param string $entity_type_id
 *   Entity type id.
 * @param string $bundle
 *   Entity bundle.
 *
 * @return array
 *   Array with options prepared to use in the "select" form element.
 */
function social_profile_privacy_form_display_options($entity_type_id, $bundle) {
  $entity_type_manager = \Drupal::entityTypeManager();
  $options = [
    "{$entity_type_id}.{$bundle}.default" => t('Default'),
  ];
  $form_displays = $entity_type_manager
    ->getStorage('entity_form_display')
    ->loadByProperties([
    'targetEntityType' => $entity_type_id,
    'bundle' => $bundle,
    'status' => TRUE,
  ]);
  foreach ($form_displays as $id => $form_display) {
    $form_modes = $entity_type_manager
      ->getStorage('entity_form_mode')
      ->loadByProperties([
      'id' => $entity_type_id . '.' . $form_display
        ->getMode(),
      'status' => TRUE,
    ]);
    if ($form_mode = current($form_modes)) {
      $options[$id] = $form_mode
        ->label();
    }
  }
  return $options;
}

/**
 * Get the field group options to be used by the module.
 *
 * @return array
 *   Returns an array with the field groups.
 */
function social_profile_privacy_form_field_group_options() {
  $field_groups = [];

  /** @var \Drupal\Core\Entity\Entity\EntityFormDisplay $form_mode */
  $default_profile_form_display = \Drupal::entityTypeManager()
    ->getStorage('entity_form_display')
    ->load('profile.profile.default');
  if ($default_profile_form_display && ($third_party_settings = $default_profile_form_display
    ->get('third_party_settings')) && !empty($third_party_settings['field_group'])) {
    $field_groups = array_map(function ($group) {
      return $group['label'];
    }, $third_party_settings['field_group']);
  }
  if (isset($field_groups['group_profile_names_image'])) {
    unset($field_groups['group_profile_names_image']);
  }
  return $field_groups;
}

/**
 * Implements hook_search_api_query_alter().
 *
 * Ensures that when the setting is enabled to be strict with first/last name
 * display and the current user does not have the permission to bypass this
 * setting that the search queries are altered to ignore the first name/last
 * name when the target user has filled in a nickname.
 *
 * Users will not be able to find themselves by first/last name if they have
 * filled in a nickname and do not have the correct permissions. This is
 * intentional because it means that they can test how other users can find
 * them.
 */
function social_profile_privacy_search_api_query_alter(QueryInterface &$query) {

  // If the social profile fields module isn't enabled then we don't have
  // anything to hide behind.
  if (!\Drupal::moduleHandler()
    ->moduleExists('social_profile_fields')) {
    return;
  }
  $ids = [
    'social_all',
    'social_users',
  ];

  // If the current query isn't for an index with users then we're done too.
  if (!in_array($query
    ->getIndex()
    ->id(), $ids)) {
    return;
  }
  $config = \Drupal::config('social_profile_privacy.settings');
  $account = \Drupal::currentUser();

  // If the use of real names is not limited or the user can bypass this
  // restriction then we're done too.
  if (!$config
    ->get('limit_search_and_mention') || $account
    ->hasPermission('social profile privacy always show full name')) {
    return;
  }
  $search_term = $query
    ->getOriginalKeys();

  // Conditions must match lowercase because that's how they're indexed.
  if (is_array($search_term)) {
    $search_term = array_map("strtolower", $search_term);
  }
  else {
    $search_term = strtolower($search_term);
  }

  // If no search string was filled out, don't add the subquery.
  if (empty($search_term)) {
    return;
  }

  // When a user has no nickname, there can be a match for a (part of the) name.
  $matched_name = (new ConditionGroup("AND", [
    'social_profile_privacy_full_name_protection',
  ]))
    ->addCondition('field_profile_nick_name', NULL, '=')
    ->addConditionGroup((new ConditionGroup("OR", [
    'social_profile_privacy_full_name_protection',
  ]))
    ->addCondition('field_profile_first_name', $search_term, '=')
    ->addCondition('field_profile_last_name', $search_term, '='));

  // Given that a user has a nickname there can be no match for the first and
  // last name.
  $no_name_match = (new ConditionGroup('AND', [
    'social_profile_privacy_full_name_protection',
  ]))
    ->addCondition('field_profile_nick_name', NULL, '<>')
    ->addCondition('field_profile_first_name', $search_term, '<>')
    ->addCondition('field_profile_last_name', $search_term, '<>');

  // Results are matched only when they do not match the first name and last
  // name, or they also match the username.
  $full_name_restricted = (new ConditionGroup('OR', [
    'social_profile_privacy_full_name_protection',
  ]))
    ->addCondition('name', $search_term)
    ->addCondition('field_profile_nick_name', $search_term)
    ->addConditionGroup($matched_name)
    ->addConditionGroup($no_name_match);
  $query
    ->addConditionGroup($full_name_restricted);
}

/**
 * Implements hook_social_user_name_display_suggestions_alter().
 *
 * Given that we're being extra strict about real names.
 * When there is both a full name and a nickname then we combine the two for
 * users that are allowed to see a full name even when there's a nickname.
 */
function social_profile_privacy_social_user_name_display_suggestions_alter(array &$suggestions, AccountInterface $account) {
  $config = \Drupal::config('social_profile_privacy.settings');
  if ($config
    ->get('limit_search_and_mention') && isset($suggestions['full_name'], $suggestions['nickname']) && \Drupal::currentUser()
    ->hasPermission('social profile privacy always show full name')) {
    $suggestions['nickname_with_full_name'] = [
      'weight' => -PHP_INT_MAX,
      'name' => $suggestions['nickname']['name'] . ' (' . $suggestions['full_name']['name'] . ')',
    ];
  }
}

Functions

Namesort descending Description
social_profile_privacy_admin_settings_form_ajax Ajax callback for social_profile_admin_settings_form().
social_profile_privacy_admin_settings_form_submit The submit function for social_profile_admin_settings_form().
social_profile_privacy_entity_field_access Implements hook_entity_field_access().
social_profile_privacy_form_alter Implements hook_form_alter().
social_profile_privacy_form_display_options Returns array keyed with form display id and form mode label.
social_profile_privacy_form_field_group_options Get the field group options to be used by the module.
social_profile_privacy_form_social_profile_admin_settings_form_alter Implements hook_form_FORM_ID_alter().
social_profile_privacy_get_field_groups_for_form_state Get the list of privacy field groups enabled for the given form state.
social_profile_privacy_get_settings_forms Get the list of settings forms which support our profile privacy config.
social_profile_privacy_private_fields_list Returns fields the names that marked as hidden.
social_profile_privacy_profile_form_submit Additional submit function to save settings of hidden fields.
social_profile_privacy_search_api_query_alter Implements hook_search_api_query_alter().
social_profile_privacy_social_user_name_display_suggestions_alter Implements hook_social_user_name_display_suggestions_alter().