You are here

social_core.module in Open Social 10.3.x

The Social core module.

File

modules/social_features/social_core/social_core.module
View source
<?php

/**
 * @file
 * The Social core module.
 */
use Drupal\block\Entity\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Field\FieldItemList;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\file\Entity\File;
use Drupal\filter\FilterFormatInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
use Drupal\filter\Entity\FilterFormat;
use Drupal\group\Entity\Group;
use Drupal\image\Entity\ImageStyle;
use Drupal\node\NodeInterface;
use Drupal\path\Plugin\Field\FieldWidget\PathWidget;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;

/**
 * Implements hook_page_attachments().
 */
function social_core_page_attachments(array &$attachments) {

  // Unconditionally attach library to the page.
  $attachments['#attached']['library'][] = 'social_core/accessibility';

  // The library with logo improvements on the top of the gin toolbar.
  $attachments['#attached']['library'][] = 'social_core/admin-toolbar';
}

/**
 * Implements hook_theme().
 */
function social_core_theme() {
  return [
    // @deprecated: use node--hero.html.twig instead.
    'page_hero_data' => [
      'variables' => [
        'title' => NULL,
        'hero_node' => NULL,
        'node' => NULL,
        'node_type' => NULL,
        'section_class' => NULL,
        'event_enrollment' => NULL,
        'hero_styled_image_url' => NULL,
      ],
    ],
  ];
}

/**
 * Implements hook_modules_installed().
 */
function social_core_modules_installed($modules) {

  // If either social_group_flexible_group or social_landing_page gets enabled
  // and the other is already enabled, we should import the the configuration
  // for the optional view mode by enabling social_flexible_group_featured.
  if (in_array('social_group_flexible_group', $modules, TRUE) && \Drupal::moduleHandler()
    ->moduleExists('social_landing_page') || in_array('social_landing_page', $modules, TRUE) && \Drupal::moduleHandler()
    ->moduleExists('social_group_flexible_group')) {
    \Drupal::service('module_installer')
      ->install([
      'social_flexible_group_featured',
    ]);
  }

  // If either social_book or social_landing_page gets enabled and
  // the other is already enabled, we should import the the configuration
  // for the optional view mode by enabling social_book_featured.
  if (in_array('social_book', $modules, TRUE) && \Drupal::moduleHandler()
    ->moduleExists('social_landing_page') || in_array('social_landing_page', $modules, TRUE) && \Drupal::moduleHandler()
    ->moduleExists('social_book')) {
    \Drupal::service('module_installer')
      ->install([
      'social_book_featured',
    ]);
  }
}

/**
 * Implements hook_entity_form_display_alter().
 */
function social_core_entity_form_display_alter(EntityFormDisplayInterface $form_display, array $context) {
  $field_groups = $form_display
    ->getThirdPartySettings('field_group');
  if (!isset($field_groups['group_settings'])) {
    return;
  }
  $fields = [];

  // List of all fields which are split by section.
  foreach ($field_groups as $field_group) {
    $fields = array_merge($fields, $field_group['children']);
  }

  // List of all fields which aren't split by section.
  $fields = array_diff(array_keys($form_display
    ->getComponents()), $fields);
  if ($fields) {
    $field_groups['group_settings']['children'] = array_merge($field_groups['group_settings']['children'], $fields);
    $form_display
      ->setThirdPartySetting('field_group', 'group_settings', $field_groups['group_settings'])
      ->save();
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function social_core_preprocess_node(&$variables) {

  /** @var \Drupal\node\NodeInterface $node */
  $node = $variables['node'];

  // Add common variable for teaser images.
  $image_field = "field_{$node->getType()}_image";

  // If machine name too long or using another image field.
  if (!$node
    ->hasField($image_field)) {
    $node_fields = $node
      ->getFields();
    $image_fields = array_filter($node_fields, '_social_core_find_image_field');

    // Get the first image field of all the fields.
    if ($image_fields) {
      $image_field = reset($image_fields)
        ->getName();
    }
    else {
      $image_field = NULL;
    }
  }
  if (!empty($variables['content'][$image_field]['#theme'])) {
    $variables['node_image'] = $variables['content'][$image_field];
    $variables['no_image'] = FALSE;
  }
  else {
    $variables['no_image'] = TRUE;
  }
  if ($variables['view_mode'] === 'hero') {

    // Add node edit url for management.
    // 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.node.edit_form',
      'entity.node.delete_form',
    ])) {
      $account = \Drupal::currentUser();
      if ($node
        ->access('update', $account)) {
        $variables['node_edit_url'] = $node
          ->toUrl('edit-form')
          ->toString();

        // Ensure the cache varies correctly depending upon access of the user.
        $variables['#cache']['contexts'][] = 'user.node_grants';
      }
    }

    // Add the hero styled image.
    if ($node
      ->hasField($image_field) && !empty($node->{$image_field}->entity)) {
      $variables['hero_styled_image_url'] = ImageStyle::load('social_xx_large')
        ->buildUrl($node->{$image_field}->entity
        ->getFileUri());
    }
  }
}

