mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/site_users.git
synced 2026-05-06 03:05:29 -03:00
Fotos LDAP:
- Ignora sync quando conta ainda não tem UID (evitava URI compartilhada)
- Filtra fotos abaixo do tamanho mínimo configurável (padrão 10 KB)
- Adiciona campo ldap_min_photo_size nas configurações e schema
- Update 10010: remove fotos placeholder já existentes
- Update 10011: remove mídias com URI ldap_photo_.{ext} sem UID
Bloco de cabeçalho do microsite:
- Exibe departamento abaixo do nome, sem label, com link para a entidade
- Exibe telefone de trabalho (work_phone) no lugar de phone (restrito)
Página de perfil:
- Título fixo "Perfil de @name" via callback profileTitle()
- Exclui rota profile da substituição de título pelo nó homepage
Subpáginas com URL amigável:
- Adiciona MicrositeSubpagePathProcessor (inbound + outbound)
- Inbound: /user/{username}/{subpage} → /user/{uid}/{subpage}
- Outbound: /user/{uid}/{subpage} → /user/{username}/{subpage}
- Busca alias em todos os idiomas para contornar limitação do AliasManager
Tema do microsite em rotas externas:
- MicrositeThemeNegotiator cobre rotas com parâmetro user sob /user/{user}/
- Cobre nós do structural_pages cujo alias começa com /user/{uid}/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
351 lines
12 KiB
PHP
351 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Drupal\site_users\Form;
|
|
|
|
use Drupal\Core\Form\ConfigFormBase;
|
|
use Drupal\Core\Form\FormStateInterface;
|
|
use Drupal\field\FieldConfigInterface;
|
|
|
|
/**
|
|
* Formulário de configuração do módulo Site Users.
|
|
*/
|
|
class SiteUsersSettingsForm extends ConfigFormBase {
|
|
|
|
/**
|
|
* Returns all configurable user fields, discovered dynamically.
|
|
*
|
|
* @return array
|
|
* Associative array of field_name => label.
|
|
*/
|
|
protected function getEditableFields(): array {
|
|
$definitions = \Drupal::service('entity_field.manager')
|
|
->getFieldDefinitions('user', 'user');
|
|
|
|
$fields = [];
|
|
foreach ($definitions as $field_name => $definition) {
|
|
if ($definition instanceof FieldConfigInterface) {
|
|
$provider = $this->getFieldStorageProvider($field_name);
|
|
$fields[$field_name] = $this->t('@label [@module:@field]', [
|
|
'@label' => $definition->getLabel(),
|
|
'@module' => $provider,
|
|
'@field' => $field_name,
|
|
]);
|
|
}
|
|
}
|
|
ksort($fields);
|
|
return $fields;
|
|
}
|
|
|
|
/**
|
|
* Finds which module provides the field storage config file.
|
|
*
|
|
* @param string $field_name
|
|
* The field name.
|
|
*
|
|
* @return string
|
|
* The module name, or 'unknown' if not found.
|
|
*/
|
|
protected function getFieldStorageProvider(string $field_name): string {
|
|
$config_file = 'field.storage.user.' . $field_name . '.yml';
|
|
$module_handler = \Drupal::moduleHandler();
|
|
$root = \Drupal::root();
|
|
|
|
foreach ($module_handler->getModuleList() as $module_name => $module) {
|
|
foreach (['config/install', 'config/optional'] as $dir) {
|
|
if (file_exists($root . '/' . $module->getPath() . '/' . $dir . '/' . $config_file)) {
|
|
return $module_name;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 'unknown';
|
|
}
|
|
|
|
/**
|
|
* {@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('Photo settings'),
|
|
'#collapsible' => FALSE,
|
|
];
|
|
|
|
$form['photos']['photos_max_count'] = [
|
|
'#type' => 'number',
|
|
'#title' => $this->t('Number of photos allowed'),
|
|
'#description' => $this->t('Maximum number of photos a user can add to the profile.'),
|
|
'#default_value' => $config->get('photos.max_count') ?? 5,
|
|
'#min' => 1,
|
|
'#max' => 100,
|
|
'#required' => TRUE,
|
|
];
|
|
|
|
$form['photos']['photos_ldap_sync_enabled'] = [
|
|
'#type' => 'checkbox',
|
|
'#title' => $this->t('Enable LDAP photo synchronization'),
|
|
'#description' => $this->t('When enabled, the user photo from LDAP is automatically added as the first profile photo on each login.'),
|
|
'#default_value' => $config->get('photos.ldap_sync_enabled') ?? FALSE,
|
|
];
|
|
|
|
$form['photos']['photos_ldap_attribute'] = [
|
|
'#type' => 'textfield',
|
|
'#title' => $this->t('LDAP photo attribute'),
|
|
'#description' => $this->t('Name of the LDAP attribute that contains the user photo (e.g., thumbnailPhoto, jpegPhoto).'),
|
|
'#default_value' => $config->get('photos.ldap_attribute') ?? 'jpegPhoto',
|
|
'#maxlength' => 255,
|
|
'#states' => [
|
|
'visible' => [
|
|
':input[name="photos_ldap_sync_enabled"]' => ['checked' => TRUE],
|
|
],
|
|
],
|
|
];
|
|
|
|
$form['photos']['photos_ldap_min_photo_size'] = [
|
|
'#type' => 'number',
|
|
'#title' => $this->t('Minimum LDAP photo size (bytes)'),
|
|
'#description' => $this->t('Photos smaller than this size are ignored during LDAP sync, avoiding placeholder images. Default: 10240 (10 KB).'),
|
|
'#default_value' => $config->get('photos.ldap_min_photo_size') ?? 10240,
|
|
'#min' => 0,
|
|
'#required' => TRUE,
|
|
'#states' => [
|
|
'visible' => [
|
|
':input[name="photos_ldap_sync_enabled"]' => ['checked' => TRUE],
|
|
],
|
|
],
|
|
];
|
|
|
|
// Fieldset para campos editáveis pelo próprio usuário.
|
|
$form['user_editable_fields'] = [
|
|
'#type' => 'fieldset',
|
|
'#title' => $this->t('User-editable profile fields'),
|
|
'#description' => $this->t('Select which fields users can edit on their own profile. Fields protected by other modules remain non-editable regardless of this setting.'),
|
|
'#tree' => TRUE,
|
|
];
|
|
|
|
$definitions = \Drupal::service('entity_field.manager')
|
|
->getFieldDefinitions('user', 'user');
|
|
$access_handler = \Drupal::entityTypeManager()
|
|
->getAccessControlHandler('user');
|
|
|
|
foreach ($this->getEditableFields() as $field_name => $label) {
|
|
$is_protected = $access_handler
|
|
->fieldAccess('edit', $definitions[$field_name], NULL, NULL, TRUE)
|
|
->isForbidden();
|
|
|
|
$form['user_editable_fields'][$field_name] = [
|
|
'#type' => 'checkbox',
|
|
'#title' => $label,
|
|
'#default_value' => $config->get('user_editable_fields.' . $field_name) ?? TRUE,
|
|
'#disabled' => $is_protected,
|
|
'#description' => $is_protected ? $this->t('Protected by another module.') : '',
|
|
];
|
|
}
|
|
|
|
// Fieldset para view modes por role.
|
|
$form['role_view_modes'] = [
|
|
'#type' => 'fieldset',
|
|
'#title' => $this->t('Profile visibility options by role'),
|
|
'#description' => $this->t('Select which view modes each role can choose for their public profile. The "restricted" option is always available to all users.'),
|
|
'#tree' => TRUE,
|
|
];
|
|
|
|
$available_view_modes = $this->getCustomUserViewModes();
|
|
|
|
if (empty($available_view_modes)) {
|
|
$form['role_view_modes']['_empty'] = [
|
|
'#markup' => '<p>' . $this->t('No custom view modes found for user entities. Create view modes at <a href=":url">Manage display</a>.', [
|
|
':url' => '/admin/config/people/accounts/display',
|
|
]) . '</p>',
|
|
];
|
|
}
|
|
else {
|
|
$roles = \Drupal\user\Entity\Role::loadMultiple();
|
|
foreach ($roles as $role_id => $role) {
|
|
if (in_array($role_id, ['anonymous', 'authenticated'])) {
|
|
continue;
|
|
}
|
|
$form['role_view_modes'][$role_id] = [
|
|
'#type' => 'checkboxes',
|
|
'#title' => $role->label(),
|
|
'#options' => $available_view_modes,
|
|
'#default_value' => $config->get('role_view_modes.' . $role_id) ?? [],
|
|
];
|
|
}
|
|
}
|
|
|
|
// Fieldset para itens do menu "Adicionar".
|
|
$form['add_content_links'] = [
|
|
'#type' => 'fieldset',
|
|
'#title' => $this->t('Add content menu items'),
|
|
'#description' => $this->t('Configure items shown under the "Adicionar" entry in the account menu. Each item points to an entity add route. Leave "Label" empty to remove the row.'),
|
|
'#tree' => TRUE,
|
|
];
|
|
|
|
$saved_items = $config->get('add_content_links') ?? [];
|
|
// Append one blank row for adding a new item.
|
|
$saved_items[] = ['label' => '', 'route_name' => '', 'route_parameters' => [], 'weight' => 0];
|
|
|
|
$form['add_content_links']['table'] = [
|
|
'#type' => 'table',
|
|
'#header' => [
|
|
$this->t('Label'),
|
|
$this->t('Route name'),
|
|
$this->t('Parameter name'),
|
|
$this->t('Parameter value'),
|
|
$this->t('Weight'),
|
|
],
|
|
'#empty' => $this->t('No items yet. Fill in the row below to add one.'),
|
|
];
|
|
|
|
foreach ($saved_items as $delta => $item) {
|
|
$params = $item['route_parameters'] ?? [];
|
|
$param_name = array_key_first($params) ?? '';
|
|
$param_value = $params[$param_name] ?? '';
|
|
|
|
$form['add_content_links']['table'][$delta]['label'] = [
|
|
'#type' => 'textfield',
|
|
'#default_value' => $item['label'] ?? '',
|
|
'#size' => 20,
|
|
'#placeholder' => $this->t('e.g. Artigo'),
|
|
];
|
|
$form['add_content_links']['table'][$delta]['route_name'] = [
|
|
'#type' => 'textfield',
|
|
'#default_value' => $item['route_name'] ?? '',
|
|
'#size' => 30,
|
|
'#placeholder' => 'e.g. node.add',
|
|
];
|
|
$form['add_content_links']['table'][$delta]['param_name'] = [
|
|
'#type' => 'textfield',
|
|
'#default_value' => $param_name,
|
|
'#size' => 20,
|
|
'#placeholder' => 'e.g. node_type',
|
|
];
|
|
$form['add_content_links']['table'][$delta]['param_value'] = [
|
|
'#type' => 'textfield',
|
|
'#default_value' => $param_value,
|
|
'#size' => 20,
|
|
'#placeholder' => 'e.g. article',
|
|
];
|
|
$form['add_content_links']['table'][$delta]['weight'] = [
|
|
'#type' => 'number',
|
|
'#default_value' => (int) ($item['weight'] ?? 0),
|
|
'#size' => 4,
|
|
];
|
|
}
|
|
|
|
return parent::buildForm($form, $form_state);
|
|
}
|
|
|
|
/**
|
|
* Returns custom user view modes (excluding 'default' and 'restricted').
|
|
*
|
|
* @return array
|
|
* Associative array of machine_name => label.
|
|
*/
|
|
protected function getCustomUserViewModes(): array {
|
|
$view_mode_storage = \Drupal::entityTypeManager()->getStorage('entity_view_mode');
|
|
$view_modes = $view_mode_storage->loadByProperties(['targetEntityType' => 'user']);
|
|
$options = [];
|
|
foreach ($view_modes as $view_mode) {
|
|
$id_parts = explode('.', $view_mode->id());
|
|
$machine_name = end($id_parts);
|
|
if ($machine_name === 'default' || $machine_name === 'restricted') {
|
|
continue;
|
|
}
|
|
$options[$machine_name] = $view_mode->label();
|
|
}
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
public function submitForm(array &$form, FormStateInterface $form_state) {
|
|
$config = $this->config('site_users.settings');
|
|
|
|
$config
|
|
->set('photos.max_count', $form_state->getValue('photos_max_count'))
|
|
->set('photos.ldap_sync_enabled', (bool) $form_state->getValue('photos_ldap_sync_enabled'))
|
|
->set('photos.ldap_attribute', $form_state->getValue('photos_ldap_attribute'))
|
|
->set('photos.ldap_min_photo_size', (int) $form_state->getValue('photos_ldap_min_photo_size'));
|
|
|
|
$definitions = \Drupal::service('entity_field.manager')
|
|
->getFieldDefinitions('user', 'user');
|
|
$access_handler = \Drupal::entityTypeManager()
|
|
->getAccessControlHandler('user');
|
|
$editable = $form_state->getValue('user_editable_fields');
|
|
foreach (array_keys($this->getEditableFields()) as $field_name) {
|
|
$is_protected = $access_handler
|
|
->fieldAccess('edit', $definitions[$field_name], NULL, NULL, TRUE)
|
|
->isForbidden();
|
|
if ($is_protected) {
|
|
// Campos protegidos por outros módulos são sempre não-editáveis.
|
|
$config->set('user_editable_fields.' . $field_name, FALSE);
|
|
}
|
|
elseif (isset($editable[$field_name])) {
|
|
$config->set('user_editable_fields.' . $field_name, (bool) $editable[$field_name]);
|
|
}
|
|
}
|
|
|
|
// Salvar add_content_links: ignorar linhas sem label ou route_name.
|
|
$links_raw = $form_state->getValue(['add_content_links', 'table']) ?? [];
|
|
$add_content_links = [];
|
|
foreach ($links_raw as $row) {
|
|
$label = trim($row['label'] ?? '');
|
|
$route_name = trim($row['route_name'] ?? '');
|
|
if ($label === '' || $route_name === '') {
|
|
continue;
|
|
}
|
|
$params = [];
|
|
$param_name = trim($row['param_name'] ?? '');
|
|
$param_value = trim($row['param_value'] ?? '');
|
|
if ($param_name !== '') {
|
|
$params[$param_name] = $param_value;
|
|
}
|
|
$add_content_links[] = [
|
|
'label' => $label,
|
|
'route_name' => $route_name,
|
|
'route_parameters' => $params,
|
|
'weight' => (int) ($row['weight'] ?? 0),
|
|
];
|
|
}
|
|
// Reordena por weight.
|
|
usort($add_content_links, fn($a, $b) => $a['weight'] <=> $b['weight']);
|
|
$config->set('add_content_links', $add_content_links);
|
|
|
|
// Salvar role_view_modes: apenas os valores marcados (filtrar 0).
|
|
$role_view_modes_raw = $form_state->getValue('role_view_modes') ?? [];
|
|
$roles = \Drupal\user\Entity\Role::loadMultiple();
|
|
foreach ($roles as $role_id => $role) {
|
|
if ($role_id === 'anonymous' || !isset($role_view_modes_raw[$role_id])) {
|
|
continue;
|
|
}
|
|
$selected = array_values(array_filter($role_view_modes_raw[$role_id]));
|
|
$config->set('role_view_modes.' . $role_id, $selected);
|
|
}
|
|
|
|
$config->save();
|
|
|
|
parent::submitForm($form, $form_state);
|
|
}
|
|
|
|
}
|