mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/site_users.git
synced 2026-03-09 09:57:41 -03:00
feat: Adiciona field type social_link para redes sociais do usuário
Implementa o field type 'social_link' com seletor de rede e URL de
perfil, composto por:
- SocialLinkItem: field type com colunas 'network' (varchar 64) e
'url' (varchar 2048), cardinalidade ilimitada
- SocialLinkWidget: widget com select de rede e input de URL
- SocialLinkFormatter: formatter que renderiza links com classe CSS
por rede (social-link--{network}), target _blank e rel noopener
- config/optional: field.storage e field.field para user
- config/translations/pt-br: tradução do label e description
- hook_install e update_10002: configura form/view displays
- UserInfoBlock: expõe social_links via getSocialLinks()
- Template: adiciona seção de redes sociais e remove referências
obsoletas a category e dept_code
- translations/site_users.pt-br.po: strings do novo field type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.user.field_user_social_links
|
||||
module:
|
||||
- site_users
|
||||
- user
|
||||
id: user.user.field_user_social_links
|
||||
field_name: field_user_social_links
|
||||
entity_type: user
|
||||
bundle: user
|
||||
label: 'Social Links'
|
||||
description: 'Social network profile links.'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: {}
|
||||
default_value_callback: ''
|
||||
settings: {}
|
||||
field_type: social_link
|
||||
@@ -0,0 +1,18 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- site_users
|
||||
- user
|
||||
id: user.field_user_social_links
|
||||
field_name: field_user_social_links
|
||||
entity_type: user
|
||||
type: social_link
|
||||
settings: {}
|
||||
module: site_users
|
||||
locked: false
|
||||
cardinality: -1
|
||||
translatable: false
|
||||
indexes: {}
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
@@ -0,0 +1,2 @@
|
||||
label: 'Redes Sociais'
|
||||
description: 'Links de perfil em redes sociais.'
|
||||
@@ -67,6 +67,16 @@ function site_users_install() {
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Redes Sociais.
|
||||
if (!$form_display->getComponent('field_user_social_links')) {
|
||||
$form_display->setComponent('field_user_social_links', [
|
||||
'type' => 'social_link_widget',
|
||||
'weight' => 16,
|
||||
'settings' => [],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Foto Padrão - oculto no form (será gerenciado via hook_form_alter).
|
||||
$form_display->removeComponent('field_user_default_photo');
|
||||
|
||||
@@ -127,6 +137,17 @@ function site_users_install() {
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Redes Sociais.
|
||||
if (!$view_display->getComponent('field_user_social_links')) {
|
||||
$view_display->setComponent('field_user_social_links', [
|
||||
'type' => 'social_link_formatter',
|
||||
'weight' => 16,
|
||||
'label' => 'above',
|
||||
'settings' => [],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Foto Padrão.
|
||||
if (!$view_display->getComponent('field_user_default_photo')) {
|
||||
$view_display->setComponent('field_user_default_photo', [
|
||||
@@ -212,3 +233,57 @@ function site_users_update_10001() {
|
||||
|
||||
return t('Default photo field created successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the field_user_social_links field for social network profile links.
|
||||
*/
|
||||
function site_users_update_10002() {
|
||||
// Create field storage if it does not exist.
|
||||
if (!FieldStorageConfig::loadByName('user', 'field_user_social_links')) {
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => 'field_user_social_links',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'social_link',
|
||||
'module' => 'site_users',
|
||||
'cardinality' => -1,
|
||||
'translatable' => FALSE,
|
||||
])->save();
|
||||
}
|
||||
|
||||
// Create field instance if it does not exist.
|
||||
if (!FieldConfig::loadByName('user', 'user', 'field_user_social_links')) {
|
||||
FieldConfig::create([
|
||||
'field_name' => 'field_user_social_links',
|
||||
'entity_type' => 'user',
|
||||
'bundle' => 'user',
|
||||
'label' => 'Social Links',
|
||||
'description' => 'Social network profile links.',
|
||||
'required' => FALSE,
|
||||
])->save();
|
||||
}
|
||||
|
||||
// Add to form display.
|
||||
$form_display = EntityFormDisplay::load('user.user.default');
|
||||
if ($form_display && !$form_display->getComponent('field_user_social_links')) {
|
||||
$form_display->setComponent('field_user_social_links', [
|
||||
'type' => 'social_link_widget',
|
||||
'weight' => 16,
|
||||
'settings' => [],
|
||||
'region' => 'content',
|
||||
])->save();
|
||||
}
|
||||
|
||||
// Add to view display.
|
||||
$view_display = EntityViewDisplay::load('user.user.default');
|
||||
if ($view_display && !$view_display->getComponent('field_user_social_links')) {
|
||||
$view_display->setComponent('field_user_social_links', [
|
||||
'type' => 'social_link_formatter',
|
||||
'weight' => 16,
|
||||
'label' => 'above',
|
||||
'settings' => [],
|
||||
'region' => 'content',
|
||||
])->save();
|
||||
}
|
||||
|
||||
return t('Social links field created successfully.');
|
||||
}
|
||||
|
||||
@@ -55,8 +55,7 @@ function site_users_entity_field_access($operation, FieldDefinitionInterface $fi
|
||||
$profile_fields = [
|
||||
'field_user_name',
|
||||
'field_user_phone',
|
||||
|
||||
|
||||
'field_user_social_links',
|
||||
'field_user_bio',
|
||||
];
|
||||
|
||||
|
||||
@@ -109,9 +109,8 @@ class UserInfoBlock extends BlockBase implements ContainerFactoryPluginInterface
|
||||
'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() : '',
|
||||
];
|
||||
@@ -150,6 +149,34 @@ class UserInfoBlock extends BlockBase implements ContainerFactoryPluginInterface
|
||||
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.
|
||||
*
|
||||
|
||||
52
src/Plugin/Field/FieldFormatter/SocialLinkFormatter.php
Normal file
52
src/Plugin/Field/FieldFormatter/SocialLinkFormatter.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\site_users\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FormatterBase;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\site_users\Plugin\Field\FieldType\SocialLinkItem;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'social_link_formatter' formatter.
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "social_link_formatter",
|
||||
* label = @Translation("Social link"),
|
||||
* field_types = {
|
||||
* "social_link"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class SocialLinkFormatter extends FormatterBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewElements(FieldItemListInterface $items, $langcode): array {
|
||||
$elements = [];
|
||||
$networks = SocialLinkItem::getNetworks();
|
||||
|
||||
foreach ($items as $delta => $item) {
|
||||
if ($item->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$network_label = $networks[$item->network] ?? $item->network;
|
||||
|
||||
$elements[$delta] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $network_label,
|
||||
'#url' => Url::fromUri($item->url),
|
||||
'#attributes' => [
|
||||
'class' => ['social-link', 'social-link--' . $item->network],
|
||||
'target' => '_blank',
|
||||
'rel' => 'noopener noreferrer',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
}
|
||||
84
src/Plugin/Field/FieldType/SocialLinkItem.php
Normal file
84
src/Plugin/Field/FieldType/SocialLinkItem.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\site_users\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Field\FieldItemBase;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'social_link' field type.
|
||||
*
|
||||
* @FieldType(
|
||||
* id = "social_link",
|
||||
* label = @Translation("Social link"),
|
||||
* description = @Translation("Stores a social network type and profile URL."),
|
||||
* default_widget = "social_link_widget",
|
||||
* default_formatter = "social_link_formatter"
|
||||
* )
|
||||
*/
|
||||
class SocialLinkItem extends FieldItemBase {
|
||||
|
||||
/**
|
||||
* Returns the list of available social networks.
|
||||
*
|
||||
* @return array
|
||||
* Associative array keyed by machine name, valued by label.
|
||||
*/
|
||||
public static function getNetworks(): array {
|
||||
return [
|
||||
'facebook' => 'Facebook',
|
||||
'instagram' => 'Instagram',
|
||||
'linkedin' => 'LinkedIn',
|
||||
'twitter' => 'X (Twitter)',
|
||||
'youtube' => 'YouTube',
|
||||
'github' => 'GitHub',
|
||||
'tiktok' => 'TikTok',
|
||||
'telegram' => 'Telegram',
|
||||
'whatsapp' => 'WhatsApp',
|
||||
'pinterest' => 'Pinterest',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition): array {
|
||||
$properties['network'] = DataDefinition::create('string')
|
||||
->setLabel(t('Network'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
$properties['url'] = DataDefinition::create('uri')
|
||||
->setLabel(t('URL'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function schema(FieldStorageDefinitionInterface $field_definition): array {
|
||||
return [
|
||||
'columns' => [
|
||||
'network' => [
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
],
|
||||
'url' => [
|
||||
'type' => 'varchar',
|
||||
'length' => 2048,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEmpty(): bool {
|
||||
return empty($this->get('network')->getValue())
|
||||
|| empty($this->get('url')->getValue());
|
||||
}
|
||||
|
||||
}
|
||||
47
src/Plugin/Field/FieldWidget/SocialLinkWidget.php
Normal file
47
src/Plugin/Field/FieldWidget/SocialLinkWidget.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\site_users\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\site_users\Plugin\Field\FieldType\SocialLinkItem;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'social_link_widget' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "social_link_widget",
|
||||
* label = @Translation("Social link"),
|
||||
* field_types = {
|
||||
* "social_link"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class SocialLinkWidget extends WidgetBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state): array {
|
||||
$item = $items[$delta];
|
||||
|
||||
$element['network'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Network'),
|
||||
'#options' => ['' => $this->t('- Select -')] + SocialLinkItem::getNetworks(),
|
||||
'#default_value' => $item->network ?? '',
|
||||
];
|
||||
|
||||
$element['url'] = [
|
||||
'#type' => 'url',
|
||||
'#title' => $this->t('Profile URL'),
|
||||
'#default_value' => $item->url ?? '',
|
||||
'#maxlength' => 2048,
|
||||
'#placeholder' => 'https://',
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +1,19 @@
|
||||
{#
|
||||
/**
|
||||
* @file
|
||||
* Template para o bloco de informações do usuário.
|
||||
* Template for the user information block.
|
||||
*
|
||||
* Variáveis disponíveis:
|
||||
* - user_info: Array com informações do usuário:
|
||||
* - uid: ID do usuário
|
||||
* - username: Nome de usuário (display name)
|
||||
* - name: Nome completo
|
||||
* - phone: Telefone
|
||||
* - category: Categoria
|
||||
* - dept_code: Código do departamento
|
||||
* - bio: Biografia
|
||||
* - photo_url: URL da foto padrão
|
||||
* - photo_alt: Texto alternativo da foto
|
||||
* - user: Entidade do usuário.
|
||||
* 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.
|
||||
*/
|
||||
#}
|
||||
<div class="site-user-info-block">
|
||||
@@ -35,22 +34,9 @@
|
||||
{{ user_info.name ?: user_info.username }}
|
||||
</h2>
|
||||
|
||||
{% if user_info.category %}
|
||||
<div class="site-user-info-block__category">
|
||||
{{ user_info.category }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if user_info.dept_code %}
|
||||
<div class="site-user-info-block__dept">
|
||||
<span class="site-user-info-block__label">{{ 'Departamento:'|t }}</span>
|
||||
{{ user_info.dept_code }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if user_info.phone %}
|
||||
<div class="site-user-info-block__phone">
|
||||
<span class="site-user-info-block__label">{{ 'Telefone:'|t }}</span>
|
||||
<span class="site-user-info-block__label">{{ 'Phone:'|t }}</span>
|
||||
<a href="tel:{{ user_info.phone }}">{{ user_info.phone }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -60,5 +46,18 @@
|
||||
{{ user_info.bio }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if user_info.social_links %}
|
||||
<div class="site-user-info-block__social-links">
|
||||
{% for link in user_info.social_links %}
|
||||
<a href="{{ link.url }}"
|
||||
class="social-link social-link--{{ link.network }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
{{ link.network }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -102,3 +102,32 @@ msgstr "Você pode adicionar no máximo @max fotos. Atualmente há @count fotos
|
||||
# Install/update
|
||||
msgid "Default photo field created successfully."
|
||||
msgstr "Campo de foto padrão criado com sucesso."
|
||||
|
||||
msgid "Social links field created successfully."
|
||||
msgstr "Campo de redes sociais criado com sucesso."
|
||||
|
||||
# Social link field type
|
||||
msgid "Social link"
|
||||
msgstr "Link social"
|
||||
|
||||
msgid "Stores a social network type and profile URL."
|
||||
msgstr "Armazena um tipo de rede social e URL de perfil."
|
||||
|
||||
msgid "Network"
|
||||
msgstr "Rede"
|
||||
|
||||
msgid "- Select -"
|
||||
msgstr "- Selecione -"
|
||||
|
||||
msgid "Profile URL"
|
||||
msgstr "URL do perfil"
|
||||
|
||||
msgid "Social Links"
|
||||
msgstr "Redes Sociais"
|
||||
|
||||
msgid "Social network profile links."
|
||||
msgstr "Links de perfil em redes sociais."
|
||||
|
||||
# Template
|
||||
msgid "Phone:"
|
||||
msgstr "Telefone:"
|
||||
|
||||
Reference in New Issue
Block a user