/**
 * Implements hook_social_user_account_header_account_links().
 *
 * Adds the "My invitations" link to the user menu.
 */
function social_core_social_user_account_header_account_links(array $context) {

  // We require a user for this link.
  if (empty($context['user']) || !$context['user'] instanceof AccountInterface) {
    return [];
  }

  /** @var \Drupal\social_core\InviteService $core_invites */
  $core_invites = \Drupal::service('social_core.invite');

  // Only when there are actual Invite plugins enabled.
  if (!empty($core_invites
    ->getInviteData('name'))) {
    $title = new TranslatableMarkup('Invites');

    // If we have invites show the number.
    if (!empty($core_invites
      ->getInviteData('amount'))) {
      $title = [
        '#type' => 'inline_template',
        '#template' => '<span>{% trans %}Invites{% endtrans %}</span> <span{{ attributes }}>{{ icon }}</span>',
        '#context' => [
          'attributes' => new Attribute([
            'class' => 'margin-left-xs badge badge-accent badge--pill',
          ]),
          'icon' => (string) $core_invites
            ->getInviteData('amount'),
        ],
      ];
    }
    return [
      'divider_no_mobile' => [
        '#wrapper_attributes' => [
          'class' => [
            'divider',
            'hidden-for-phone-only',
          ],
          'role' => 'separator',
        ],
        '#weight' => 325,
      ],
      'my_invites' => [
        '#type' => 'link',
        '#attributes' => [
          'title' => new TranslatableMarkup('Invites'),
        ],
        '#title' => $title,
        '#weight' => 350,
      ] + Url::fromRoute('social_core.my_invites')
        ->toRenderArray(),
    ];
  }
}

/**
 * Implements hook_social_user_account_header_items().
 *
 * Adds an indicator to the user account menu.
 */
function social_core_social_user_account_header_items_alter(array &$menu_links, array $context) {

  // 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;
  }

  // Get the total amount of notifications for the user.

  /** @var \Drupal\social_core\InviteService $core_invites */
  $core_invites = \Drupal::service('social_core.invite');

  // Only when there are actual Invites.
  if ($core_invites
    ->getInviteData('amount') > 0) {
    $menu_links['account_box']['#wrapper_attributes']['class'][] = 'has-alert has-alert--desktop';
  }
}

/**
 * Tries to determine if a field is an image field based on a field name.
 *
 * @param \Drupal\Core\Field\FieldItemList $field
 *   A field object.
 *
 * @return \Drupal\Core\Field\FieldItemList|null
 *   Will return the FieldItemList or null if its not an image field.
 */
function _social_core_find_image_field(FieldItemList $field) {
  if (strpos($field
    ->getName(), 'image') !== FALSE) {
    return $field;
  }
  return NULL;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * For Site information Site details form.
 */
function social_core_form_system_site_information_settings_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Hide site slogan field and make it disabled as well whenever the user
  // is able to fill out the textfield.
  if (!empty($form['site_information']['site_slogan'])) {
    $form['site_information']['site_slogan']['#type'] = 'hidden';
    $form['site_information']['site_slogan']['#disabled'] = TRUE;
  }
}

/**
 * Implements hook_node_links_alter().
 */
function social_core_node_links_alter(array &$links, NodeInterface $entity, array &$context) {

  // Remove the comment links.
  unset($links['comment__field_topic_comments'], $links['comment__field_event_comments']);
  if (isset($context['view_mode']) && in_array($context['view_mode'], [
    'activity',
    'activity_comment',
  ])) {

    // Add a readmore link.
    $node_title_stripped = strip_tags($entity
      ->label());
    $links['node']['#links']['node-readmore'] = [
      'title' => t('Read more<span class="visually-hidden"> about @title</span>', [
        '@title' => $node_title_stripped,
      ]),
      'url' => $entity
        ->toUrl(),
      'attributes' => [
        'rel' => 'tag',
        'title' => $node_title_stripped,
      ],
    ];
  }
}

/**
 * Prepares variables for comment field templates.
 *
 * Default template: field--comment.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - element: An associative array containing render arrays for the list of
 *     comments, and the comment form. Array keys: comments, comment_form.
 */
function social_core_preprocess_field(array &$variables) {
  $element = $variables['element'];
  if ($element['#formatter'] == 'comment_node') {

    // Create separate variables for the more_link.
    $variables['more_link'] = $element[0]['more_link'];
  }
}

/**
 * Implements hook_preprocess_block().
 */
