mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/site_users.git
synced 2026-03-08 01:17:41 -03:00
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:
141
README.md
Normal file
141
README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Site Users
|
||||
|
||||
Módulo Drupal para customização de perfis de usuário, incluindo campos adicionais e gerenciamento de fotos.
|
||||
|
||||
## Funcionalidades
|
||||
|
||||
- **Campos de perfil customizados:**
|
||||
- Nome completo
|
||||
- Telefone
|
||||
- Categoria
|
||||
- Código do departamento
|
||||
- Biografia
|
||||
|
||||
- **Gerenciamento de fotos:**
|
||||
- Suporte a múltiplas fotos por usuário
|
||||
- Seleção de foto padrão
|
||||
- Limite configurável de quantidade de fotos
|
||||
- Integração com Media Library
|
||||
|
||||
- **Controle de permissões granular:**
|
||||
- Visualizar/editar campos do próprio perfil
|
||||
- Visualizar/editar campos de qualquer usuário
|
||||
- Gerenciar fotos próprias ou de outros usuários
|
||||
|
||||
- **Bloco de informações:**
|
||||
- Exibe dados do usuário em páginas de perfil
|
||||
- Template customizável via Twig
|
||||
|
||||
## Requisitos
|
||||
|
||||
- Drupal 10 ou 11
|
||||
- Módulos core: User, Telephone, Text, Media, Media Library
|
||||
|
||||
## Instalação
|
||||
|
||||
1. Copie o módulo para `modules/custom/site_users`
|
||||
2. Ative o módulo via Drush ou interface administrativa:
|
||||
|
||||
```bash
|
||||
drush en site_users
|
||||
```
|
||||
|
||||
## Configuração
|
||||
|
||||
Acesse as configurações em:
|
||||
**Administração > Configuração > Módulos Locais > Site Users**
|
||||
|
||||
`/admin/config/local-modules/site-users`
|
||||
|
||||
### Opções disponíveis
|
||||
|
||||
| Configuração | Descrição | Padrão |
|
||||
|--------------|-----------|--------|
|
||||
| Quantidade de fotos | Número máximo de fotos por usuário | 5 |
|
||||
| Atributo LDAP | Nome do atributo LDAP para foto (se aplicável) | jpegPhoto |
|
||||
|
||||
## Permissões
|
||||
|
||||
| Permissão | Descrição |
|
||||
|-----------|-----------|
|
||||
| `administer site_users settings` | Administrar configurações do módulo |
|
||||
| `view own user profile fields` | Visualizar campos do próprio perfil |
|
||||
| `view any user profile fields` | Visualizar campos de qualquer usuário |
|
||||
| `edit own user profile fields` | Editar campos do próprio perfil |
|
||||
| `edit any user profile fields` | Editar campos de qualquer usuário |
|
||||
| `manage own user photos` | Gerenciar próprias fotos |
|
||||
| `manage user photos` | Gerenciar fotos de qualquer usuário |
|
||||
|
||||
## Uso do Bloco
|
||||
|
||||
O módulo fornece o bloco **"Informações do Usuário"** que pode ser posicionado em regiões do tema para exibir informações do perfil nas páginas `/user/{id}`.
|
||||
|
||||
Para adicionar o bloco:
|
||||
1. Acesse **Estrutura > Layout de blocos**
|
||||
2. Adicione o bloco "Informações do Usuário" na região desejada
|
||||
3. Configure a visibilidade para páginas de usuário
|
||||
|
||||
## Customização de Templates
|
||||
|
||||
Os templates podem ser sobrescritos no tema:
|
||||
|
||||
- `site-user-info-block.html.twig` - Bloco de informações do usuário
|
||||
- `user--full.html.twig` - Página completa do usuário
|
||||
|
||||
### Variáveis disponíveis no bloco
|
||||
|
||||
```twig
|
||||
{{ user_info.uid }} {# ID do usuário #}
|
||||
{{ user_info.username }} {# Nome de exibição #}
|
||||
{{ user_info.name }} {# Nome completo #}
|
||||
{{ user_info.phone }} {# Telefone #}
|
||||
{{ user_info.category }} {# Categoria #}
|
||||
{{ user_info.dept_code }} {# Código do departamento #}
|
||||
{{ user_info.bio }} {# Biografia #}
|
||||
{{ user_info.photo_url }} {# URL da foto padrão #}
|
||||
{{ user_info.photo_alt }} {# Texto alternativo da foto #}
|
||||
{{ user }} {# Entidade completa do usuário #}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Obter foto padrão de um usuário
|
||||
|
||||
```php
|
||||
$photo = site_users_get_default_photo($user);
|
||||
if ($photo instanceof \Drupal\media\MediaInterface) {
|
||||
// Usar a foto
|
||||
}
|
||||
```
|
||||
|
||||
## Estrutura do Módulo
|
||||
|
||||
```
|
||||
site_users/
|
||||
├── config/
|
||||
│ ├── install/
|
||||
│ │ └── site_users.settings.yml
|
||||
│ └── optional/
|
||||
│ └── field.*.yml
|
||||
├── css/
|
||||
│ └── site-user-info-block.css
|
||||
├── src/
|
||||
│ ├── Form/
|
||||
│ │ └── SiteUsersSettingsForm.php
|
||||
│ └── Plugin/Block/
|
||||
│ └── UserInfoBlock.php
|
||||
├── templates/
|
||||
│ ├── site-user-info-block.html.twig
|
||||
│ └── user--full.html.twig
|
||||
├── site_users.info.yml
|
||||
├── site_users.install
|
||||
├── site_users.libraries.yml
|
||||
├── site_users.links.menu.yml
|
||||
├── site_users.module
|
||||
├── site_users.permissions.yml
|
||||
└── site_users.routing.yml
|
||||
```
|
||||
|
||||
## Licença
|
||||
|
||||
Este módulo é software livre distribuído sob a licença GPL-2.0-or-later.
|
||||
3
config/install/site_users.settings.yml
Normal file
3
config/install/site_users.settings.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
photos:
|
||||
max_count: 5
|
||||
ldap_attribute: 'jpegPhoto'
|
||||
20
config/optional/field.field.user.user.field_user_bio.yml
Normal file
20
config/optional/field.field.user.user.field_user_bio.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.user.field_user_bio
|
||||
module:
|
||||
- text
|
||||
- user
|
||||
id: user.user.field_user_bio
|
||||
field_name: field_user_bio
|
||||
entity_type: user
|
||||
bundle: user
|
||||
label: Biografia
|
||||
description: 'Uma breve descrição sobre o usuário.'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: text_long
|
||||
@@ -0,0 +1,19 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.user.field_user_category
|
||||
module:
|
||||
- user
|
||||
id: user.user.field_user_category
|
||||
field_name: field_user_category
|
||||
entity_type: user
|
||||
bundle: user
|
||||
label: Categoria
|
||||
description: 'Categoria do usuário.'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: string
|
||||
@@ -0,0 +1,30 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.user.field_user_default_photo
|
||||
- media.type.image
|
||||
module:
|
||||
- media
|
||||
- user
|
||||
id: user.user.field_user_default_photo
|
||||
field_name: field_user_default_photo
|
||||
entity_type: user
|
||||
bundle: user
|
||||
label: Foto Padrão
|
||||
description: 'Selecione a foto principal do perfil.'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
handler: 'default:media'
|
||||
handler_settings:
|
||||
target_bundles:
|
||||
image: image
|
||||
sort:
|
||||
field: _none
|
||||
direction: ASC
|
||||
auto_create: false
|
||||
auto_create_bundle: ''
|
||||
field_type: entity_reference
|
||||
@@ -0,0 +1,19 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.user.field_user_dept_code
|
||||
module:
|
||||
- user
|
||||
id: user.user.field_user_dept_code
|
||||
field_name: field_user_dept_code
|
||||
entity_type: user
|
||||
bundle: user
|
||||
label: Código do Departamento
|
||||
description: 'Código do departamento do usuário.'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: string
|
||||
19
config/optional/field.field.user.user.field_user_name.yml
Normal file
19
config/optional/field.field.user.user.field_user_name.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.user.field_user_name
|
||||
module:
|
||||
- user
|
||||
id: user.user.field_user_name
|
||||
field_name: field_user_name
|
||||
entity_type: user
|
||||
bundle: user
|
||||
label: Nome
|
||||
description: 'Nome completo do usuário.'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: string
|
||||
20
config/optional/field.field.user.user.field_user_phone.yml
Normal file
20
config/optional/field.field.user.user.field_user_phone.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.user.field_user_phone
|
||||
module:
|
||||
- telephone
|
||||
- user
|
||||
id: user.user.field_user_phone
|
||||
field_name: field_user_phone
|
||||
entity_type: user
|
||||
bundle: user
|
||||
label: Telefone
|
||||
description: 'Número de telefone do usuário.'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: telephone
|
||||
30
config/optional/field.field.user.user.field_user_photos.yml
Normal file
30
config/optional/field.field.user.user.field_user_photos.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.user.field_user_photos
|
||||
- media.type.image
|
||||
module:
|
||||
- media
|
||||
- user
|
||||
id: user.user.field_user_photos
|
||||
field_name: field_user_photos
|
||||
entity_type: user
|
||||
bundle: user
|
||||
label: Fotos
|
||||
description: 'Fotos do usuário.'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
handler: 'default:media'
|
||||
handler_settings:
|
||||
target_bundles:
|
||||
image: image
|
||||
sort:
|
||||
field: _none
|
||||
direction: ASC
|
||||
auto_create: false
|
||||
auto_create_bundle: ''
|
||||
field_type: entity_reference
|
||||
18
config/optional/field.storage.user.field_user_bio.yml
Normal file
18
config/optional/field.storage.user.field_user_bio.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- text
|
||||
- user
|
||||
id: user.field_user_bio
|
||||
field_name: field_user_bio
|
||||
entity_type: user
|
||||
type: text_long
|
||||
settings: { }
|
||||
module: text
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
20
config/optional/field.storage.user.field_user_category.yml
Normal file
20
config/optional/field.storage.user.field_user_category.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: user.field_user_category
|
||||
field_name: field_user_category
|
||||
entity_type: user
|
||||
type: string
|
||||
settings:
|
||||
max_length: 255
|
||||
is_ascii: false
|
||||
case_sensitive: false
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
@@ -0,0 +1,19 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- media
|
||||
- user
|
||||
id: user.field_user_default_photo
|
||||
field_name: field_user_default_photo
|
||||
entity_type: user
|
||||
type: entity_reference
|
||||
settings:
|
||||
target_type: media
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: false
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
20
config/optional/field.storage.user.field_user_dept_code.yml
Normal file
20
config/optional/field.storage.user.field_user_dept_code.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: user.field_user_dept_code
|
||||
field_name: field_user_dept_code
|
||||
entity_type: user
|
||||
type: string
|
||||
settings:
|
||||
max_length: 255
|
||||
is_ascii: false
|
||||
case_sensitive: false
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
20
config/optional/field.storage.user.field_user_name.yml
Normal file
20
config/optional/field.storage.user.field_user_name.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
||||
id: user.field_user_name
|
||||
field_name: field_user_name
|
||||
entity_type: user
|
||||
type: string
|
||||
settings:
|
||||
max_length: 255
|
||||
is_ascii: false
|
||||
case_sensitive: false
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
18
config/optional/field.storage.user.field_user_phone.yml
Normal file
18
config/optional/field.storage.user.field_user_phone.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- telephone
|
||||
- user
|
||||
id: user.field_user_phone
|
||||
field_name: field_user_phone
|
||||
entity_type: user
|
||||
type: telephone
|
||||
settings: { }
|
||||
module: telephone
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
19
config/optional/field.storage.user.field_user_photos.yml
Normal file
19
config/optional/field.storage.user.field_user_photos.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- media
|
||||
- user
|
||||
id: user.field_user_photos
|
||||
field_name: field_user_photos
|
||||
entity_type: user
|
||||
type: entity_reference
|
||||
settings:
|
||||
target_type: media
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: -1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
97
css/site-user-info-block.css
Normal file
97
css/site-user-info-block.css
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @file
|
||||
* Estilos para o bloco de informações do usuário.
|
||||
*/
|
||||
|
||||
.site-user-info-block {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
padding: 1.5rem;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.site-user-info-block__photo {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.site-user-info-block__image {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.site-user-info-block__no-photo {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
background-color: #6c757d;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.site-user-info-block__initials {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.site-user-info-block__details {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.site-user-info-block__name {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.site-user-info-block__category {
|
||||
font-size: 1.1rem;
|
||||
color: #495057;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.site-user-info-block__dept,
|
||||
.site-user-info-block__phone {
|
||||
font-size: 0.95rem;
|
||||
color: #6c757d;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.site-user-info-block__label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.site-user-info-block__phone a {
|
||||
color: #0d6efd;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.site-user-info-block__phone a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.site-user-info-block__bio {
|
||||
margin-top: 1rem;
|
||||
font-size: 0.95rem;
|
||||
color: #495057;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Responsivo */
|
||||
@media (max-width: 576px) {
|
||||
.site-user-info-block {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.site-user-info-block__details {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
11
site_users.info.yml
Normal file
11
site_users.info.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
name: Site Users
|
||||
type: module
|
||||
description: 'Customizações de usuários do site, incluindo campos e templates.'
|
||||
core_version_requirement: ^10 || ^11
|
||||
package: Custom
|
||||
dependencies:
|
||||
- drupal:user
|
||||
- drupal:telephone
|
||||
- drupal:text
|
||||
- drupal:media
|
||||
- drupal:media_library
|
||||
266
site_users.install
Normal file
266
site_users.install
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the Site Users module.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function site_users_install() {
|
||||
// Configurar form display para os campos de usuário.
|
||||
$form_display = EntityFormDisplay::load('user.user.default');
|
||||
if ($form_display) {
|
||||
// Campo Nome.
|
||||
if (!$form_display->getComponent('field_user_name')) {
|
||||
$form_display->setComponent('field_user_name', [
|
||||
'type' => 'string_textfield',
|
||||
'weight' => 10,
|
||||
'settings' => [
|
||||
'size' => 60,
|
||||
'placeholder' => '',
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Telefone.
|
||||
if (!$form_display->getComponent('field_user_phone')) {
|
||||
$form_display->setComponent('field_user_phone', [
|
||||
'type' => 'telephone_default',
|
||||
'weight' => 11,
|
||||
'settings' => [
|
||||
'placeholder' => '',
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Categoria.
|
||||
if (!$form_display->getComponent('field_user_category')) {
|
||||
$form_display->setComponent('field_user_category', [
|
||||
'type' => 'string_textfield',
|
||||
'weight' => 12,
|
||||
'settings' => [
|
||||
'size' => 60,
|
||||
'placeholder' => '',
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Código do Departamento.
|
||||
if (!$form_display->getComponent('field_user_dept_code')) {
|
||||
$form_display->setComponent('field_user_dept_code', [
|
||||
'type' => 'string_textfield',
|
||||
'weight' => 13,
|
||||
'settings' => [
|
||||
'size' => 60,
|
||||
'placeholder' => '',
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Fotos - Media Library widget.
|
||||
if (!$form_display->getComponent('field_user_photos')) {
|
||||
$form_display->setComponent('field_user_photos', [
|
||||
'type' => 'media_library_widget',
|
||||
'weight' => 14,
|
||||
'settings' => [
|
||||
'media_types' => [],
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Biografia.
|
||||
if (!$form_display->getComponent('field_user_bio')) {
|
||||
$form_display->setComponent('field_user_bio', [
|
||||
'type' => 'text_textarea',
|
||||
'weight' => 15,
|
||||
'settings' => [
|
||||
'rows' => 5,
|
||||
'placeholder' => '',
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Foto Padrão - oculto no form (será gerenciado via hook_form_alter).
|
||||
$form_display->removeComponent('field_user_default_photo');
|
||||
|
||||
$form_display->save();
|
||||
}
|
||||
|
||||
// Configurar view display para os campos de usuário.
|
||||
$view_display = EntityViewDisplay::load('user.user.default');
|
||||
if ($view_display) {
|
||||
// Campo Nome.
|
||||
if (!$view_display->getComponent('field_user_name')) {
|
||||
$view_display->setComponent('field_user_name', [
|
||||
'type' => 'string',
|
||||
'weight' => 10,
|
||||
'label' => 'above',
|
||||
'settings' => [
|
||||
'link_to_entity' => FALSE,
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Telefone.
|
||||
if (!$view_display->getComponent('field_user_phone')) {
|
||||
$view_display->setComponent('field_user_phone', [
|
||||
'type' => 'telephone_link',
|
||||
'weight' => 11,
|
||||
'label' => 'above',
|
||||
'settings' => [
|
||||
'title' => '',
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Categoria.
|
||||
if (!$view_display->getComponent('field_user_category')) {
|
||||
$view_display->setComponent('field_user_category', [
|
||||
'type' => 'string',
|
||||
'weight' => 12,
|
||||
'label' => 'above',
|
||||
'settings' => [
|
||||
'link_to_entity' => FALSE,
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Código do Departamento.
|
||||
if (!$view_display->getComponent('field_user_dept_code')) {
|
||||
$view_display->setComponent('field_user_dept_code', [
|
||||
'type' => 'string',
|
||||
'weight' => 13,
|
||||
'label' => 'above',
|
||||
'settings' => [
|
||||
'link_to_entity' => FALSE,
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Fotos.
|
||||
if (!$view_display->getComponent('field_user_photos')) {
|
||||
$view_display->setComponent('field_user_photos', [
|
||||
'type' => 'entity_reference_entity_view',
|
||||
'weight' => 14,
|
||||
'label' => 'above',
|
||||
'settings' => [
|
||||
'view_mode' => 'default',
|
||||
'link' => FALSE,
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Biografia.
|
||||
if (!$view_display->getComponent('field_user_bio')) {
|
||||
$view_display->setComponent('field_user_bio', [
|
||||
'type' => 'text_default',
|
||||
'weight' => 15,
|
||||
'label' => 'above',
|
||||
'settings' => [],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
// Campo Foto Padrão.
|
||||
if (!$view_display->getComponent('field_user_default_photo')) {
|
||||
$view_display->setComponent('field_user_default_photo', [
|
||||
'type' => 'entity_reference_entity_view',
|
||||
'weight' => 5,
|
||||
'label' => 'hidden',
|
||||
'settings' => [
|
||||
'view_mode' => 'default',
|
||||
'link' => FALSE,
|
||||
],
|
||||
'region' => 'content',
|
||||
]);
|
||||
}
|
||||
|
||||
$view_display->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adiciona o campo field_user_default_photo para seleção de foto padrão.
|
||||
*/
|
||||
function site_users_update_10001() {
|
||||
// Criar field storage se não existir.
|
||||
if (!FieldStorageConfig::loadByName('user', 'field_user_default_photo')) {
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => 'field_user_default_photo',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'entity_reference',
|
||||
'settings' => [
|
||||
'target_type' => 'media',
|
||||
],
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
])->save();
|
||||
}
|
||||
|
||||
// Criar field instance se não existir.
|
||||
if (!FieldConfig::loadByName('user', 'user', 'field_user_default_photo')) {
|
||||
FieldConfig::create([
|
||||
'field_name' => 'field_user_default_photo',
|
||||
'entity_type' => 'user',
|
||||
'bundle' => 'user',
|
||||
'label' => 'Foto Padrão',
|
||||
'description' => 'Selecione a foto principal do perfil.',
|
||||
'required' => FALSE,
|
||||
'settings' => [
|
||||
'handler' => 'default:media',
|
||||
'handler_settings' => [
|
||||
'target_bundles' => [
|
||||
'image' => 'image',
|
||||
],
|
||||
'sort' => [
|
||||
'field' => '_none',
|
||||
'direction' => 'ASC',
|
||||
],
|
||||
'auto_create' => FALSE,
|
||||
'auto_create_bundle' => '',
|
||||
],
|
||||
],
|
||||
])->save();
|
||||
}
|
||||
|
||||
// Configurar view display.
|
||||
$view_display = EntityViewDisplay::load('user.user.default');
|
||||
if ($view_display && !$view_display->getComponent('field_user_default_photo')) {
|
||||
$view_display->setComponent('field_user_default_photo', [
|
||||
'type' => 'entity_reference_entity_view',
|
||||
'weight' => 5,
|
||||
'label' => 'hidden',
|
||||
'settings' => [
|
||||
'view_mode' => 'default',
|
||||
'link' => FALSE,
|
||||
],
|
||||
'region' => 'content',
|
||||
])->save();
|
||||
}
|
||||
|
||||
// Ocultar do form display (será gerenciado via hook_form_alter).
|
||||
$form_display = EntityFormDisplay::load('user.user.default');
|
||||
if ($form_display) {
|
||||
$form_display->removeComponent('field_user_default_photo')->save();
|
||||
}
|
||||
|
||||
return t('Campo de foto padrão criado com sucesso.');
|
||||
}
|
||||
5
site_users.libraries.yml
Normal file
5
site_users.libraries.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
user-info-block:
|
||||
version: 1.x
|
||||
css:
|
||||
component:
|
||||
css/site-user-info-block.css: {}
|
||||
6
site_users.links.menu.yml
Normal file
6
site_users.links.menu.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
site_users.settings:
|
||||
title: 'Site Users'
|
||||
description: 'Configurações de campos e fotos de usuários.'
|
||||
route_name: site_users.settings
|
||||
parent: site_tools.admin_config
|
||||
weight: 10
|
||||
350
site_users.module
Normal file
350
site_users.module
Normal file
@@ -0,0 +1,350 @@
|
||||
<?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\media\MediaInterface;
|
||||
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();
|
||||
}
|
||||
|
||||
// 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('Foto padrão'),
|
||||
'#description' => t('Selecione a foto principal do perfil.'),
|
||||
'#options' => $photos,
|
||||
'#default_value' => $default_photo_id,
|
||||
];
|
||||
}
|
||||
else {
|
||||
$form['default_photo_selector']['field_user_default_photo_select'] = [
|
||||
'#type' => 'item',
|
||||
'#title' => t('Foto padrão'),
|
||||
'#markup' => t('Adicione fotos para selecionar uma como padrão.'),
|
||||
];
|
||||
}
|
||||
|
||||
// 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('Você pode adicionar no máximo @max fotos. Atualmente há @count fotos selecionadas.', [
|
||||
'@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;
|
||||
}
|
||||
30
site_users.permissions.yml
Normal file
30
site_users.permissions.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
administer site_users settings:
|
||||
title: 'Administrar configurações do Site Users'
|
||||
description: 'Permite administrar as configurações do módulo Site Users.'
|
||||
restrict access: true
|
||||
|
||||
view any user profile fields:
|
||||
title: 'Visualizar campos de perfil de qualquer usuário'
|
||||
description: 'Permite visualizar os campos customizados (nome, telefone, categoria, etc.) de qualquer usuário.'
|
||||
|
||||
view own user profile fields:
|
||||
title: 'Visualizar campos do próprio perfil'
|
||||
description: 'Permite visualizar os próprios campos customizados de perfil.'
|
||||
|
||||
edit any user profile fields:
|
||||
title: 'Editar campos de perfil de qualquer usuário'
|
||||
description: 'Permite editar os campos customizados de qualquer usuário.'
|
||||
restrict access: true
|
||||
|
||||
edit own user profile fields:
|
||||
title: 'Editar campos do próprio perfil'
|
||||
description: 'Permite editar os próprios campos customizados de perfil.'
|
||||
|
||||
manage user photos:
|
||||
title: 'Gerenciar fotos de usuários'
|
||||
description: 'Permite adicionar, editar e remover fotos de qualquer usuário.'
|
||||
restrict access: true
|
||||
|
||||
manage own user photos:
|
||||
title: 'Gerenciar próprias fotos'
|
||||
description: 'Permite adicionar, editar e remover as próprias fotos de perfil.'
|
||||
7
site_users.routing.yml
Normal file
7
site_users.routing.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
site_users.settings:
|
||||
path: '/admin/config/local-modules/site-users'
|
||||
defaults:
|
||||
_form: '\Drupal\site_users\Form\SiteUsersSettingsForm'
|
||||
_title: 'Configurações do Site Users'
|
||||
requirements:
|
||||
_permission: 'administer site_users settings'
|
||||
73
src/Form/SiteUsersSettingsForm.php
Normal file
73
src/Form/SiteUsersSettingsForm.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\site_users\Form;
|
||||
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Formulário de configuração do módulo Site Users.
|
||||
*/
|
||||
class SiteUsersSettingsForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'site_users_settings_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['site_users.settings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('site_users.settings');
|
||||
|
||||
// Fieldset para configurações de fotos.
|
||||
$form['photos'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Configurações de Fotos'),
|
||||
'#collapsible' => FALSE,
|
||||
];
|
||||
|
||||
$form['photos']['photos_max_count'] = [
|
||||
'#type' => 'number',
|
||||
'#title' => $this->t('Quantidade de fotos permitidas'),
|
||||
'#description' => $this->t('Número máximo de fotos que um usuário pode adicionar ao perfil.'),
|
||||
'#default_value' => $config->get('photos.max_count') ?? 5,
|
||||
'#min' => 1,
|
||||
'#max' => 100,
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
$form['photos']['photos_ldap_attribute'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Atributo LDAP da foto'),
|
||||
'#description' => $this->t('Se LDAP estiver habilitado, informe o nome do atributo que contém a foto do usuário (ex: thumbnailPhoto, jpegPhoto).'),
|
||||
'#default_value' => $config->get('photos.ldap_attribute') ?? 'jpegPhoto',
|
||||
'#maxlength' => 255,
|
||||
];
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->config('site_users.settings')
|
||||
->set('photos.max_count', $form_state->getValue('photos_max_count'))
|
||||
->set('photos.ldap_attribute', $form_state->getValue('photos_ldap_attribute'))
|
||||
->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
192
src/Plugin/Block/UserInfoBlock.php
Normal file
192
src/Plugin/Block/UserInfoBlock.php
Normal 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']);
|
||||
}
|
||||
|
||||
}
|
||||
64
templates/site-user-info-block.html.twig
Normal file
64
templates/site-user-info-block.html.twig
Normal file
@@ -0,0 +1,64 @@
|
||||
{#
|
||||
/**
|
||||
* @file
|
||||
* Template para o bloco de informações do usuário.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#}
|
||||
<div class="site-user-info-block">
|
||||
<div class="site-user-info-block__photo">
|
||||
{% if user_info.photo_url %}
|
||||
<img src="{{ user_info.photo_url }}" alt="{{ user_info.photo_alt }}" class="site-user-info-block__image" />
|
||||
{% else %}
|
||||
<div class="site-user-info-block__no-photo">
|
||||
<span class="site-user-info-block__initials">
|
||||
{{ user_info.name ? user_info.name|first|upper : user_info.username|first|upper }}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="site-user-info-block__details">
|
||||
<h2 class="site-user-info-block__name">
|
||||
{{ 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>
|
||||
<a href="tel:{{ user_info.phone }}">{{ user_info.phone }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if user_info.bio %}
|
||||
<div class="site-user-info-block__bio">
|
||||
{{ user_info.bio }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
18
templates/user--full.html.twig
Normal file
18
templates/user--full.html.twig
Normal file
@@ -0,0 +1,18 @@
|
||||
{#
|
||||
/**
|
||||
* @file
|
||||
* Template para página de perfil de usuário (view mode: full).
|
||||
*
|
||||
* Variáveis disponíveis:
|
||||
* - content: Campos do usuário renderizados.
|
||||
* - user: Objeto do usuário.
|
||||
* - attributes: Atributos HTML do elemento wrapper.
|
||||
*
|
||||
* @see template_preprocess_user()
|
||||
*/
|
||||
#}
|
||||
<article{{ attributes.addClass('user-profile') }}>
|
||||
{% if content %}
|
||||
{{ content }}
|
||||
{% endif %}
|
||||
</article>
|
||||
Reference in New Issue
Block a user