' . t('About') . ''; $output .= '

' . t('The Profile module provides a fieldable entity, that allows administrators to define different sets of fields for user profiles, which are then displayed in the My Account section. This permits users of a site to share more information about themselves, and can help community-based sites organize users around specific information.', ['@user' => Url::fromRoute('user.page')->toString()]) . '

'; $output .= '
'; $output .= '
' . t('Types of profiles') . '
'; $output .= '
' . t('Profile types provide a way of grouping similar data for user profiles e.g. Personal information, Work etc. A default "Personal information type is provided. You may create more types and manage fields for each type from the Profile types admin page. When creating a new profile type, you will be able to specify whether a user may create multiple profiles or make the profile form available when registering a new user.', ['@profile-types' => Url::fromRoute('entity.profile_type.collection')->toString()]) . '
'; $output .= '
' . t('Creating profiles') . '
'; $output .= '
' . t('A user will see tabs they have access to, when editing their main user account e.g. "Add personal information profile". The visibility of a tab depends on whether they can create multiple profiles or if they haven\'t created a profile of the type that doesn\'t allow multiple instances.') . '
'; $output .= '
'; return $output; } } /** * Implements hook_entity_field_access(). */ function profile_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) { if ($operation == 'view' && $items && $field_definition->getTargetEntityTypeId() == 'profile') { if ($field_definition instanceof FieldConfigInterface) { $is_private = $field_definition->getThirdPartySetting('profile', 'profile_private', FALSE); if ($is_private) { // Users may see their own private profile fields by default, so this // requires user granularity for caching. /** @var \Drupal\profile\Entity\ProfileInterface $profile */ $profile = $items->getEntity(); if ($account->id() === $profile->getOwnerId()) { return AccessResult::neutral(); } return AccessResult::forbiddenIf(!$account->hasPermission('administer profile')); } } } return AccessResult::neutral(); } /** * Implements hook_theme(). */ function profile_theme() { return [ 'profile' => [ 'render element' => 'elements', ], ]; } /** * Prepares variables for profile templates. * * Default template: profile.html.twig. * * @param array $variables * An associative array containing: * - elements: An associative array containing rendered fields. * - attributes: HTML attributes for the containing element. */ function template_preprocess_profile(array &$variables) { /** @var Drupal\profile\Entity\ProfileInterface $profile */ $profile = $variables['elements']['#profile']; $variables['profile'] = $profile; $variables['url'] = $profile->id() ? $profile->toUrl() : FALSE; // Helpful $content variable for templates. $variables['content'] = []; foreach (Element::children($variables['elements']) as $key) { $variables['content'][$key] = $variables['elements'][$key]; } } /** * Implements hook_user_view(). */ function profile_user_view(array &$build, UserInterface $account, EntityViewDisplayInterface $display, $view_mode) { // Iterate through each bundle and see if it's component exists. foreach (ProfileType::loadMultiple() as $bundle) { $component_key = 'profile_' . $bundle->id(); if ($display->getComponent($component_key)) { // Embed the view of active profiles for profile type. $build[$component_key] = [ '#type' => 'view', '#name' => 'profiles', '#display_id' => 'user_view', '#arguments' => [$account->id(), $bundle->id(), 1], '#embed' => TRUE, '#title' => $bundle->label(), '#pre_render' => [ ['\Drupal\views\Element\View', 'preRenderViewElement'], 'profile_views_add_title_pre_render', ], ]; } } } /** * Implements hook_entity_extra_field_info(). */ function profile_entity_extra_field_info() { $extra = []; // Add each profile type as an extra field for display. Not enabled by default // as many sites will not need this and it otherwise also gets added // automatically to other view modes. /** @var \Drupal\profile\Entity\ProfileType $bundle */ foreach (ProfileType::loadMultiple() as $bundle) { $extra['user']['user']['display']['profile_' . $bundle->id()] = array( 'label' => $bundle->label(), 'description' => t('Display @type profiles', ['@type' => $bundle->label()]), 'weight' => 10, 'visible' => FALSE, ); } return $extra; } /** * Implements hook_user_delete(). */ function profile_user_delete(EntityInterface $entity) { $list = \Drupal::entityTypeManager() ->getStorage('profile') ->loadByProperties([ 'uid' => $entity->id(), ]); foreach ($list as $profile) { $profile->delete(); } } /** * Implements hook_form_FORM_ID_alter(). */ function profile_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) { $field = $form_state->getFormObject()->getEntity(); if ($field->getTargetEntityTypeId() != 'profile') { return; } $form['field']['profile']['profile_private'] = [ '#type' => 'checkbox', '#title' => t('This is a private field.'), '#default_value' => $field->getThirdPartySetting('profile', 'profile_private', FALSE), ]; $form['actions']['submit']['#submit'][] = 'profile_form_field_config_edit_form_submit'; } /** * Form submission handler for profile_form_field_config_edit_form_alter. * * @param array $form * The form array. * @param FormStateInterface $form_state * The form state. */ function profile_form_field_config_edit_form_submit(array $form, FormStateInterface $form_state) { $field = $form_state->getFormObject()->getEntity(); $form_fields = &$form_state->getValues(); // If the private option is checked, update settings. if ($form_fields['profile_private']) { $field->setThirdPartySetting('profile', 'profile_private', TRUE); $field->save(); } else { $field->unsetThirdPartySetting('profile', 'profile_private'); $field->save(); } } /** * Implements hook_form_FORM_ID_alter(). * * Add available profile forms to the user registration form. */ function profile_form_user_register_form_alter(&$form, FormStateInterface $form_state) { $attached_profile_form = FALSE; $weight = 90; /** @var ProfileType[] $profile_types */ $profile_types = ProfileType::loadMultiple(); foreach ($profile_types as $profile_type) { $instances = array_filter(\Drupal::service('entity_field.manager')->getFieldDefinitions('profile', $profile_type->id()), function ($field_definition) { return $field_definition instanceof FieldConfigInterface; }); if ($profile_type->getRegistration() === TRUE && count($instances)) { $property = ['profiles', $profile_type->id()]; $profile = $form_state->get($property); if (empty($profile)) { $profile = Profile::create([ 'type' => $profile_type->id(), 'langcode' => $profile_type->language() ? $profile_type->language() : \Drupal::languageManager()->getDefaultLanguage()->getId(), ]); // Attach profile entity form. $form_state->set($property, $profile); } $form_state->set('form_display_' . $profile_type->id(), EntityFormDisplay::collectRenderDisplay($profile, 'default')); $form['entity_' . $profile_type->id()] = [ '#type' => 'details', '#title' => $profile_type->label(), '#tree' => TRUE, '#parents' => ['entity_' . $profile_type->id()], '#weight' => ++$weight, '#open' => TRUE, ]; // @see https://www.drupal.org/node/2871480. if (\Drupal::moduleHandler()->moduleExists('field_group')) { $context = [ 'entity_type' => $profile->getEntityTypeId(), 'bundle' => $profile->bundle(), 'entity' => $profile, 'context' => 'form', 'display_context' => 'form', 'mode' => 'default', ]; field_group_attach_groups($form['entity_' . $profile_type->id()], $context); $form['entity_' . $profile_type->id()]['#pre_render'][] = 'field_group_form_pre_render'; } $form_state ->get('form_display_' . $profile_type->id()) ->buildForm($profile, $form['entity_' . $profile_type->id()], $form_state); $attached_profile_form = TRUE; } } if ($attached_profile_form) { $form['actions']['submit']['#validate'][] = 'profile_form_user_register_form_validate'; $form['actions']['submit']['#submit'][] = 'profile_form_user_register_form_submit'; } } /** * Extra form validation handler for the user registration form. */ function profile_form_user_register_form_validate(array &$form, FormStateInterface $form_state) { $profiles = $form_state->get('profiles'); if (!empty($profiles)) { foreach ($profiles as $bundle => $entity) { /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ $form_display = $form_state->get('form_display_' . $bundle); if (isset($form['entity_' . $bundle])) { $form_display->extractFormValues($entity, $form['entity_' . $bundle], $form_state); $form_display->validateFormValues($entity, $form['entity_' . $bundle], $form_state); } } } // Entity was validated in entityFormValidate(). This will prevent validation // exception from being thrown. $form_state->getFormObject()->validateForm($form, $form_state); } /** * Extra form submission handler for the user registration form. */ function profile_form_user_register_form_submit(array &$form, FormStateInterface $form_state) { /** @var \Drupal\Core\Session\AccountInterface $account */ $account = $form_state->getFormObject()->getEntity(); $profiles = $form_state->get('profiles'); if (!empty($profiles)) { foreach ($profiles as $bundle => $entity) { $entity->setOwnerId($account->id()); $entity->setActive(TRUE); $entity->save(); } } } /** * Pre render callback for profile embedded views to ensure a title is set. * @param $element * * @return mixed */ function profile_views_add_title_pre_render($element) { /** @var \Drupal\views\ViewExecutable $view */ if (isset($element['#title'])) { $view = $element['view_build']['#view']; if (!empty($view->result)) { $view->setTitle($element['#title']); } } return $element; } /** * Implements hook_preprocess_HOOK(). */ function profile_preprocess_views_view(&$variables) { // We have to manually add back the title since it was removed by Views. // @see template_preprocess_views_view() /** @var \Drupal\views\ViewExecutable $view */ $view = $variables['view']; if ($view->storage->id() == 'profiles' && !empty($view->result)) { // Test access to the profile. /** @var \Drupal\profile\Entity\profile $entity */ $entity = reset($view->result)->_entity; if ($entity->access('view')) { $variables['title'] = $view->getTitle(); } } } /** * Implements hook_views_data_alter(). * * Adds a relationship from the user table to its' profile entity. */ function profile_views_data_alter(&$data) { $data['users_field_data']['profile']['relationship'] = [ 'title' => t('Profile'), 'label' => t('Profile'), 'group' => 'User', 'help' => t('Reference to the profile of a user.'), 'id' => 'standard', 'base' => 'profile', 'base field' => 'uid', 'field' => 'uid', ]; $data['users_field_data']['profile_type']['relationship'] = [ 'title' => t('Profile Type'), 'label' => t('Profile Type'), 'group' => 'User', 'help' => t('Reference to a specific profile type of a user.'), 'id' => 'profile_relationship', 'base' => 'profile', 'base field' => 'uid', 'field' => 'uid', ]; } /** * Implements hook_theme_suggestions_HOOK(). */ function profile_theme_suggestions_profile(array $variables) { $original = $variables['theme_hook_original']; $entity = $variables['elements']['#profile']; $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_'); $suggestions = []; $suggestions[] = $original; $suggestions[] = $original . '__' . $sanitized_view_mode; $suggestions[] = $original . '__' . $entity->bundle(); $suggestions[] = $original . '__' . $entity->bundle() . '__' . $sanitized_view_mode; $suggestions[] = $original . '__' . $entity->id(); $suggestions[] = $original . '__' . $entity->id() . '__' . $sanitized_view_mode; return $suggestions; }