function social_core_preprocess_block(&$variables) {

  /** @var \Drupal\user\Entity\User $account */
  $account = \Drupal::routeMatch()
    ->getParameter('user');

  /** @var \Drupal\group\Entity\Group $group */
  $group = \Drupal::routeMatch()
    ->getParameter('group');
  if (is_numeric($account)) {
    $account = User::load($account);
  }
  if (is_numeric($group)) {
    $group = Group::load($group);
  }
  if (!empty($variables['elements']['content']['#view'])) {
    $view = $variables['elements']['content']['#view'];
    if (!empty($view
      ->getDisplay())) {
      $link = $view
        ->getDisplay();
      if (!empty($link
        ->useMoreText())) {
        $more_link = $link
          ->useMoreText();
      }
    }
  }

  // Add variables to sidebar blocks.
  switch ($variables['elements']['#derivative_plugin_id']) {
    case 'upcoming_events-block_my_upcoming_events':
      $variables['view_all_path'] = Url::fromRoute('view.events.events_overview', [
        'user' => \Drupal::currentUser()
          ->id(),
      ]);
      $variables['button_text'] = $more_link;
      $link
        ->setOption('use_more', FALSE);
      break;
    case 'upcoming_events-block_community_events':
      $variables['subtitle'] = t('in the community');
      $variables['view_all_path'] = Url::fromRoute('view.upcoming_events.page_community_events');
      if (isset($variables['label']['#markup'])) {
        $label = $variables['label']['#markup'];
      }
      else {
        $label = t('events');
      }
      $variables['button_text'] = t('All @label', [
        '@label' => $label,
      ]);
      break;
    case 'latest_topics-block_latest_topics':
      $variables['subtitle'] = t('in the community');
      $variables['view_all_path'] = Url::fromRoute('view.latest_topics.page_latest_topics');
      $variables['button_text'] = $more_link;
      $link
        ->setOption('use_more', FALSE);
      break;
    case 'newest_groups-block_newest_groups':
      $variables['subtitle'] = t('in the community');
      $variables['view_all_path'] = Url::fromRoute('view.newest_groups.page_all_groups');
      $variables['button_text'] = $more_link;
      $link
        ->setOption('use_more', FALSE);
      break;
    case 'newest_users-block_newest_users':
      $variables['subtitle'] = t('in the community');
      $variables['view_all_path'] = Url::fromRoute('view.newest_users.page_newest_users');
      $variables['button_text'] = $more_link;
      $link
        ->setOption('use_more', FALSE);
      break;
    case 'events-block_events_on_profile':
      if ($account instanceof UserInterface) {
        $variables['subtitle'] = t('for this user');
        $variables['view_all_path'] = Url::fromRoute('view.events.events_overview', [
          'user' => $account
            ->id(),
        ]);
        $variables['button_text'] = t('All @label', [
          '@label' => $variables['label']['#markup'],
        ]);
      }
      break;
    case 'topics-block_user_topics':
      if ($account instanceof UserInterface) {
        $variables['subtitle'] = t('for this user');
        $variables['view_all_path'] = Url::fromRoute('view.topics.page_profile', [
          'user' => $account
            ->id(),
        ]);
        $variables['button_text'] = $more_link;
        $link
          ->setOption('use_more', FALSE);
      }
      break;
    case 'groups-block_user_groups':
      if ($account instanceof UserInterface) {
        $variables['subtitle'] = t('for this user');
        $variables['view_all_path'] = Url::fromRoute('view.groups.page_user_groups', [
          'user' => $account
            ->id(),
        ]);
        $variables['button_text'] = $more_link;
        $link
          ->setOption('use_more', FALSE);
      }
      break;
    case 'group_members-block_newest_members':
      $variables['subtitle'] = t('in the group');
      $variables['view_all_path'] = Url::fromRoute('view.group_members.page_group_members', [
        'group' => $group
          ->id(),
      ]);
      $variables['button_text'] = $more_link;
      $link
        ->setOption('use_more', FALSE);
      break;
    case 'upcoming_events-upcoming_events_group':
      $variables['subtitle'] = t('in the group');
      $variables['view_all_path'] = Url::fromRoute('view.group_events.page_group_events', [
        'group' => $group
          ->id(),
      ]);
      $variables['button_text'] = t('All @label', [
        '@label' => $variables['label']['#markup'],
      ]);
      break;
    case 'latest_topics-group_topics_block':
      $variables['subtitle'] = t('in the group');
      $variables['view_all_path'] = Url::fromRoute('view.group_topics.page_group_topics', [
        'group' => $group
          ->id(),
      ]);
      $variables['button_text'] = $more_link;
      $link
        ->setOption('use_more', FALSE);
      break;
  }
}

/**
 * Implements hook_menu_local_tasks_alter().
 */
function social_core_menu_local_tasks_alter(&$data, $route_name) {
  if (\Drupal::currentUser()
    ->isAnonymous()) {

    // Anonymous user so we unset the user tabs on login register etc.
    if (isset($data['tabs'][0]['user.register'])) {
      unset($data['tabs'][0]['user.register']);
    }
    if (isset($data['tabs'][0]['user.pass'])) {
      unset($data['tabs'][0]['user.pass']);
    }
    if (isset($data['tabs'][0]['user.login'])) {
      unset($data['tabs'][0]['user.login']);
    }
  }

  // Remove node Edit tab. Edit will always go through Floating Edit Button.
  if (isset($data['tabs'][0]['entity.node.edit_form'])) {
    unset($data['tabs'][0]['entity.node.edit_form']);
  }

  // Change the default 'View' tab title.
  if (isset($data['tabs'][0]['entity.node.canonical']['#link'])) {
    $data['tabs'][0]['entity.node.canonical']['#link']['title'] = t('Details');
  }

  // Remove Delete tab. Delete will always go through Edit.
  if (isset($data['tabs'][0]['entity.node.delete_form'])) {
    unset($data['tabs'][0]['entity.node.delete_form']);
  }
}

