feat: Descoberta dinâmica de campos de usuário editáveis

hook_entity_field_access() passa a cobrir todos os FieldConfigInterface
do usuário, sem lista hardcoded. Campos de referência a mídia usam
lógica de fotos; demais usam lógica de perfil.

SiteUsersSettingsForm descobre campos dinamicamente via
entity_field.manager, listando todos os campos configuráveis do usuário
independente do módulo que os define.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 07:43:41 -03:00
parent 06e5bae039
commit 120d7b1858
3 changed files with 72 additions and 32 deletions

View File

@@ -12,6 +12,7 @@ use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\field\FieldConfigInterface;
use Drupal\media\MediaInterface;
use Drupal\user\UserInterface;
@@ -51,31 +52,20 @@ function site_users_entity_field_access($operation, FieldDefinitionInterface $fi
return AccessResult::neutral();
}
// Lista de campos controlados pelo módulo.
$profile_fields = [
'field_user_name',
'field_user_phone',
'field_user_social_links',
'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, $field_name);
// Apenas campos configuráveis (não campos base como name, mail, status).
if (!($field_definition instanceof FieldConfigInterface)) {
return AccessResult::neutral();
}
// Verificar se é um campo de fotos.
if (in_array($field_name, $photo_fields)) {
$field_name = $field_definition->getName();
// Campos de referência a mídia usam lógica de fotos.
if ($field_definition->getType() === 'entity_reference'
&& $field_definition->getSetting('target_type') === 'media') {
return site_users_check_photo_field_access($operation, $account, $items, $field_name);
}
return AccessResult::neutral();
return site_users_check_profile_field_access($operation, $account, $items, $field_name);
}
/**

View File

@@ -4,6 +4,7 @@ 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.
@@ -11,19 +12,51 @@ use Drupal\Core\Form\FormStateInterface;
class SiteUsersSettingsForm extends ConfigFormBase {
/**
* Returns the list of fields controllable by the admin.
* Returns all configurable user fields, discovered dynamically.
*
* @return array
* Associative array of field_name => label.
*/
protected function getEditableFields(): array {
return [
'field_user_name' => $this->t('Full Name'),
'field_user_phone' => $this->t('Phone'),
'field_user_bio' => $this->t('Biography'),
'field_user_social_links' => $this->t('Social Links'),
'field_user_photos' => $this->t('Photos'),
];
$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]', [
'@label' => $definition->getLabel(),
'@module' => $provider,
]);
}
}
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();
foreach ($module_handler->getModuleList() as $module_name => $module) {
foreach (['config/install', 'config/optional'] as $dir) {
if (file_exists($module->getPath() . '/' . $dir . '/' . $config_file)) {
return $module_name;
}
}
}
return 'unknown';
}
/**
@@ -75,15 +108,26 @@ class SiteUsersSettingsForm extends ConfigFormBase {
$form['user_editable_fields'] = [
'#type' => 'fieldset',
'#title' => $this->t('User-editable profile fields'),
'#description' => $this->t('Select which fields users with the "Edit own user profile fields" or "Manage own user photos" permission can edit on their own profile.'),
'#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.') : '',
];
}
@@ -102,7 +146,10 @@ class SiteUsersSettingsForm extends ConfigFormBase {
$editable = $form_state->getValue('user_editable_fields');
foreach (array_keys($this->getEditableFields()) as $field_name) {
$config->set('user_editable_fields.' . $field_name, (bool) ($editable[$field_name] ?? FALSE));
// Disabled fields are not submitted; skip them to preserve current value.
if (isset($editable[$field_name])) {
$config->set('user_editable_fields.' . $field_name, (bool) $editable[$field_name]);
}
}
$config->save();

View File

@@ -132,8 +132,11 @@ msgstr "Links de perfil em redes sociais."
msgid "User-editable profile fields"
msgstr "Campos do perfil editáveis pelo usuário"
msgid "Select which fields users with the \"Edit own user profile fields\" or \"Manage own user photos\" permission can edit on their own profile."
msgstr "Selecione quais campos os usuários com a permissão \"Editar campos do próprio perfil\" ou \"Gerenciar próprias fotos\" podem editar no próprio perfil."
msgid "Select which fields users can edit on their own profile. Fields protected by other modules remain non-editable regardless of this setting."
msgstr "Selecione quais campos os usuários podem editar no próprio perfil. Campos protegidos por outros módulos permanecem não editáveis independentemente desta configuração."
msgid "Protected by another module."
msgstr "Protegido por outro módulo."
# Template
msgid "Phone:"