addStatus(t('Structural Pages module installed successfully. Configure Pathauto patterns at /admin/config/search/path/patterns')); } /** * Creates default terms for the configured site section vocabulary. */ function _structural_pages_create_default_terms(): void { $vocabulary = _structural_pages_get_vocabulary(); // Check if terms already exist (avoid duplication on reinstall). $existing = \Drupal::entityQuery('taxonomy_term') ->accessCheck(FALSE) ->condition('vid', $vocabulary) ->count() ->execute(); if ($existing > 0) { return; } // Structure: name => children. $terms_structure = [ 'Notícias' => [], 'Eventos' => [], 'Pessoas' => [], 'Institucional' => [ 'Sobre', 'Comunicação', 'Informações e Serviços', 'Equipe', 'Gestão', 'Inclusão e Pertencimento', ], 'Graduação' => [ 'Estatística', 'Matemática', 'Matemática Aplicada', 'Licenciatura em Matemática', ], 'Pós-Graduação' => [ 'Programa de Estatística', 'Programa de Matemática', 'Programa de Matemática Aplicada', ], 'Pesquisa' => [], 'Extensão' => [], 'Administração' => [], 'Departamentos' => [ 'Departamento de Estatística', 'Departamento de Matemática', 'Departamento de Matemática Aplicada', ], 'Biblioteca' => [], 'Informática' => [], ]; $created_terms = []; $weight = 0; foreach ($terms_structure as $parent_name => $children) { // Create parent term. $parent_term = Term::create([ 'vid' => $vocabulary, 'name' => $parent_name, 'weight' => $weight++, ]); $parent_term->save(); $created_terms[] = $parent_term; // Create child terms. $child_weight = 0; foreach ($children as $child_name) { $child_term = Term::create([ 'vid' => $vocabulary, 'name' => $child_name, 'parent' => $parent_term->id(), 'weight' => $child_weight++, ]); $child_term->save(); $created_terms[] = $child_term; } } // Add content translations if available. _structural_pages_add_term_translations($created_terms); } /** * Adds content translations to taxonomy terms. * * @param \Drupal\taxonomy\Entity\Term[] $terms * The terms to translate. */ function _structural_pages_add_term_translations(array $terms): void { if (!\Drupal::moduleHandler()->moduleExists('content_translation')) { return; } $translations = _structural_pages_term_translations(); $language_manager = \Drupal::languageManager(); foreach ($translations as $langcode => $name_map) { if (!$language_manager->getLanguage($langcode)) { continue; } foreach ($terms as $term) { $name = $term->getName(); if (!isset($name_map[$name])) { continue; } $translated_name = $name_map[$name]; if ($term->hasTranslation($langcode)) { // Update existing translation if name doesn't match. $translation = $term->getTranslation($langcode); if ($translation->getName() !== $translated_name) { $translation->setName($translated_name); $translation->save(); } } else { $term->addTranslation($langcode, ['name' => $translated_name]); $term->save(); } } } } /** * Returns term name translations keyed by langcode. * * @return array * Nested array: langcode => [english_name => translated_name]. */ function _structural_pages_term_translations(): array { return [ 'pt-br' => [ 'News' => 'Notícias', 'Events' => 'Eventos', 'People' => 'Pessoas', 'Institutional' => 'Institucional', 'About' => 'Sobre', 'Communication' => 'Comunicação', 'Information and Services' => 'Informações e Serviços', 'Team' => 'Equipe', 'Management' => 'Gestão', 'Inclusion and Belonging' => 'Inclusão e Pertencimento', 'Undergraduate' => 'Graduação', 'Statistics' => 'Estatística', 'Mathematics' => 'Matemática', 'Applied Mathematics' => 'Matemática Aplicada', 'Mathematics Teaching' => 'Licenciatura em Matemática', 'Graduate' => 'Pós-Graduação', 'Statistics Program' => 'Programa de Estatística', 'Mathematics Program' => 'Programa de Matemática', 'Applied Mathematics Program' => 'Programa de Matemática Aplicada', 'Research' => 'Pesquisa', 'Extension' => 'Extensão', 'Administration' => 'Administração', 'Departments' => 'Departamentos', 'Statistics Department' => 'Departamento de Estatística', 'Mathematics Department' => 'Departamento de Matemática', 'Applied Mathematics Department' => 'Departamento de Matemática Aplicada', 'Library' => 'Biblioteca', 'IT Services' => 'Informática', ], ]; } /** * Implements hook_uninstall(). */ function structural_pages_uninstall(): void { $entity_type_manager = \Drupal::entityTypeManager(); // Remove nodes from module's content types. foreach (['section_page', 'content_page'] as $bundle) { $nids = \Drupal::entityQuery('node') ->accessCheck(FALSE) ->condition('type', $bundle) ->execute(); if ($nids) { $nodes = $entity_type_manager->getStorage('node')->loadMultiple($nids); $entity_type_manager->getStorage('node')->delete($nodes); \Drupal::messenger()->addWarning(t('Deleted @count @bundle nodes.', [ '@count' => count($nids), '@bundle' => $bundle, ])); } } // Remove terms from the configured vocabulary. $vocabulary = _structural_pages_get_vocabulary(); $tids = \Drupal::entityQuery('taxonomy_term') ->accessCheck(FALSE) ->condition('vid', $vocabulary) ->execute(); if ($tids) { $terms = $entity_type_manager->getStorage('taxonomy_term')->loadMultiple($tids); $entity_type_manager->getStorage('taxonomy_term')->delete($terms); } // Remove configurations in correct order (dependencies first). $configs_to_delete = [ // Views. 'views.view.child_pages', // Pathauto patterns. 'pathauto.pattern.section_page', 'pathauto.pattern.content_page', 'pathauto.pattern.' . $vocabulary . '_term', // Entity displays. 'core.entity_form_display.node.section_page.default', 'core.entity_view_display.node.section_page.default', 'core.entity_form_display.node.content_page.default', 'core.entity_view_display.node.content_page.default', // Field instances. 'field.field.node.section_page.field_site_section', 'field.field.node.section_page.body', 'field.field.node.content_page.field_parent_page', 'field.field.node.content_page.field_site_section', 'field.field.node.content_page.body', // Field storages (only if not used by other bundles). 'field.storage.node.field_site_section', 'field.storage.node.field_parent_page', // Node types. 'node.type.section_page', 'node.type.content_page', // Vocabulary. 'taxonomy.vocabulary.' . $vocabulary, ]; $config_factory = \Drupal::configFactory(); foreach ($configs_to_delete as $config_name) { $config = $config_factory->getEditable($config_name); if (!$config->isNew()) { $config->delete(); } } \Drupal::messenger()->addStatus(t('Structural Pages module uninstalled successfully.')); } /** * Update content_page content type name from "Guide Page" to "Content Page". */ function structural_pages_update_10001(): void { $config = \Drupal::configFactory()->getEditable('node.type.content_page'); if (!$config->isNew()) { $current_name = $config->get('name'); // Update if still using old name. if ($current_name === 'Guide Page') { $config->set('name', 'Content Page'); $config->set('description', 'Pages with hierarchical parent-child structure for content organization.'); $config->save(); \Drupal::messenger()->addStatus(t('Updated content_page content type name to "Content Page".')); } } } /** * Add content translations to site_sections taxonomy terms (no-op, see 10003). */ function structural_pages_update_10002(): void { } /** * Add content translations to site_sections taxonomy terms (no-op, see 10004). */ function structural_pages_update_10003(): void { } /** * Fix content translation config and update term translations. */ function structural_pages_update_10004(): void { // Ensure content translation is properly configured for the vocabulary. _structural_pages_ensure_content_translation(); $vocabulary = _structural_pages_get_vocabulary(); $terms = \Drupal::entityTypeManager() ->getStorage('taxonomy_term') ->loadByProperties(['vid' => $vocabulary]); if ($terms) { _structural_pages_add_term_translations($terms); } } /** * Ensures content translation settings exist for the configured vocabulary. */ function _structural_pages_ensure_content_translation(): void { if (!\Drupal::moduleHandler()->moduleExists('content_translation')) { return; } $vocabulary = _structural_pages_get_vocabulary(); $config = \Drupal\language\Entity\ContentLanguageSettings::loadByEntityTypeBundle('taxonomy_term', $vocabulary); $config->setDefaultLangcode('site_default'); $config->setLanguageAlterable(TRUE); $config->setThirdPartySetting('content_translation', 'enabled', TRUE); $config->save(); } /** * Corrige handler_settings nulo no field_parent_page (compatibilidade PHP 8). * * O widget DynamicEntityReferenceWidget faz $settings[$type]['handler_settings'] * + array, que lança TypeError quando handler_settings é null no PHP 8. * Re-aplica as settings completas a partir do YAML de install do módulo. */ function structural_pages_update_10006(): string { $config = \Drupal::configFactory() ->getEditable('field.field.node.content_page.field_parent_page'); if ($config->isNew()) { return 'Campo field_parent_page não encontrado — nada a corrigir.'; } // Re-aplica as settings completas a partir do YAML de install. $module_path = \Drupal::service('extension.list.module')->getPath('structural_pages'); $yaml_file = $module_path . '/config/install/field.field.node.content_page.field_parent_page.yml'; if (!file_exists($yaml_file)) { return 'Arquivo YAML de install não encontrado.'; } $yaml_settings = \Symfony\Component\Yaml\Yaml::parse(file_get_contents($yaml_file)); $config->set('settings', $yaml_settings['settings']); $config->save(TRUE); return 'Settings de field_parent_page re-sincronizadas a partir do YAML de install.'; } /** * Corrige exclude_entity_types no storage do field_parent_page (PHP 8 TypeError). * * defaultStorageSettings() retorna exclude_entity_types: TRUE, o que fazia * getTargetTypes() retornar todos os entity types EXCETO node/taxonomy_term. */ function structural_pages_update_10007(): string { $config = \Drupal::configFactory() ->getEditable('field.storage.node.field_parent_page'); if ($config->isNew()) { return 'field.storage.node.field_parent_page não encontrado.'; } $config->set('settings.exclude_entity_types', FALSE)->save(TRUE); return 'exclude_entity_types corrigido para FALSE em field_parent_page storage.'; } /** * Adiciona coluna field_parent_page_target_type ausente no schema do banco. * * O campo foi originalmente criado como entity_reference simples e depois * migrado para dynamic_entity_reference no config, mas a coluna target_type * nunca foi adicionada ao banco. Popula o tipo de cada linha consultando as * tabelas node e taxonomy_term para determinar o tipo correto da referência. */ function structural_pages_update_10008(): string { $database = \Drupal::database(); $schema = $database->schema(); $column_spec = [ 'type' => 'varchar_ascii', 'length' => 128, 'not null' => FALSE, 'description' => 'The entity type of the field_parent_page reference.', ]; $tables = [ 'node__field_parent_page', 'node_revision__field_parent_page', ]; $updated = []; foreach ($tables as $table) { if (!$schema->tableExists($table)) { continue; } if (!$schema->fieldExists($table, 'field_parent_page_target_type')) { $schema->addField($table, 'field_parent_page_target_type', $column_spec); } // Determina o tipo de cada referência existente comparando o target_id // com os IDs existentes em node e taxonomy_term. $rows = $database->select($table, 't') ->fields('t', ['entity_id', 'revision_id', 'field_parent_page_target_id']) ->isNull('field_parent_page_target_type') ->execute() ->fetchAll(); foreach ($rows as $row) { $target_id = $row->field_parent_page_target_id; $is_node = (bool) $database->select('node', 'n') ->fields('n', ['nid']) ->condition('n.nid', $target_id) ->countQuery() ->execute() ->fetchField(); $target_type = $is_node ? 'node' : 'taxonomy_term'; $database->update($table) ->fields(['field_parent_page_target_type' => $target_type]) ->condition('entity_id', $row->entity_id) ->condition('revision_id', $row->revision_id) ->execute(); } $updated[] = $table; } if (empty($updated)) { return 'Tabelas não encontradas — nada a corrigir.'; } return 'Coluna field_parent_page_target_type adicionada e populada em: ' . implode(', ', $updated) . '.'; } /** * Define entity_type_ids no storage do field_parent_page para node e taxonomy_term. * * Com entity_type_ids vazio e exclude_entity_types FALSE, o widget DER não * conseguia determinar quais entity types exibir e deixava o campo em branco. */ function structural_pages_update_10009(): string { $config = \Drupal::configFactory() ->getEditable('field.storage.node.field_parent_page'); if ($config->isNew()) { return 'field.storage.node.field_parent_page não encontrado.'; } $config->set('settings.entity_type_ids', ['node', 'taxonomy_term'])->save(TRUE); return 'entity_type_ids definido como [node, taxonomy_term] em field_parent_page storage.'; } /** * Adiciona suporte ao entity type user no campo field_parent_page. * * Atualiza o storage (entity_type_ids) e a instância do campo (handler settings * para user) para que o DER module possa referenciar usuários como pai de * content_page nodes. */ function structural_pages_update_10010(): string { $messages = []; // 1. Storage: adiciona 'user' a entity_type_ids. $storage_config = \Drupal::configFactory() ->getEditable('field.storage.node.field_parent_page'); if (!$storage_config->isNew()) { $entity_type_ids = $storage_config->get('settings.entity_type_ids') ?? []; if (!in_array('user', $entity_type_ids, TRUE)) { $entity_type_ids[] = 'user'; $storage_config->set('settings.entity_type_ids', $entity_type_ids)->save(TRUE); $messages[] = 'Storage atualizado: user adicionado a entity_type_ids.'; } else { $messages[] = 'Storage: user já presente em entity_type_ids.'; } } else { $messages[] = 'field.storage.node.field_parent_page não encontrado.'; } // 2. Instância: adiciona handler settings para user. $field_config = \Drupal::configFactory() ->getEditable('field.field.node.content_page.field_parent_page'); if (!$field_config->isNew()) { $settings = $field_config->get('settings') ?? []; $type_ids = $settings['entity_type_ids'] ?? []; if (!in_array('user', $type_ids, TRUE)) { $type_ids[] = 'user'; $settings['entity_type_ids'] = $type_ids; } if (!isset($settings['user'])) { $settings['user'] = [ 'handler' => 'default:user', 'handler_settings' => [ 'include_anonymous' => FALSE, 'filter' => ['type' => '_none'], 'target_bundles' => NULL, 'sort' => ['field' => 'name', 'direction' => 'asc'], 'auto_create' => FALSE, ], ]; } $field_config->set('settings', $settings)->save(TRUE); $messages[] = 'Instância atualizada: handler settings de user adicionadas.'; } else { $messages[] = 'field.field.node.content_page.field_parent_page não encontrado.'; } return implode(' ', $messages); } /** * Migra field_site_section de entity_reference para dynamic_entity_reference. * * O campo precisa armazenar o domínio raiz da página (taxonomy_term OU user), * por isso deve se tornar um campo DER em vez de entity_reference simples. * * Passos: * 1. Adiciona coluna target_type nas tabelas do campo. * 2. Preenche todas as linhas existentes com target_type = 'taxonomy_term'. * 3. Atualiza o storage config para dynamic_entity_reference. * 4. Atualiza as instâncias (content_page e section_page). */ function structural_pages_update_10011(): string { $database = \Drupal::database(); $schema = $database->schema(); $messages = []; $column_spec = [ 'type' => 'varchar_ascii', 'length' => 128, 'not null' => FALSE, 'description' => 'The entity type of the field_site_section reference.', ]; foreach (['node__field_site_section', 'node_revision__field_site_section'] as $table) { if (!$schema->tableExists($table)) { continue; } if (!$schema->fieldExists($table, 'field_site_section_target_type')) { $schema->addField($table, 'field_site_section_target_type', $column_spec); // Populate existing rows as taxonomy_term. $database->update($table) ->fields(['field_site_section_target_type' => 'taxonomy_term']) ->isNull('field_site_section_target_type') ->execute(); $messages[] = "Coluna target_type adicionada em $table."; } else { $messages[] = "Coluna target_type já existe em $table."; } } // Update storage config. $storage = \Drupal::configFactory()->getEditable('field.storage.node.field_site_section'); if (!$storage->isNew()) { $storage->set('type', 'dynamic_entity_reference'); $storage->set('module', 'dynamic_entity_reference'); $storage->set('settings', [ 'exclude_entity_types' => FALSE, 'entity_type_ids' => ['taxonomy_term', 'user'], ]); $deps = $storage->get('dependencies') ?? []; $mods = $deps['module'] ?? []; if (!in_array('dynamic_entity_reference', $mods, TRUE)) { $mods[] = 'dynamic_entity_reference'; } $deps['module'] = $mods; $storage->set('dependencies', $deps); $storage->save(TRUE); $messages[] = 'Storage config atualizado para dynamic_entity_reference.'; } // Update content_page instance config. $field_cp = \Drupal::configFactory()->getEditable('field.field.node.content_page.field_site_section'); if (!$field_cp->isNew()) { $field_cp->set('field_type', 'dynamic_entity_reference'); $field_cp->set('settings', [ 'entity_type_ids' => ['taxonomy_term', 'user'], 'taxonomy_term' => [ 'handler' => 'default:taxonomy_term', 'handler_settings' => [ 'target_bundles' => ['site_sections' => 'site_sections'], 'sort' => ['field' => 'name', 'direction' => 'asc'], 'auto_create' => FALSE, ], ], 'user' => [ 'handler' => 'default:user', 'handler_settings' => [ 'include_anonymous' => FALSE, 'filter' => ['type' => '_none'], 'target_bundles' => NULL, 'sort' => ['field' => 'name', 'direction' => 'asc'], 'auto_create' => FALSE, ], ], ]); $deps = $field_cp->get('dependencies') ?? []; $mods = $deps['module'] ?? []; if (!in_array('dynamic_entity_reference', $mods, TRUE)) { $mods[] = 'dynamic_entity_reference'; } $deps['module'] = $mods; $field_cp->set('dependencies', $deps); $field_cp->save(TRUE); $messages[] = 'Instância content_page atualizada.'; } // Update section_page instance config (taxonomy_term only). $field_sp = \Drupal::configFactory()->getEditable('field.field.node.section_page.field_site_section'); if (!$field_sp->isNew()) { $field_sp->set('field_type', 'dynamic_entity_reference'); $field_sp->set('settings', [ 'entity_type_ids' => ['taxonomy_term'], 'taxonomy_term' => [ 'handler' => 'default:taxonomy_term', 'handler_settings' => [ 'target_bundles' => ['site_sections' => 'site_sections'], 'sort' => ['field' => 'name', 'direction' => 'asc'], 'auto_create' => FALSE, ], ], ]); $deps = $field_sp->get('dependencies') ?? []; $mods = $deps['module'] ?? []; if (!in_array('dynamic_entity_reference', $mods, TRUE)) { $mods[] = 'dynamic_entity_reference'; } $deps['module'] = $mods; $field_sp->set('dependencies', $deps); $field_sp->save(TRUE); $messages[] = 'Instância section_page atualizada.'; } // Rebuild field definitions cache. \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions(); return implode(' ', $messages); } /** * Restringe field_parent_page a content_page nodes e limpa referências inválidas. * * Na arquitetura anterior, field_parent_page podia apontar para taxonomy_term * ou user (como raiz do domínio). Na nova arquitetura, field_site_section é o * portador do domínio raiz, e field_parent_page aponta somente para * content_page nodes pai (ou é nulo para páginas raiz). * * Passos: * 1. Zera linhas onde target_type != 'node' (eram raízes de domínio). * 2. Atualiza storage e instância para entity_type_ids: [node]. */ function structural_pages_update_10012(): string { $database = \Drupal::database(); $schema = $database->schema(); $messages = []; foreach (['node__field_parent_page', 'node_revision__field_parent_page'] as $table) { if (!$schema->tableExists($table)) { continue; } // Clear rows that pointed to non-node entities (taxonomy_term, user, etc.). $deleted = $database->delete($table) ->condition('field_parent_page_target_type', 'node', '<>') ->execute(); if ($deleted) { $messages[] = "Removidas $deleted linhas não-node em $table."; } } // Update storage config. $storage = \Drupal::configFactory()->getEditable('field.storage.node.field_parent_page'); if (!$storage->isNew()) { $storage->set('settings.entity_type_ids', ['node'])->save(TRUE); $messages[] = 'Storage field_parent_page restrito a [node].'; } // Update content_page instance config. $field_cp = \Drupal::configFactory()->getEditable('field.field.node.content_page.field_parent_page'); if (!$field_cp->isNew()) { $field_cp->set('settings', [ 'entity_type_ids' => ['node'], 'node' => [ 'handler' => 'default:node', 'handler_settings' => [ 'target_bundles' => ['content_page' => 'content_page'], 'sort' => ['field' => 'title', 'direction' => 'asc'], 'auto_create' => FALSE, ], ], ]); // Remove dependencies no longer needed (taxonomy, user). $deps = $field_cp->get('dependencies') ?? []; $config_deps = $deps['config'] ?? []; $config_deps = array_filter($config_deps, fn($c) => !str_contains($c, 'taxonomy.vocabulary') && !str_contains($c, 'node.type.section_page')); $deps['config'] = array_values($config_deps); $field_cp->set('dependencies', $deps); $field_cp->save(TRUE); $messages[] = 'Instância field_parent_page restrita a content_page.'; } \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions(); return empty($messages) ? 'Nada a migrar.' : implode(' ', $messages); } /** * Corrige o tipo do storage field_parent_page para dynamic_entity_reference. * * Os update hooks anteriores (10006–10012) alteraram apenas as settings do * campo (entity_type_ids, exclude_entity_types) sem mudar o campo 'type' de * 'entity_reference' para 'dynamic_entity_reference'. Isso fazia com que o * sistema de entity queries do Drupal rejeitasse o especificador 'target_type' * com "Invalid specifier 'target_type'". * * Além disso, se o módulo event_management estiver instalado, adiciona 'event' * ao entity_type_ids do field_site_section para que content_pages possam * referenciar eventos como domínio raiz. */ function structural_pages_update_10013(): string { $messages = []; // 1. Migra field_parent_page de entity_reference para dynamic_entity_reference. $storage = \Drupal::configFactory()->getEditable('field.storage.node.field_parent_page'); if (!$storage->isNew() && $storage->get('type') !== 'dynamic_entity_reference') { $database = \Drupal::database(); $schema = $database->schema(); $col_spec = [ 'type' => 'varchar_ascii', 'length' => 128, 'not null' => FALSE, 'description' => 'Entity type of the field_parent_page reference.', ]; foreach (['node__field_parent_page', 'node_revision__field_parent_page'] as $table) { if ($schema->tableExists($table) && !$schema->fieldExists($table, 'field_parent_page_target_type')) { $schema->addField($table, 'field_parent_page_target_type', $col_spec); $database->update($table) ->fields(['field_parent_page_target_type' => 'node']) ->isNull('field_parent_page_target_type') ->execute(); } } $storage->set('type', 'dynamic_entity_reference'); $storage->set('module', 'dynamic_entity_reference'); $deps = $storage->get('dependencies') ?? []; $mods = $deps['module'] ?? []; if (!in_array('dynamic_entity_reference', $mods, TRUE)) { $mods[] = 'dynamic_entity_reference'; $deps['module'] = $mods; $storage->set('dependencies', $deps); } $storage->save(TRUE); $messages[] = 'field_parent_page migrado para dynamic_entity_reference.'; } else { $messages[] = 'field_parent_page já é dynamic_entity_reference.'; } // 2. Adiciona 'event' ao field_site_section se event_management estiver instalado. if (\Drupal::moduleHandler()->moduleExists('event_management')) { $fss_storage = \Drupal::configFactory()->getEditable('field.storage.node.field_site_section'); if (!$fss_storage->isNew() && $fss_storage->get('type') === 'dynamic_entity_reference') { $ids = $fss_storage->get('settings.entity_type_ids') ?? []; if (!in_array('event', $ids, TRUE)) { $ids[] = 'event'; $fss_storage->set('settings.entity_type_ids', $ids)->save(TRUE); $messages[] = "'event' adicionado ao entity_type_ids de field_site_section."; } else { $messages[] = "'event' já presente no entity_type_ids de field_site_section."; } } // Adiciona handler settings de event na instância content_page. $field_cp = \Drupal::configFactory()->getEditable('field.field.node.content_page.field_site_section'); if (!$field_cp->isNew() && $field_cp->get('field_type') === 'dynamic_entity_reference') { $settings = $field_cp->get('settings') ?? []; $type_ids = $settings['entity_type_ids'] ?? []; if (!in_array('event', $type_ids, TRUE)) { $type_ids[] = 'event'; $settings['entity_type_ids'] = $type_ids; } if (!isset($settings['event'])) { $settings['event'] = [ 'handler' => 'default:event', 'handler_settings' => [ 'filter' => ['type' => '_none'], 'sort' => ['field' => 'title', 'direction' => 'asc'], 'auto_create' => FALSE, ], ]; } $field_cp->set('settings', $settings)->save(TRUE); $messages[] = "Handler settings de event adicionadas ao field_site_section de content_page."; } } \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions(); return implode(' ', $messages); } /** * Implements hook_requirements(). */ function structural_pages_requirements(string $phase): array { $requirements = []; if ($phase === 'runtime') { // Check if the configured vocabulary has terms. $vocabulary = _structural_pages_get_vocabulary(); $term_count = \Drupal::entityQuery('taxonomy_term') ->accessCheck(FALSE) ->condition('vid', $vocabulary) ->count() ->execute(); if ($term_count === 0) { $requirements['structural_pages_terms'] = [ 'title' => t('Structural Pages'), 'value' => t('No terms in @vocabulary vocabulary', ['@vocabulary' => $vocabulary]), 'description' => t('The Structural Pages module requires terms in the site section vocabulary. Add terms.', [ ':url' => '/admin/structure/taxonomy/manage/' . $vocabulary . '/add', ]), 'severity' => REQUIREMENT_WARNING, ]; } else { $requirements['structural_pages_terms'] = [ 'title' => t('Structural Pages'), 'value' => t('@count terms configured', ['@count' => $term_count]), 'severity' => REQUIREMENT_OK, ]; } } return $requirements; } /** * Adiciona field_menu_title e field_weight ao tipo content_page. */ function structural_pages_update_10014(): string { $etm = \Drupal::entityTypeManager(); // --- field_menu_title --- if (!\Drupal\field\Entity\FieldStorageConfig::loadByName('node', 'field_menu_title')) { \Drupal\field\Entity\FieldStorageConfig::create([ 'field_name' => 'field_menu_title', 'entity_type' => 'node', 'type' => 'string', 'settings' => ['max_length' => 128], 'cardinality' => 1, 'translatable' => TRUE, ])->save(); } if (!\Drupal\field\Entity\FieldConfig::loadByName('node', 'content_page', 'field_menu_title')) { \Drupal\field\Entity\FieldConfig::create([ 'field_name' => 'field_menu_title', 'entity_type' => 'node', 'bundle' => 'content_page', 'label' => 'Título no menu', 'description' => 'Título abreviado para o menu de navegação. Se vazio, usa o título da página.', 'required' => FALSE, 'translatable' => TRUE, ])->save(); } // --- field_weight --- if (!\Drupal\field\Entity\FieldStorageConfig::loadByName('node', 'field_weight')) { \Drupal\field\Entity\FieldStorageConfig::create([ 'field_name' => 'field_weight', 'entity_type' => 'node', 'type' => 'integer', 'cardinality' => 1, 'translatable' => FALSE, ])->save(); } if (!\Drupal\field\Entity\FieldConfig::loadByName('node', 'content_page', 'field_weight')) { \Drupal\field\Entity\FieldConfig::create([ 'field_name' => 'field_weight', 'entity_type' => 'node', 'bundle' => 'content_page', 'label' => 'Peso (ordenação)', 'description' => 'Valores menores aparecem primeiro entre páginas com o mesmo pai. Padrão: 0.', 'required' => FALSE, 'translatable' => FALSE, 'default_value' => [['value' => 0]], ])->save(); } // Adiciona os campos ao form display padrão. $form_display = $etm->getStorage('entity_form_display') ->load('node.content_page.default'); if ($form_display) { if (!$form_display->getComponent('field_menu_title')) { $form_display->setComponent('field_menu_title', [ 'type' => 'string_textfield', 'weight' => 1, 'region' => 'content', 'settings' => ['size' => 60, 'placeholder' => ''], ]); } if (!$form_display->getComponent('field_weight')) { $form_display->setComponent('field_weight', [ 'type' => 'number', 'weight' => 27, 'region' => 'content', 'settings' => ['placeholder' => '0'], ]); } $form_display->save(); } return 'Campos field_menu_title e field_weight adicionados ao content_page.'; } /** * Adiciona field_show_in_menu (boolean) ao content_page. * * Retroativamente define o valor como 1 (exibir) para todas as páginas * existentes, preservando o comportamento anterior. */ function structural_pages_update_10015(): string { $etm = \Drupal::entityTypeManager(); // --- field_show_in_menu --- if (!\Drupal\field\Entity\FieldStorageConfig::loadByName('node', 'field_show_in_menu')) { \Drupal\field\Entity\FieldStorageConfig::create([ 'field_name' => 'field_show_in_menu', 'entity_type' => 'node', 'type' => 'boolean', 'settings' => ['on_label' => 'Sim', 'off_label' => 'Não'], 'cardinality' => 1, 'translatable' => FALSE, ])->save(); } if (!\Drupal\field\Entity\FieldConfig::loadByName('node', 'content_page', 'field_show_in_menu')) { \Drupal\field\Entity\FieldConfig::create([ 'field_name' => 'field_show_in_menu', 'entity_type' => 'node', 'bundle' => 'content_page', 'label' => 'Exibir no menu', 'description' => 'Se desmarcado, a página não aparecerá no menu de navegação.', 'required' => FALSE, 'translatable' => FALSE, 'default_value' => [['value' => 1]], ])->save(); } // Adiciona ao form display padrão. $form_display = $etm->getStorage('entity_form_display') ->load('node.content_page.default'); if ($form_display && !$form_display->getComponent('field_show_in_menu')) { $form_display->setComponent('field_show_in_menu', [ 'type' => 'boolean_checkbox', 'weight' => 1, 'region' => 'content', 'settings' => ['display_label' => TRUE], ]); $form_display->save(); } // Retroativamente define field_show_in_menu = 1 para content_pages existentes // que ainda não têm valor (NULL é tratado como 0 nas queries). $nids = $etm->getStorage('node')->getQuery() ->accessCheck(FALSE) ->condition('type', 'content_page') ->notExists('field_show_in_menu') ->execute(); foreach ($nids as $nid) { /** @var \Drupal\node\NodeInterface $node */ $node = $etm->getStorage('node')->load($nid); if ($node) { $node->set('field_show_in_menu', 1); $node->save(); } } return 'Campo field_show_in_menu adicionado ao content_page; ' . count($nids) . ' página(s) retroativamente marcada(s) como visíveis.'; }