diff --git a/modules/site_users_microsite/site_users_microsite.module b/modules/site_users_microsite/site_users_microsite.module index fdd43fc..3da6950 100644 --- a/modules/site_users_microsite/site_users_microsite.module +++ b/modules/site_users_microsite/site_users_microsite.module @@ -7,6 +7,24 @@ use Drupal\user\UserInterface; +/** + * Implements hook_theme(). + */ +function site_users_microsite_theme(): array { + return [ + 'microsite_header_block' => [ + 'variables' => [ + 'photo_url' => NULL, + 'photo_alt' => '', + 'name' => NULL, + 'bio' => NULL, + 'phone' => NULL, + 'email' => NULL, + ], + ], + ]; +} + /** * Implements hook_preprocess_page(). * diff --git a/modules/site_users_microsite/src/Plugin/Block/MicrositeHeaderBlock.php b/modules/site_users_microsite/src/Plugin/Block/MicrositeHeaderBlock.php new file mode 100644 index 0000000..946f94e --- /dev/null +++ b/modules/site_users_microsite/src/Plugin/Block/MicrositeHeaderBlock.php @@ -0,0 +1,166 @@ +get('current_route_match'), + $container->get('entity_type.manager'), + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration(): array { + return ['label_display' => '0'] + parent::defaultConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function build(): array { + $user = $this->getUser(); + if (!$user instanceof UserInterface) { + return []; + } + + return [ + '#theme' => 'microsite_header_block', + '#photo_url' => $this->getPhotoUrl($user), + '#photo_alt' => $this->getPhotoAlt($user), + '#name' => $this->getFieldValue($user, 'field_user_name') ?: $user->getDisplayName(), + '#bio' => $this->getProcessedValue($user, 'field_user_bio'), + '#phone' => $this->getFieldValue($user, 'field_user_phone'), + '#email' => $user->getEmail(), + '#cache' => [ + 'tags' => $user->getCacheTags(), + 'contexts' => ['route'], + ], + ]; + } + + /** + * Retorna o usuário da rota atual. + */ + protected function getUser(): ?UserInterface { + $user = $this->routeMatch->getParameter('user'); + if ($user instanceof UserInterface) { + return $user; + } + if (is_numeric($user)) { + return $this->entityTypeManager->getStorage('user')->load($user); + } + return NULL; + } + + /** + * Retorna a URL absoluta da foto padrão do usuário. + */ + protected function getPhotoUrl(UserInterface $user): ?string { + if (!function_exists('site_users_get_default_photo')) { + return NULL; + } + $media = site_users_get_default_photo($user); + if (!$media) { + return NULL; + } + $source_field = $media->getSource()->getConfiguration()['source_field']; + if (!$media->hasField($source_field) || $media->get($source_field)->isEmpty()) { + return NULL; + } + $file = $media->get($source_field)->entity; + if (!$file) { + return NULL; + } + return \Drupal::service('file_url_generator')->generateAbsoluteString($file->getFileUri()); + } + + /** + * Retorna o texto alternativo da foto padrão. + */ + protected function getPhotoAlt(UserInterface $user): string { + if (!function_exists('site_users_get_default_photo')) { + return ''; + } + $media = site_users_get_default_photo($user); + return $media ? $media->label() : ''; + } + + /** + * Retorna o valor de texto de um campo do usuário. + */ + 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; + } + + /** + * Retorna o valor processado (HTML filtrado) de um campo text_long. + * + * Usa ->processed, que aplica o formato de texto configurado e retorna um + * objeto Markup — o Twig renderiza como HTML sem escape adicional. + */ + protected function getProcessedValue(UserInterface $user, string $field_name): ?string { + if ($user->hasField($field_name) && !$user->get($field_name)->isEmpty()) { + return $user->get($field_name)->processed; + } + return NULL; + } + + /** + * {@inheritdoc} + */ + public function getCacheTags(): array { + $user = $this->getUser(); + if ($user instanceof UserInterface) { + return Cache::mergeTags(parent::getCacheTags(), $user->getCacheTags()); + } + return parent::getCacheTags(); + } + + /** + * {@inheritdoc} + */ + public function getCacheContexts(): array { + return Cache::mergeContexts(parent::getCacheContexts(), ['route']); + } + +} diff --git a/modules/site_users_microsite/templates/microsite-header-block.html.twig b/modules/site_users_microsite/templates/microsite-header-block.html.twig new file mode 100644 index 0000000..4a1a8d8 --- /dev/null +++ b/modules/site_users_microsite/templates/microsite-header-block.html.twig @@ -0,0 +1,59 @@ +{# +/** + * @file + * Template do bloco de cabeçalho do microsite. + * + * Variáveis: + * - photo_url: URL absoluta da foto padrão (string|null). + * - photo_alt: Texto alternativo da foto (string). + * - name: Nome completo do usuário (string). + * - roles: Array de rótulos das roles do usuário (string[]). + * - bio: Biografia (string|null). + * - phone: Telefone (string|null). + * - email: E-mail (string|null). + */ +#} +
+ +
+ {% if photo_url %} + {{ photo_alt }} + {% else %} + + {% endif %} +
+ +
+ +

{{ name }}

+ + {% if bio %} +
{{ bio|raw }}
+ {% endif %} + + {% if phone or email %} + + {% endif %} + +
+ +
diff --git a/site_users.module b/site_users.module index 64cea82..22ca1cd 100644 --- a/site_users.module +++ b/site_users.module @@ -32,13 +32,7 @@ function site_users_theme($existing, $type, $theme, $path) { 'template' => 'user--restricted', 'base hook' => 'user', ], - 'site_users_info_block' => [ - 'template' => 'site-user-info-block', - 'variables' => [ - 'user_info' => [], - 'user' => NULL, - ], - ], + ]; } diff --git a/src/Plugin/Block/UserInfoBlock.php b/src/Plugin/Block/UserInfoBlock.php deleted file mode 100644 index 3832a8f..0000000 --- a/src/Plugin/Block/UserInfoBlock.php +++ /dev/null @@ -1,219 +0,0 @@ -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'), - 'bio' => $this->getFieldValue($user, 'field_user_bio'), - 'social_links' => $this->getSocialLinks($user), - '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; - } - - /** - * Returns the social network links of a user. - * - * @param \Drupal\user\UserInterface $user - * The user entity. - * - * @return array - * Array of items with 'network' and 'url' keys. - */ - protected function getSocialLinks(UserInterface $user): array { - $links = []; - - if (!$user->hasField('field_user_social_links') || $user->get('field_user_social_links')->isEmpty()) { - return $links; - } - - foreach ($user->get('field_user_social_links') as $item) { - if (!$item->isEmpty()) { - $links[] = [ - 'network' => $item->network, - 'url' => $item->url, - ]; - } - } - - return $links; - } - - /** - * 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']); - } - -} diff --git a/templates/site-user-info-block.html.twig b/templates/site-user-info-block.html.twig deleted file mode 100644 index 7e20d4a..0000000 --- a/templates/site-user-info-block.html.twig +++ /dev/null @@ -1,63 +0,0 @@ -{# -/** - * @file - * Template for the user information block. - * - * Available variables: - * - user_info: Array with user information: - * - uid: User ID - * - username: Display name - * - name: Full name - * - phone: Phone number - * - bio: Biography - * - social_links: Array of social links, each with 'network' and 'url' keys - * - photo_url: Default photo URL - * - photo_alt: Photo alternative text - * - user: User entity. - */ -#} -
-
- {% if user_info.photo_url %} - - {% else %} - - {% endif %} -
- -
- - - {% if user_info.phone %} - - {% endif %} - - {% if user_info.bio %} - - {% endif %} - - {% if user_info.social_links %} - - {% endif %} -
-
diff --git a/themes/site_users_microsite_theme/css/microsite.css b/themes/site_users_microsite_theme/css/microsite.css index 7f539d8..6fd2863 100644 --- a/themes/site_users_microsite_theme/css/microsite.css +++ b/themes/site_users_microsite_theme/css/microsite.css @@ -154,6 +154,105 @@ body.microsite { margin-bottom: 1.5rem; } +/* Microsite Header Block --------------------------------------------------- */ +/* + * Bloco .msite-header-block: foto circular, nome (h1), meta (roles + + * departamento), biografia e lista de contatos. + * Assume fundo escuro herdado do .microsite-header. + */ + +.msite-header-block { + display: flex; + align-items: flex-start; + gap: 1.75rem; + max-width: 1500px; + margin: 0 auto; + padding: 2rem 1.5rem; + color: #fff; +} + +/* --- Foto ----------------------------------------------------------------- */ + +.msite-header-block__photo-wrap { + flex-shrink: 0; +} + +.msite-header-block__photo { + display: block; + width: 220px; + height: 280px; + border-radius: 50%; + object-fit: cover; + border: 3px solid rgba(255, 255, 255, 0.35); +} + +/* Fallback de iniciais quando não há foto */ +.msite-header-block__photo--initials { + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.15); + font-size: 4rem; + font-weight: 700; + color: #fff; + user-select: none; +} + +/* --- Informações ---------------------------------------------------------- */ + +.msite-header-block__info { + flex: 1; + min-width: 0; +} + +.msite-header-block__name { + margin: 0 0 0.3rem; + font-size: 1.8rem; + font-weight: 700; + line-height: 1.2; + color: #fff; +} + +.msite-header-block__meta { + margin: 0 0 0.75rem; + font-size: 0.95rem; + font-style: italic; + color: rgba(255, 255, 255, 0.75); +} + +.msite-header-block__bio { + margin-bottom: 0.75rem; + font-size: 0.9rem; + line-height: 1.5; + color: rgba(255, 255, 255, 0.85); +} + +/* --- Contatos ------------------------------------------------------------- */ + +.msite-header-block__contact { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-wrap: wrap; + gap: 0.3rem 1.5rem; + font-size: 0.85rem; + color: rgba(255, 255, 255, 0.8); +} + +.msite-header-block__contact-label { + font-weight: 600; +} + +.msite-header-block__contact a { + color: hsl(202, 79%, 70%); + text-decoration: none; +} + +.msite-header-block__contact a:hover { + text-decoration: underline; +} + /* Top Bar ------------------------------------------------------------------ */ /* * Barra estreita no topo da página, antes do header.