[ '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(); } // Lista de campos controlados pelo módulo. $profile_fields = [ 'field_user_name', 'field_user_phone', 'field_user_category', 'field_user_dept_code', 'field_user_bio', ]; $photo_fields = [ 'field_user_photos', 'field_user_default_photo', ]; $field_name = $field_definition->getName(); // Verificar se é um campo de perfil. if (in_array($field_name, $profile_fields)) { return site_users_check_profile_field_access($operation, $account, $items); } // Verificar se é um campo de fotos. if (in_array($field_name, $photo_fields)) { return site_users_check_photo_field_access($operation, $account, $items); } return AccessResult::neutral(); } /** * Verifica acesso aos campos de perfil. */ function site_users_check_profile_field_access($operation, AccountInterface $account, ?FieldItemListInterface $items = NULL) { // 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') { // Pode ver qualquer perfil. if ($account->hasPermission('view any user profile fields')) { return AccessResult::allowed()->cachePerPermissions(); } // Pode ver apenas o próprio perfil. if ($is_own && $account->hasPermission('view own user profile fields')) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser(); } return AccessResult::forbidden()->cachePerPermissions()->cachePerUser(); } if ($operation === 'edit') { // Pode editar qualquer perfil. if ($account->hasPermission('edit any user profile fields')) { return AccessResult::allowed()->cachePerPermissions(); } // Pode editar apenas o próprio perfil. if ($is_own && $account->hasPermission('edit own user profile fields')) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser(); } 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) { // 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') { // Fotos seguem a mesma regra dos campos de perfil para visualização. if ($account->hasPermission('view any user profile fields')) { return AccessResult::allowed()->cachePerPermissions(); } if ($is_own && $account->hasPermission('view own user profile fields')) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser(); } return AccessResult::forbidden()->cachePerPermissions()->cachePerUser(); } if ($operation === 'edit') { // Pode gerenciar fotos de qualquer usuário. if ($account->hasPermission('manage user photos')) { return AccessResult::allowed()->cachePerPermissions(); } // Pode gerenciar apenas as próprias fotos. if ($is_own && $account->hasPermission('manage own user photos')) { return AccessResult::allowed()->cachePerPermissions()->cachePerUser(); } 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]); } } /** * 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; }