/**
 * Implements hook_form_taxonomy_vocabulary_form_alter().
 */
function social_core_form_taxonomy_vocabulary_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Fetch current user.
  $account = \Drupal::currentUser();

  // Check if the current use is the admin.
  if (!in_array('administrator', $account
    ->getRoles())) {

    // Remove the option to delete.
    unset($form['actions']['delete']);
  }
}

/**
 * Implements hook_form_taxonomy_vocabulary_confirm_delete_alter().
 */
function social_core_form_taxonomy_vocabulary_confirm_delete_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Fetch current user.
  $account = \Drupal::currentUser();

  // Check if the current use is the admin.
  if (!in_array('administrator', $account
    ->getRoles())) {

    // Remove the option to delete.
    unset($form['actions']['submit']);
    $form['description']['#markup'] = t('You have insufficient permissions to delete this vocabulary');
  }
}

/**
 * Implements hook_preprocess().
 */
function social_core_preprocess_breadcrumb(&$variables) {

  // Get the user.
  $user = \Drupal::currentUser();

  // Add cache tag for the user.
  $variables['#cache']['tags'][] = 'user:breadcrumb:' . $user
    ->id();
}

/**
 * Implements hook_element_info_alter().
 */
function social_core_element_info_alter(array &$info) {
  if (isset($info['text_format']['#process'])) {
    $info['text_format']['#process'][] = 'social_core_filter_process_format';
  }
  if (isset($info['entity_autocomplete'])) {
    $info['entity_autocomplete']['#value_callback'] = [
      '\\Drupal\\social_core\\Entity\\Element\\EntityAutocomplete',
      'valueCallback',
    ];
  }
}

/**
 * Remove ability of selecting format if full_html is available.
 *
 * @todo Instead of defining the list of fields it would be better to add a
 *   separate Setting to all text_format fields, allowing to select whether to
 *   show format selector or not.
 */
function social_core_filter_process_format($element) {

  // Only fields listed here will have text format settings disabled.
  $full_html_field_ids = [
    'edit-body-0',
    'edit-field-profile-self-introduction-0',
    'edit-field-group-description-0',
  ];
  $element_id = $element['#id'];

  // Check if there is generated sub-id and cleanup it.
  if (($sub_id = strpos($element_id, '--')) !== FALSE) {
    $element_id = substr($element_id, 0, $sub_id);
  }

  // Default filter format for text area fields.
  $social_filter_format = 'full_html';

  // Allow module to override default filter format because some content type
  // could require specific one.
  Drupal::moduleHandler()
    ->alter('social_filter_format_default', $social_filter_format);

  /** @var \Drupal\filter\Entity\FilterFormat $social_filter_format */
  $social_filter_format = FilterFormat::load($social_filter_format);

  // Check if filter format was loaded, to prevent fatal errors if wrong value
  // was provided in the hook_social_filter_format_default_alter().
  if (!$social_filter_format instanceof FilterFormatInterface) {
    return $element;
  }
  $permission_name = $social_filter_format
    ->getPermissionName();
  $account = Drupal::currentUser();
  if ($element['#type'] === 'text_format' && $element['#format'] === $social_filter_format
    ->id() && !$account
    ->hasPermission($permission_name)) {
    $element['#format'] = 'basic_html';
    $element['value']['#format'] = 'basic_html';
    $element['format']['format']['#default_value'] = 'basic_html';
    $element['format']['format']['#value'] = 'basic_html';
    $element['value']['#disabled'] = FALSE;
    $element['format']['format']['#access'] = FALSE;
    $element['format']['#access'] = TRUE;
    $key = array_search('filter_form_access_denied', $element['value']['#pre_render']);
    if (isset($element['value']['#pre_render'][$key])) {
      unset($element['value']['#pre_render'][$key]);
    }
  }
  elseif ($element['#type'] === 'text_format' && $account
    ->hasPermission($permission_name) && in_array($element_id, $full_html_field_ids)) {
    $element['#format'] = $social_filter_format;
    $element['format']['format']['#access'] = FALSE;
    $element['format']['format']['#value'] = $social_filter_format
      ->id();
    $element['format']['help']['#access'] = FALSE;
    $element['format']['format']['#options'] = [
      $social_filter_format
        ->id() => $social_filter_format
        ->label(),
    ];
  }
  return $element;
}

/**
 * Hide blocks on extendable visibility path.
 *
 * Implements hook_block_access().
 */
