feat: Módulo Site Users para customização de perfis de usuário

Módulo Drupal para gerenciamento de campos e fotos de perfil de usuários:
- Campos customizados: nome, telefone, categoria, departamento, biografia
- Suporte a múltiplas fotos com seleção de foto padrão
- Controle de permissões granular para visualização e edição
- Bloco de informações do usuário para exibição em páginas
- Configurações administrativas para limite de fotos e integração LDAP

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-04 07:25:51 -03:00
commit 6215759045
28 changed files with 1554 additions and 0 deletions

View File

@@ -0,0 +1,192 @@
<?php
namespace Drupal\site_users\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\user\UserInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Bloco com informações básicas do usuário.
*
* Exibe automaticamente as informações do usuário da rota /user/{id}.
*
* @Block(
* id = "site_users_info_block",
* admin_label = @Translation("Informações do Usuário"),
* category = @Translation("Site Users")
* )
*/
class UserInfoBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The current route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a new UserInfoBlock instance.
*
* @param array $configuration
* The plugin configuration.
* @param string $plugin_id
* The plugin ID.
* @param mixed $plugin_definition
* The plugin definition.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
RouteMatchInterface $route_match,
EntityTypeManagerInterface $entity_type_manager
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->routeMatch = $route_match;
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('current_route_match'),
$container->get('entity_type.manager')
);
}
/**
* {@inheritdoc}
*/
public function build() {
$user = $this->getUserFromContext();
if (!$user instanceof UserInterface) {
return [];
}
// Obter foto padrão.
$default_photo = NULL;
$default_photo_url = NULL;
if (function_exists('site_users_get_default_photo')) {
$default_photo = site_users_get_default_photo($user);
if ($default_photo) {
// Usar o source field do media (forma padrão do Drupal).
$source_field = $default_photo->getSource()->getConfiguration()['source_field'];
if ($default_photo->hasField($source_field) && !$default_photo->get($source_field)->isEmpty()) {
$file = $default_photo->get($source_field)->entity;
if ($file) {
$default_photo_url = \Drupal::service('file_url_generator')->generateAbsoluteString($file->getFileUri());
}
}
}
}
// Coletar informações do usuário.
$user_info = [
'uid' => $user->id(),
'username' => $user->getDisplayName(),
'name' => $this->getFieldValue($user, 'field_user_name'),
'phone' => $this->getFieldValue($user, 'field_user_phone'),
'category' => $this->getFieldValue($user, 'field_user_category'),
'dept_code' => $this->getFieldValue($user, 'field_user_dept_code'),
'bio' => $this->getFieldValue($user, 'field_user_bio'),
'photo_url' => $default_photo_url,
'photo_alt' => $default_photo ? $default_photo->label() : '',
];
return [
'#theme' => 'site_users_info_block',
'#user_info' => $user_info,
'#user' => $user,
'#attached' => [
'library' => [
'site_users/user-info-block',
],
],
];
}
/**
* Obtém o usuário da rota atual.
*
* @return \Drupal\user\UserInterface|null
* O usuário ou NULL se não estiver em uma página de usuário.
*/
protected function getUserFromContext(): ?UserInterface {
// Obter da rota /user/{user}.
$user = $this->routeMatch->getParameter('user');
if ($user instanceof UserInterface) {
return $user;
}
// Se for apenas o ID, carregar o usuário.
if (is_numeric($user)) {
return $this->entityTypeManager->getStorage('user')->load($user);
}
return NULL;
}
/**
* Obtém o valor de um campo do usuário.
*
* @param \Drupal\user\UserInterface $user
* O usuário.
* @param string $field_name
* O nome do campo.
*
* @return string|null
* O valor do campo ou NULL.
*/
protected function getFieldValue(UserInterface $user, string $field_name): ?string {
if ($user->hasField($field_name) && !$user->get($field_name)->isEmpty()) {
return $user->get($field_name)->value;
}
return NULL;
}
/**
* {@inheritdoc}
*/
public function getCacheTags() {
$tags = parent::getCacheTags();
$user = $this->getUserFromContext();
if ($user instanceof UserInterface) {
$tags = Cache::mergeTags($tags, $user->getCacheTags());
}
return $tags;
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return Cache::mergeContexts(parent::getCacheContexts(), ['route', 'user']);
}
}