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' => '
' . $this->t('No custom view modes found for user entities. Create view modes at Manage display.', [ ':url' => '/admin/config/people/accounts/display', ]) . '
', ]; } 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); } }