Files
site_users/site_users.module
Quintino A. G. Souza af7ebfb947 feat: Expõe links sociais do usuário no bloco ShareLinks do site_tools
Implementa hook_site_tools_share_links() para fornecer os links do campo
field_user_social_links ao ShareLinksBlock quando em página de perfil de
usuário. Adiciona site_tools como dependência do módulo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 08:23:42 -03:00

409 lines
13 KiB
Plaintext

<?php
/**
* @file
* Módulo Site Users - customizações de usuários do site.
*/
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\field\FieldConfigInterface;
use Drupal\media\MediaInterface;
use Drupal\site_users\Plugin\Field\FieldType\SocialLinkItem;
use Drupal\user\UserInterface;
/**
* Implements hook_theme().
*/
function site_users_theme($existing, $type, $theme, $path) {
return [
'user__full' => [
'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;
}