Files
structural_pages/structural_pages.install
Quintino A. G. Souza f32a27dff9 Adiciona campo field_show_in_menu ao content_page
Campo booleano (padrão: ativo) que controla se a página aparece no menu
de navegação. Quando desmarcado, oculta field_menu_title no formulário
via #states e exclui a página da query em getChildPages(). O campo
field_weight permanece sempre visível, pois a ordenação se aplica
independentemente da exibição no menu.

Hook update_10015 cria storage + instância, atualiza o form display e
retroativamente define o valor como 1 para páginas existentes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 09:55:26 -03:00

992 lines
34 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* @file
* Install, update and uninstall functions for the Structural Pages module.
*/
declare(strict_types=1);
use Drupal\taxonomy\Entity\Term;
/**
* Implements hook_install().
*/
function structural_pages_install(): void {
// Create default terms for the configured vocabulary.
_structural_pages_create_default_terms();
// Display success message.
\Drupal::messenger()->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 (1000610012) 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. <a href=":url">Add terms</a>.', [
':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.';
}