[ 'template' => 'user--full', 'base hook' => 'user', ], 'site_users_info_block' => [ 'template' => 'site-user-info-block', 'variables' => [ 'user_info' => [], 'user' => NULL, ], ], ]; } /** * Implements hook_theme_suggestions_HOOK_alter() for user templates. */ function site_users_theme_suggestions_user_alter(array &$suggestions, array $variables) { $view_mode = $variables['elements']['#view_mode'] ?? 'default'; $suggestions[] = 'user__' . $view_mode; } /** * Implements hook_entity_field_access(). */ function site_users_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, ?FieldItemListInterface $items = NULL) { // Apenas para entidade user. if ($field_definition->getTargetEntityTypeId() !== 'user') { return AccessResult::neutral(); } // Apenas campos configuráveis (não campos base como name, mail, status). if (!($field_definition instanceof FieldConfigInterface)) { return AccessResult::neutral(); } $field_name = $field_definition->getName(); // Campos de referência a mídia usam lógica de fotos. if ($field_definition->getType() === 'entity_reference' && $field_definition->getSetting('target_type') === 'media') { return site_users_check_photo_field_access($operation, $account, $items, $field_name); } return site_users_check_profile_field_access($operation, $account, $items, $field_name); } /** * Verifica acesso aos campos de perfil. */ function site_users_check_profile_field_access($operation, AccountInterface $account, ?FieldItemListInterface $items = NULL, string $field_name = '') { // Administradores têm acesso total. if ($account->hasPermission('administer site_users settings')) { return AccessResult::allowed()->cachePerPermissions(); } // Determinar se é o próprio usuário. $is_own = FALSE; if ($items) { $entity = $items->getEntity(); $is_own = ($entity->id() == $account->id()); } if ($operation === 'view') { if ($account->hasPermission('view any user profile fields')) { return AccessResult::allowed()->cachePerPermissions(); } // Sem entidade disponível, defer. if (!$items) { return AccessResult::neutral(); } if ($is_own && $account->hasPermission('view own user profile fields')) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser(); } if (!$is_own) { return AccessResult::forbidden()->cachePerPermissions()->cachePerUser(); } return AccessResult::neutral()->cachePerUser(); } if ($operation === 'edit') { if ($account->hasPermission('edit any user profile fields')) { return AccessResult::allowed()->cachePerPermissions(); } // Sem entidade disponível, defer. if (!$items) { return AccessResult::neutral(); } if ($is_own) { // Campo habilitado na config? $config = \Drupal::config('site_users.settings'); $field_enabled = $config->get('user_editable_fields.' . $field_name) ?? TRUE; if ($field_enabled) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheTags(['config:site_users.settings']); } return AccessResult::forbidden()->cachePerPermissions()->cachePerUser()->addCacheTags(['config:site_users.settings']); } return AccessResult::forbidden()->cachePerPermissions()->cachePerUser(); } return AccessResult::neutral(); } /** * Verifica acesso ao campo de fotos. */ function site_users_check_photo_field_access($operation, AccountInterface $account, ?FieldItemListInterface $items = NULL, string $field_name = '') { // Administradores têm acesso total. if ($account->hasPermission('administer site_users settings')) { return AccessResult::allowed()->cachePerPermissions(); } // Determinar se é o próprio usuário. $is_own = FALSE; if ($items) { $entity = $items->getEntity(); $is_own = ($entity->id() == $account->id()); } if ($operation === 'view') { if ($account->hasPermission('view any user profile fields')) { return AccessResult::allowed()->cachePerPermissions(); } if (!$items) { return AccessResult::neutral(); } if ($is_own && $account->hasPermission('view own user profile fields')) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser(); } if (!$is_own) { return AccessResult::forbidden()->cachePerPermissions()->cachePerUser(); } return AccessResult::neutral()->cachePerUser(); } if ($operation === 'edit') { if ($account->hasPermission('manage user photos')) { return AccessResult::allowed()->cachePerPermissions(); } if (!$items) { return AccessResult::neutral(); } if ($is_own) { $config = \Drupal::config('site_users.settings'); $field_enabled = $config->get('user_editable_fields.' . $field_name) ?? TRUE; if ($field_enabled) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheTags(['config:site_users.settings']); } return AccessResult::forbidden()->cachePerPermissions()->cachePerUser()->addCacheTags(['config:site_users.settings']); } return AccessResult::forbidden()->cachePerPermissions()->cachePerUser(); } return AccessResult::neutral(); } /** * Implements hook_form_FORM_ID_alter() for user_form. */ function site_users_form_user_form_alter(&$form, FormStateInterface $form_state, $form_id) { if (isset($form['field_user_photos'])) { $form['#validate'][] = 'site_users_validate_photos_count'; _site_users_add_default_photo_selector($form, $form_state); } } /** * Implements hook_form_FORM_ID_alter() for user_register_form. */ function site_users_form_user_register_form_alter(&$form, FormStateInterface $form_state, $form_id) { if (isset($form['field_user_photos'])) { $form['#validate'][] = 'site_users_validate_photos_count'; _site_users_add_default_photo_selector($form, $form_state); } } /** * Adiciona o seletor de foto padrão ao formulário. */ function _site_users_add_default_photo_selector(&$form, FormStateInterface $form_state) { /** @var \Drupal\user\UserInterface $user */ $user = $form_state->getFormObject()->getEntity(); // Obter fotos atuais do usuário. $photos = []; if ($user->hasField('field_user_photos') && !$user->get('field_user_photos')->isEmpty()) { foreach ($user->get('field_user_photos')->referencedEntities() as $media) { if ($media instanceof MediaInterface) { $photos[$media->id()] = $media->label(); } } } // Obter foto padrão atual. $default_photo_id = NULL; if ($user->hasField('field_user_default_photo') && !$user->get('field_user_default_photo')->isEmpty()) { $default_photo_id = $user->get('field_user_default_photo')->target_id; } // Adicionar seletor de foto padrão após o campo de fotos. $form['default_photo_selector'] = [ '#type' => 'container', '#weight' => $form['field_user_photos']['#weight'] + 0.5 ?? 15, '#states' => [ 'visible' => [ ':input[name="field_user_photos[selection]"]' => ['filled' => TRUE], ], ], ]; if (!empty($photos)) { $form['default_photo_selector']['field_user_default_photo_select'] = [ '#type' => 'radios', '#title' => t('Default photo'), '#description' => t('Select the main profile photo.'), '#options' => $photos, '#default_value' => $default_photo_id, ]; } else { $form['default_photo_selector']['field_user_default_photo_select'] = [ '#type' => 'item', '#title' => t('Default photo'), '#markup' => t('Add photos to select one as default.'), ]; } // Adicionar submit handler para salvar a foto padrão. $form['actions']['submit']['#submit'][] = '_site_users_save_default_photo'; } /** * Submit handler para salvar a foto padrão selecionada. */ function _site_users_save_default_photo(&$form, FormStateInterface $form_state) { $selected_photo = $form_state->getValue('field_user_default_photo_select'); /** @var \Drupal\user\UserInterface $user */ $user = $form_state->getFormObject()->getEntity(); if ($user->hasField('field_user_default_photo')) { $user->set('field_user_default_photo', $selected_photo); $user->save(); } } /** * Validação customizada para quantidade máxima de fotos. */ function site_users_validate_photos_count(&$form, FormStateInterface $form_state) { $photos = $form_state->getValue('field_user_photos'); if (empty($photos)) { return; } // Contar fotos selecionadas (ignorar valores vazios). $count = 0; foreach ($photos as $delta => $photo) { if (is_numeric($delta) && !empty($photo['target_id'])) { $count++; } } // Obter limite configurado. $config = \Drupal::config('site_users.settings'); $max_count = $config->get('photos.max_count') ?? 5; if ($count > $max_count) { $form_state->setErrorByName('field_user_photos', t('You can add a maximum of @max photos. Currently @count photos are selected.', [ '@max' => $max_count, '@count' => $count, ])); } } /** * Implements hook_ENTITY_TYPE_presave() for user entities. */ function site_users_user_presave(UserInterface $user) { // Garantir consistência da foto padrão. if (!$user->hasField('field_user_photos') || !$user->hasField('field_user_default_photo')) { return; } // Obter IDs das fotos atuais. $photo_ids = []; foreach ($user->get('field_user_photos')->getValue() as $item) { if (!empty($item['target_id'])) { $photo_ids[] = $item['target_id']; } } // Obter foto padrão atual. $default_photo_id = $user->get('field_user_default_photo')->target_id; // Se não há fotos, limpar foto padrão. if (empty($photo_ids)) { $user->set('field_user_default_photo', NULL); return; } // Se a foto padrão não está entre as fotos, selecionar a primeira. if (empty($default_photo_id) || !in_array($default_photo_id, $photo_ids)) { $user->set('field_user_default_photo', $photo_ids[0]); } } /** * Implements hook_site_tools_share_links(). */ function site_users_site_tools_share_links(): array { $user = \Drupal::routeMatch()->getParameter('user'); if (!($user instanceof UserInterface)) { return []; } if (!$user->hasField('field_user_social_links') || $user->get('field_user_social_links')->isEmpty()) { return []; } $links = []; $networks = SocialLinkItem::getNetworks(); foreach ($user->get('field_user_social_links') as $delta => $item) { if ($item->isEmpty()) { continue; } $network_label = $networks[$item->network] ?? $item->network; $links['social_' . $item->network] = [ 'content' => [ '#type' => 'link', '#title' => $network_label, '#url' => Url::fromUri($item->url), '#attributes' => [ 'class' => ['social-link', 'social-link--' . $item->network], 'target' => '_blank', 'rel' => 'noopener noreferrer', ], ], 'weight' => $delta, 'provider' => 'site_users', ]; } return $links; } /** * Obtém a foto padrão de um usuário. * * @param \Drupal\user\UserInterface $user * O usuário. * * @return \Drupal\media\MediaInterface|null * A entidade de mídia da foto padrão, ou NULL se não houver. */ function site_users_get_default_photo(UserInterface $user): ?MediaInterface { if (!$user->hasField('field_user_default_photo') || !$user->hasField('field_user_photos')) { return NULL; } // Tentar obter a foto padrão configurada. if (!$user->get('field_user_default_photo')->isEmpty()) { $default_photo = $user->get('field_user_default_photo')->entity; if ($default_photo instanceof MediaInterface) { return $default_photo; } } // Fallback: retornar a primeira foto disponível. if (!$user->get('field_user_photos')->isEmpty()) { $first_photo = $user->get('field_user_photos')->first()->entity; if ($first_photo instanceof MediaInterface) { return $first_photo; } } return NULL; }