function social_core_block_access(Block $block, $operation, AccountInterface $account) {
  $blocks =& drupal_static(__FUNCTION__);
  $blocks = [];

  // First check if we have it cached before we run the modulehandler alter.

  /** @var \Drupal\Core\Cache\CacheBackendInterface $cache */
  $cache = \Drupal::service('cache.default');
  if ($cache
    ->get('social_core.block_visibility_cache') === FALSE) {

    // Allow other modules to change the block visibility.
    $blocks = \Drupal::moduleHandler()
      ->invokeAll('social_core_block_visibility_path');

    // Set the cache expire to 1 week.
    $date = new DateTime(date('Y-m-d'));
    $date
      ->modify('+7 day');
    $cache
      ->set('social_core.block_visibility_cache', $blocks, $date
      ->getTimestamp(), [
      'social_core.block_visibility_cache',
    ]);
  }
  else {
    $blocks = $cache
      ->get('social_core.block_visibility_cache')->data;
  }

  // Alter the access based on the blocks array.
  if ($operation === 'view' && isset($blocks[$block
    ->getPluginId()])) {

    // Get the current path.
    $current_path = \Drupal::service('path.current')
      ->getPath();

    // Go through the request paths and see if they match.
    foreach ($blocks[$block
      ->getPluginId()] as $page) {
      if (\Drupal::service('path.matcher')
        ->matchPath($current_path, $page) === TRUE) {
        return AccessResult::forbidden();
      }
    }
  }

  // No opinion.
  return AccessResult::neutral();
}

/**
 * Implements hook_field_widget_form_alter().
 */
function social_core_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
  $field_definition = $context['items']
    ->getFieldDefinition();
  if ($field_definition
    ->getType() == 'path') {
    $url = Url::fromRoute('<front>', [], [
      'absolute' => TRUE,
    ]);

    // Protocols we can strip.
    $protocols = [
      'https://www.',
      'http://www.',
      'https://',
      'http://',
      'www.',
    ];

    // The URL.
    $truncate_url = $url
      ->toString();

    // Loop over the protocols and try to replace them in the string.
    foreach ($protocols as $protocol) {
      $truncate_url = str_replace($protocol, '', $truncate_url);
    }

    // Put the protocoless, truncated URL in the addon.
    $element['alias']['#field_prefix'] = '<div class="input-group input-group-expanded"><span class="input-group-addon">' . $truncate_url . '</span>';
    $element['alias']['#field_suffix'] = '</div>';

    // Unset description and title.
    unset($element['alias']['#description']);
    unset($element['alias']['#title']);

    // URLs are stored with a leading slash but our display includes that slash
    // in the prefix so we remove it here.
    if (isset($element['alias']['#default_value'])) {
      $element['alias']['#default_value'] = ltrim($element['alias']['#default_value'], '/');
    }
    $element['#element_validate'] = [
      'social_core_path_validate',
    ];
  }
}

/**
 * Form element validation handler for URL alias form element.
 *
 * Ensures that a path alias entered by a user is always a relative internal URL
 * that starts with a leading slash. If the user did not enter a leading slash
 * then we add it for them.
 *
 * @param array $element
 *   The form element.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state.
 */
