From 120d7b18585a020f1f3992c7a558779cef470c44 Mon Sep 17 00:00:00 2001 From: "Quintino A. G. Souza" Date: Tue, 24 Feb 2026 07:43:41 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20Descoberta=20din=C3=A2mica=20de=20campo?= =?UTF-8?q?s=20de=20usu=C3=A1rio=20edit=C3=A1veis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- site_users.module | 30 +++++-------- src/Form/SiteUsersSettingsForm.php | 67 +++++++++++++++++++++++++----- translations/site_users.pt-br.po | 7 +++- 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/site_users.module b/site_users.module index 82b7726..6c0fdc9 100644 --- a/site_users.module +++ b/site_users.module @@ -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); } /** diff --git a/src/Form/SiteUsersSettingsForm.php b/src/Form/SiteUsersSettingsForm.php index bd9c40c..fc17e35 100644 --- a/src/Form/SiteUsersSettingsForm.php +++ b/src/Form/SiteUsersSettingsForm.php @@ -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(); diff --git a/translations/site_users.pt-br.po b/translations/site_users.pt-br.po index 401e846..733486c 100644 --- a/translations/site_users.pt-br.po +++ b/translations/site_users.pt-br.po @@ -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:"