function social_core_path_validate(array &$element, FormStateInterface $form_state) {
  $alias = trim($element['alias']['#value'], " \\/");
  $parsed_url = parse_url($alias);
  if (isset($parsed_url['host']) || isset($parsed_url['scheme']) || !isset($parsed_url['path'])) {
    $form_state
      ->setError($element, t('The URL alias must be a relative URL.'));
  }
  elseif (trim($element['alias']['#value']) !== "/") {

    // $alias is already trimmed of leading/trailing slashes.
    // We use rtrim here to ensure we don't set the value to an empty slash
    // during form submission.
    // The following line only changes the value of the rendered form element.
    // If we have a non-empty alias PathWidget::validateFormElement will
    // update the actual stored value to include the leading slash.
    $element['alias']['#value'] = rtrim('/' . $alias, '/');
    PathWidget::validateFormElement($element, $form_state);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_core_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Array of node types, which have the new group_settings fieldset,
  // these nodes can use the new content style, and have $form['advanced']
  // removed.
  // if we remove it for nodes which don't have that fieldset, fields will be
  // rendered outside of a card.
  $compatible_content_type_forms = [
    'node_event_form',
    'node_event_edit_form',
    'node_topic_form',
    'node_topic_edit_form',
    'node_page_form',
    'node_page_edit_form',
  ];

  // Create new alter hook which allows extensions to add nodes
  // to this list once they have added the group_settings.
  \Drupal::moduleHandler()
    ->alter('social_core_compatible_content_forms', $compatible_content_type_forms);

  // Let's remove the form elements not needed by Open Social.
  // We understand this is quite BC breaking, url_redirects / revision info
  // we don't use as Open Social, you might use. So by
  // changing the theme setting in social blue, you can go back to the
  // old settings and use Field UI to change the other fields.
  $use_social_content_forms = theme_get_setting('content_entity_form_style');
  if ($use_social_content_forms === 'open_social' && in_array($form_id, $compatible_content_type_forms)) {

    // We want to move all the fields from $form['advanced'] to our new
    // field sets which are created as part of #3186003.
    if (!empty($form['advanced'])) {
      unset($form['advanced']);

      // Unset optional fields.
      if (isset($form['revision'])) {
        unset($form['revision']);
      }
      if (isset($form['revision_log'])) {
        unset($form['revision_log']);
      }
      if (isset($form['revision_information'])) {
        unset($form['revision_information']);
      }
      if (isset($form['url_redirects'])) {
        unset($form['url_redirects']);
      }
      if (isset($form['meta'])) {
        unset($form['meta']);
      }

      // Add descriptive text to URL Alias field.
      if (isset($form['path']['widget'][0]['alias'])) {
        $form['path']['widget'][0]['alias']['#description'] = t('The URL alias allows you to customise the link to this page.');
      }

      // Hide the automatic URL alias.
      if (isset($form['path']['widget'][0]['pathauto'])) {
        $form['path']['widget'][0]['pathauto']['#type'] = 'hidden';

        // Path uses the above checkbox to decide whether or not to save
        // the automatically generated alias, or use a custom one.
        // since we hide the element, and the default is checked,
        // the path will never be overridden if someone updates it.
        // So we need to check this in our custom submit handler.
        // It should perform before path auto runs.
        array_unshift($form['actions']['submit']['#submit'], '_social_core_path_widget_submit');
      }

      // Move the fields from advanced to group_settings.
      if (!empty($form['#fieldgroups']['group_settings'])) {

        // Move the book outline fields to the group settings.
        // #502430 we need to remove this ourselves, users with permission
        // can see the book outline even when not configured as part
        // of the book settings.
        if (!empty($form['book']) && $form_state
          ->getFormObject() !== NULL && \Drupal::moduleHandler()
          ->moduleExists('book')) {

          // Grab the Entity to see if books are enabled for this one.
          $entity = $form_state
            ->getFormObject()
            ->getEntity();
          if (!empty($entity
            ->getType()) && is_string($entity
            ->getType())) {

            // Move it to our new fieldset on book pages either on its own
            // or in the group_settings.
            $form['book']['#type'] = 'fieldset';
            $form['book']['#attributes']['class'][] = 'card';
            $form['book']['#weight'] = isset($form['#fieldgroups']['group_visibility']->weight) ? $form['#fieldgroups']['group_visibility']->weight + 1 : '3';
            $enabled = book_type_is_allowed($entity
              ->getType());

            // If it's not enabled we can just safely unset it.
            if (!$enabled) {
              unset($form['book']);
            }
          }
        }

        // Move the node Menu settings to group_settings.
        if (!empty($form['menu'])) {
          $form['menu']['#group'] = 'group_settings';
          $form['menu']['#weight'] = -20;
          $form['menu']['#title'] = t('Menu link');
          if (!empty($form['menu']['link']['title'])) {
            $form['menu']['link']['title']['#title'] = t('Title');
            $form['menu']['link']['title']['#description'] = t('This title will be displayed in the menu.');
          }
          if (!empty($form['menu']['link']['description'])) {
            $form['menu']['link']['title']['#description'] = t('This description will be displayed when hovering over the menu link.');
          }
        }

        // Move the Published status to group settings with a new wrapper.
        if (!empty($form['status'])) {

          // Add an details element to status.
          $form['status_label'] = [
            '#type' => 'details',
            '#title' => t('Publish status'),
            '#description' => '',
            '#open' => TRUE,
            '#weight' => 110,
            '#group' => 'group_settings',
          ];
          $form['status']['#group'] = 'status_label';
          $form['status_label']['status'] = $form['status'];
          unset($form['status']);
        }

        // Node author information for administrators.
        $form['author_info'] = [
          '#type' => 'details',
          '#title' => t('Author Information'),
          '#description' => '',
          '#open' => TRUE,
          '#weight' => 100,
          '#group' => 'group_settings',
        ];

        // Move author uid to the new author info fieldset,
        // and remove descriptive text.
        if (isset($form['uid'])) {
          $form['uid']['#group'] = 'author_info';
          $form['uid']['widget']['#description'] = '';
          if (!empty($form['uid']['widget'][0]['target_id'])) {
            $form['uid']['widget'][0]['target_id']['#description'] = '';
          }
          $form['author_info']['uid'] = $form['uid'];
          unset($form['uid']);
        }

        // Move created to the new author info fieldset,
        // and remove descriptive text.
        if (isset($form['created'])) {
          $form['created']['#group'] = 'author_info';
          $form['created']['widget']['#description'] = '';
          $form['created']['widget'][0]['#description'] = '';
          $form['created']['widget'][0]['value']['#description'] = '';
          $form['author_info']['created'] = $form['created'];
          unset($form['created']);
        }
        $has_option_fields = FALSE;
        foreach ([
          'promote',
          'sticky',
        ] as $field) {
          if (isset($form[$field])) {
            $form['options'][$field] = $form[$field];
            unset($form[$field]);
            $has_option_fields = TRUE;
          }
        }

        // When the "Promoted to front page" field or the "Sticky at top of
        // lists" field is enabled then move their wrapper (the "Promotion
        // options" sub-section) to the "Settings" section.
        // @see \Drupal\node\NodeForm::form().
        if ($has_option_fields) {
          $form['options']['#group'] = 'group_settings';

          // Place the "Promotion options" sub-section after the "Publish
          // status" sub-section.
          $form['options']['#weight'] = 120;

          // Display sub-section as expanded.
          $form['options']['#optional'] = FALSE;
        }
      }

      // According to FileWidget.php we cant use a widget form alter.
      if (!empty($form['field_files']) && !empty($form['field_files']['widget']['#file_upload_title'])) {
        $form['field_files']['widget']['#file_upload_title'] = t('Attachments');
      }
    }

    // Change the button text from Save to Create topic / event etc.
    // Adds a cancel button.
    // Removes the preview button.
    if (!empty($form['actions']['submit']) && !empty($form['actions']['submit']['#value']) && $form_state
      ->getFormObject() !== NULL && method_exists($form_state
      ->getFormObject(), 'getOperation')) {

      // Change the button text from Save to Create topic / event etc.
      // Only on node add pages.
      if ($form_state
        ->getFormObject()
        ->getOperation() !== 'edit') {

        /** @var \Drupal\Node\NodeInterface $entity */
        $entity = $form_state
          ->getFormObject()
          ->getEntity();
        if (!empty(node_get_type_label($entity)) && is_string(node_get_type_label($entity))) {
          $form['actions']['submit']['#value'] = t('Create @entity', [
            '@entity' => strtolower(node_get_type_label($entity)),
          ]);
        }
      }

      // Adds cancel button.
      if (!isset($form['actions']['cancel'])) {
        $form['actions']['cancel'] = [
          '#type' => 'submit',
          '#value' => t('Cancel'),
          '#submit' => [
            '_social_core_node_cancel',
          ],
          '#limit_validation_errors' => [],
          '#attributes' => [
            'class' => [
              'button btn-default btn waves-effect waves-btn',
            ],
          ],
        ];
      }

      // Remove the preview button.
      if (isset($form['actions']['preview'])) {
        unset($form['actions']['preview']);
      }
    }
  }
}

/**
 * Custom form submit handler for handling an path alias update.
 *
 * @param array $form
 *   The form array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Form state.
 *
 * @throws \Drupal\Core\Entity\EntityStorageException
 */
function _social_core_path_widget_submit(array $form, FormStateInterface $form_state) {

  // Path uses the pathauto checkbox to decide whether or not to save
  // the automatically generated alias, or use a custom one.
  // since we hide the element, and the default is checked,
  // the path will never be overridden if someone updates it.
  // So we need to check this in our custom submit handler.
  $default_path = isset($form['path']['widget'][0]['alias']['#default_value']) ? $form['path']['widget'][0]['alias']['#default_value'] : '';
  $input = $form_state
    ->getUserInput();
  $updated_path = isset($input['path'][0]['alias']) ? $input['path'][0]['alias'] : '';

  // If we don't have a match, make sure we override the checkbox as well.
  if ($updated_path !== $default_path) {

    // Override the User input, act as if this was changed.
    $input['path'][0]['pathauto'] = "0";
    $form_state
      ->setUserInput($input);

    // Override the value.
    $form_state
      ->setValue([
      'path',
      '0',
      'pathauto',
    ], "0");

    // If the updated path is empty, make it automatic again.
    if (empty($updated_path)) {
      $input['path'][0]['pathauto'] = "1";
      $form_state
        ->setUserInput($input);
      $form_state
        ->setValue([
        'path',
        '0',
        'pathauto',
      ], "1");
    }
  }
}

/**
 * Redirects to the previous page, or home when pressing cancel form.
 *
 * @param array $form
 *   Join and leave form.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Form state interface.
 */
function _social_core_node_cancel(array &$form, FormStateInterface $form_state) {

  // By default the front page will be used,
  // especially for adding a node.
  $form_state
    ->setRedirect('<front>');

  // If we have an existing node, we rather go to the node itself.
  if (!empty($form_state
    ->getFormObject()
    ->getEntity())) {
    $entity = $form_state
      ->getFormObject()
      ->getEntity();
    if ($entity instanceof NodeInterface && $entity
      ->id() !== NULL) {
      $form_state
        ->setRedirect('entity.node.canonical', [
        'node' => $entity
          ->id(),
        [],
      ]);
    }
  }
}

/**
 * Implements hook_theme_registry_alter().
 */
function social_core_theme_registry_alter(&$theme_registry) {
  $theme_registry['menu__toolbar']['variables']['menu_name'] = NULL;
}

/**
 * Function to set default email logo if not set already for OS.
 */
function _social_core_set_default_email_logo_for_socialblue() {

  // Only when socialblue is default we continue.
  if (\Drupal::configFactory()
    ->get('system.theme')
    ->get('default') === 'socialblue') {
    $logo = theme_get_setting('logo.url', 'socialblue');
    $basetheme_url = drupal_get_path('theme', 'socialblue');

    // Only if the theme setting for the logo, is still the socialblue
    // default logo, we can update our default logo.
    // If someone changed it to their own branding, let's not update it
    // because that would create regression.
    // that will be /profile/contrib/social/themes/socialblue/logo.svg.
    if (!empty($basetheme_url) && $logo === DIRECTORY_SEPARATOR . $basetheme_url . DIRECTORY_SEPARATOR . 'logo.svg') {

      // Add default image.
      $config_factory = \Drupal::configFactory();

      // Lets get the email logo settings.
      $socialblue_settings = $config_factory
        ->getEditable('socialblue.settings');
      $default_image = $socialblue_settings
        ->get('email_logo');

      // Only if there is no default image, or if its the same as the
      // socialblue logo we can continue.
      if (empty($default_image) || $default_image === $basetheme_url . DIRECTORY_SEPARATOR . 'logo.svg') {
        $file_system = \Drupal::service('file_system');
        $uri = $file_system
          ->copy($basetheme_url . DIRECTORY_SEPARATOR . 'logo_email.png', 'public://logo_email.png', FileSystemInterface::EXISTS_REPLACE);
        $media = File::create([
          'uri' => $uri,
        ]);
        $media
          ->setPermanent();
        $media
          ->save();
        $default_image[0] = $media
          ->id();

        // We just save the file id.
        $socialblue_settings
          ->set('email_logo', $default_image)
          ->save();
      }
    }
  }
}

/**
 * Used to set the favicon of GIN to open socials logo.
 */
function _social_core_set_favicon() {
  $config = \Drupal::configFactory()
    ->getEditable('gin.settings');
  if (!empty($config
    ->getRawData())) {
    $gin_config = $config
      ->getRawData();

    // Update the favicon in GIN.
    $gin_config['favicon']['use_default'] = FALSE;
    $gin_config['favicon']['path'] = 'themes/contrib/socialblue/favicon.ico';
    $gin_config['favicon']['mimetype'] = 'image/vnd.microsoft.icon';
    $config
      ->setData($gin_config);
    $config
      ->save();
  }
}

/**
 * Implements hook_preprocess_HOOK() for status_messages.
 */
function social_core_preprocess_status_messages(&$variables) {

  // We want to remove stratus messages coming form image_widget_crop module.
  // These notifications are technical in nature and add no value for the user.
  // @see: \Drupal\image_widget_crop\ImageWidgetCropManager::saveCrop().
  if (isset($variables['message_list']['status'])) {

    // Get all the status messages.
    $status_messages = $variables['message_list']['status'];

    // Loop through them to find the relevant messages we are looking for.
    foreach ($status_messages as $delta => $message) {

      // Unset them finally.
      if (strpos((string) $message, 'The crop') !== FALSE) {
        unset($variables['message_list']['status'][$delta]);
      }
    }
  }
}

Functions

Namesort descending Description
social_core_block_access Hide blocks on extendable visibility path.
social_core_element_info_alter Implements hook_element_info_alter().
social_core_entity_form_display_alter Implements hook_entity_form_display_alter().
social_core_field_widget_form_alter Implements hook_field_widget_form_alter().
social_core_filter_process_format Remove ability of selecting format if full_html is available.
social_core_form_node_form_alter Implements hook_form_FORM_ID_alter().
social_core_form_system_site_information_settings_alter Implements hook_form_FORM_ID_alter().
social_core_form_taxonomy_vocabulary_confirm_delete_alter Implements hook_form_taxonomy_vocabulary_confirm_delete_alter().
social_core_form_taxonomy_vocabulary_form_alter Implements hook_form_taxonomy_vocabulary_form_alter().
social_core_menu_local_tasks_alter Implements hook_menu_local_tasks_alter().
social_core_modules_installed Implements hook_modules_installed().
social_core_node_links_alter Implements hook_node_links_alter().
social_core_page_attachments Implements hook_page_attachments().
social_core_path_validate Form element validation handler for URL alias form element.
social_core_preprocess_block Implements hook_preprocess_block().
social_core_preprocess_breadcrumb Implements hook_preprocess().
social_core_preprocess_field Prepares variables for comment field templates.
social_core_preprocess_node Implements hook_preprocess_HOOK().
social_core_preprocess_status_messages Implements hook_preprocess_HOOK() for status_messages.
social_core_social_user_account_header_account_links Implements hook_social_user_account_header_account_links().
social_core_social_user_account_header_items_alter Implements hook_social_user_account_header_items().
social_core_theme Implements hook_theme().
social_core_theme_registry_alter Implements hook_theme_registry_alter().
_social_core_find_image_field Tries to determine if a field is an image field based on a field name.
_social_core_node_cancel Redirects to the previous page, or home when pressing cancel form.
_social_core_path_widget_submit Custom form submit handler for handling an path alias update.
_social_core_set_default_email_logo_for_socialblue Function to set default email logo if not set already for OS.
_social_core_set_favicon Used to set the favicon of GIN to open socials logo.