'item_list', '#items' => $missing_extensions]; $requirements['xmlsitemap_php_extensions'] = [ 'title' => $t('XML Sitemap PHP extensions'), 'value' => $t('Disabled'), 'severity' => REQUIREMENT_ERROR, 'description' => $t("The XML Sitemap module requires you to enable the PHP extensions in the following list (see the module's system requirements page for more information): @extensions", [ '@xmlsitemap_requirements' => 'https://www.drupal.org/documentation/modules/xmlsitemap/requirements', '@extensions' => \Drupal::service('renderer')->renderPlain($items), ]), ]; } if ($phase == 'runtime') { // If clean URLs are disabled there must not be an actual sitemap.xml in // the root directory. if (\Drupal::config('xmlsitemap.settings')->get('clean_url') && file_exists(DRUPAL_ROOT . '/sitemap.xml')) { $requirements['xmlsitemap_file'] = [ 'title' => $t('XML Sitemap'), 'value' => $t('Existing sitemap.xml file found.'), 'severity' => REQUIREMENT_ERROR, 'description' => $t('The XML Sitemap module cannot display its XML output if there is an existing sitemap.xml file in your website root.'), ]; } // Check that the base directory and all its subdirectories are writable. $requirements['xmlsitemap_directory'] = [ 'title' => $t('XML Sitemap cache directory'), 'value' => $t('Writable'), ]; if (!xmlsitemap_check_directory()) { $requirements['xmlsitemap_directory']['value'] = $t('Not found or not writable'); $requirements['xmlsitemap_directory']['severity'] = REQUIREMENT_ERROR; $requirements['xmlsitemap_directory']['description'] = $t('The directory %directory was not found or is not writable by the server. See @docpage for more information.', ['%directory' => xmlsitemap_get_directory(), '@docpage' => 'https://www.drupal.org/node/244924']); } else { $directories = xmlsitemap_check_all_directories(); foreach ($directories as $directory => $writable) { if ($writable) { unset($directories[$directory]); } } if (!empty($directories)) { $items = ['#theme' => 'item_list', '#items' => array_keys($directories)]; $requirements['xmlsitemap_directory']['value'] = $t('Not found or not writable'); $requirements['xmlsitemap_directory']['severity'] = REQUIREMENT_ERROR; $requirements['xmlsitemap_directory']['description'] = $t('The following directories were not found or are not writable by the server. See @docpage for more information. @directories', ['@directories' => \Drupal::service('renderer')->renderPlain($items), '@docpage' => 'https://www.drupal.org/node/244924']); } } $sitemaps = \Drupal::entityTypeManager()->getStorage('xmlsitemap')->loadMultiple(); $max_links = -1; $max_chunks = -1; $max_filesize = -1; foreach ($sitemaps as $sitemap) { $max_links = max([$max_links, $sitemap->getLinks()]); $max_chunks = max([$max_chunks, $sitemap->getChunks()]); $max_filesize = max([$max_filesize, $sitemap->getMaxFileSize()]); } // The maximum number of links in a sitemap. $max_links_limit = XMLSITEMAP_MAX_SITEMAP_LINKS * XMLSITEMAP_MAX_SITEMAP_LINKS; if ($max_links > $max_links_limit) { $requirements['xmlsitemap_link_count'] = [ 'title' => $t('XML Sitemap link count'), 'value' => $max_links, 'description' => $t('You have exceeded the number of links that your sitemap can contain (@num).', ['@num' => number_format($max_links)]), 'severity' => REQUIREMENT_ERROR, ]; } // The maximum number of chunks in a sitemap. if ($max_chunks > XMLSITEMAP_MAX_SITEMAP_LINKS) { $requirements['xmlsitemap_chunk_count'] = [ 'title' => $t('XML Sitemap page count'), 'value' => $max_chunks, 'description' => $t('You have exceeded the number of sitemap pages (@number).', ['@number' => number_format(XMLSITEMAP_MAX_SITEMAP_LINKS)]), 'severity' => REQUIREMENT_ERROR, ]; if (!in_array(xmlsitemap_get_chunk_size(), [50000, 'auto'])) { $requirements['xmlsitemap_chunk_count']['description'] .= ' ' . t('Please increase the number of links per page.'); } } // Check maximum file size. $requirements['xmlsitemap_file_size'] = [ 'title' => $t('XML Sitemap maximum file size'), 'value' => format_size($max_filesize), ]; if ($max_filesize > XMLSITEMAP_MAX_SITEMAP_FILESIZE) { $requirements['xmlsitemap_file_size']['description'] = $t('You have exceeded the maximum sitemap file size of @size. If possible, decrease the number of links per sitemap page.', ['@size' => format_size(XMLSITEMAP_MAX_SITEMAP_FILESIZE)]); $requirements['xmlsitemap_file_size']['severity'] = REQUIREMENT_ERROR; } elseif (!\Drupal::state()->get('xmlsitemap_developer_mode')) { unset($requirements['xmlsitemap_file_size']); } // Check when the cached files were last generated. $generated_last = \Drupal::state()->get('xmlsitemap_generated_last', 0); $generated_ago = \Drupal::time()->getRequestTime() - $generated_last; $requirements['xmlsitemap_generated'] = [ 'title' => $t('XML Sitemap'), 'value' => $generated_last ? $t('Last attempted generation on @date (@interval ago).', [ '@date' => \Drupal::service('date.formatter')->format($generated_last, 'small'), '@interval' => \Drupal::service('date.formatter')->formatInterval($generated_ago), ]) : $t('Cached files have not been generated yet.'), 'severity' => REQUIREMENT_OK, ]; if (\Drupal::state()->get('xmlsitemap_rebuild_needed')) { $requirements['xmlsitemap_generated']['severity'] = REQUIREMENT_ERROR; $requirements['xmlsitemap_generated']['description'] = $t('The XML Sitemap data is out of sync and needs to be completely rebuilt.', ['@link-rebuild' => Url::fromRoute('xmlsitemap.admin_rebuild')->toString()]); } elseif (\Drupal::state()->get('xmlsitemap_regenerate_needed')) { $last_run = $generated_last; // If cron regeneration is enabled, factor in the last time cron was run // because the regenerate flag might have been set between the last cron // run and now. if (!\Drupal::config('xmlsitemap.settings')->get('disable_cron_regeneration')) { $last_run = max($generated_last, \Drupal::state()->get('system.cron_last', 0), \Drupal::state()->get('install_time', 0)); } $last_run_ago = \Drupal::time()->getRequestTime() - $last_run; $cron_warning_threshold = \Drupal::config('system.cron')->get('threshold.requirements_warning'); $cron_error_threshold = \Drupal::config('system.cron')->get('threshold.requirements_error'); if ($max_filesize == 0) { // A maximum sitemap file size of 0 indicates an error in generation. $requirements['xmlsitemap_generated']['severity'] = REQUIREMENT_ERROR; } elseif ($last_run_ago >= $cron_error_threshold) { $requirements['xmlsitemap_generated']['severity'] = REQUIREMENT_ERROR; } elseif ($last_run_ago >= $cron_warning_threshold) { $requirements['xmlsitemap_generated']['severity'] = REQUIREMENT_WARNING; } if ($requirements['xmlsitemap_generated']['severity']) { if (\Drupal::config('xmlsitemap.settings')->get('disable_cron_regeneration')) { // Don't show the link to run cron if cron regeneration is disabled. $requirements['xmlsitemap_generated']['description'] = $t('The XML cached files are out of date and need to be regenerated.'); } else { $requirements['xmlsitemap_generated']['description'] = $t('The XML cached files are out of date and need to be regenerated. You can run cron manually to regenerate the sitemap files.', [ '@link-cron' => Url::fromRoute('system.run_cron', [], ['query' => \Drupal::destination()->getAsArray()])->toString(), ]); } } } $anonymous_accout = new AnonymousUserSession(); if (!$anonymous_accout->hasPermission('access user profiles') && xmlsitemap_link_bundle_check_enabled('user', 'user')) { $requirements['xmlsitemap_user_anonymous_permission'] = [ 'title' => $t('XML Sitemap user'), 'value' => $t('Anonymous access to user profiles'), 'description' => $t('In order to list user profile links in the sitemap, the anonymous user must have the View user profiles permission.', [ '@perm-link' => Url::fromRoute('entity.user_role.edit_permissions_form', ['user_role' => RoleInterface::ANONYMOUS_ID], ['fragment' => 'module-user'])->toString(), ]), 'severity' => REQUIREMENT_ERROR, ]; } } return $requirements; } /** * Implements hook_schema(). */ function xmlsitemap_schema() { // @todo Rename to xmlsitemap_link $schema['xmlsitemap'] = [ 'description' => 'The base table for xmlsitemap links.', 'fields' => [ 'id' => [ 'description' => 'Primary key with type; a unique id for the item.', 'type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => '', ], 'type' => [ 'description' => 'Primary key with id; the type of item (e.g. node, user, etc.).', 'type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => '', ], 'subtype' => [ 'description' => 'A sub-type identifier for the link (node type, menu name, term VID, etc.).', 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => '', ], 'loc' => [ 'description' => 'The URL to the item relative to the Drupal path.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '', ], 'language' => [ 'description' => 'The {languages}.language of this link or an empty string if it is language-neutral.', 'type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '', ], 'access' => [ 'description' => 'A boolean that represents if the item is viewable by the anonymous user. This field is useful to store the result of node_access() so we can retain changefreq and priority_override information.', 'type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1, ], 'status' => [ 'description' => 'An integer that represents if the item is included in the sitemap.', 'type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 1, ], 'status_override' => [ 'description' => 'A boolean that if TRUE means that the status field has been overridden from its default value.', 'type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, ], 'lastmod' => [ 'description' => 'The UNIX timestamp of last modification of the item.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ], 'priority' => [ 'description' => 'The priority of this URL relative to other URLs on your site. Valid values range from 0.0 to 1.0.', 'type' => 'float', 'default' => NULL, ], 'priority_override' => [ 'description' => 'A boolean that if TRUE means that the priority field has been overridden from its default value.', 'type' => 'int', 'size' => 'tiny', 'not null' => TRUE, 'default' => 0, ], 'changefreq' => [ 'description' => 'The average time in seconds between changes of this item.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ], 'changecount' => [ 'description' => 'The number of times this item has been changed. Used to help calculate the next changefreq value.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ], ], 'primary key' => ['id', 'type', 'language'], 'indexes' => [ 'loc' => ['loc'], 'access_status_loc' => ['access', 'status', 'loc'], 'type_subtype' => ['type', 'subtype'], ], ]; return $schema; } /** * Implements hook_install(). */ function xmlsitemap_install() { // Set this module's weight to 1 so xmlsitemap_cron() runs after all other // xmlsitemap_x_cron() runs. module_set_weight('xmlsitemap', 1); // Insert the homepage link into the {xmlsitemap} table so we do not show an // empty sitemap after install. \Drupal::database()->insert('xmlsitemap') ->fields([ 'type' => 'frontpage', 'id' => 0, 'loc' => '/', 'priority' => \Drupal::config('xmlsitemap.settings')->get('frontpage_priority'), 'changefreq' => \Drupal::config('xmlsitemap.settings')->get('frontpage_changefreq'), 'language' => LanguageInterface::LANGCODE_NOT_SPECIFIED, ]) ->execute(); $state_variables = xmlsitemap_state_variables(); \Drupal::state()->setMultiple($state_variables); $xmlsitemap_base_url = rtrim(Url::fromUri('base:', ['absolute' => TRUE])->toString(), '/'); \Drupal::state()->set('xmlsitemap_base_url', $xmlsitemap_base_url); $context = xmlsitemap_get_current_context(); $sitemap = \Drupal::entityTypeManager()->getStorage('xmlsitemap')->create([ 'id' => xmlsitemap_sitemap_get_context_hash($context), ]); $sitemap->context = xmlsitemap_get_current_context(); $sitemap = $sitemap->setLabel(\Drupal::state()->get('xmlsitemap_base_url')); $sitemap->save(); xmlsitemap_check_directory(); \Drupal::state()->set('xmlsitemap_regenerate_needed', TRUE); } /** * Implements hook_uninstall(). */ function xmlsitemap_uninstall() { $variables = array_keys(xmlsitemap_state_variables()); foreach ($variables as $variable) { \Drupal::state()->delete($variable); } // Remove the file cache directory. xmlsitemap_clear_directory(NULL, TRUE); $entity_types = \Drupal::entityTypeManager()->getDefinitions(); $bundles = \Drupal::service('entity_type.bundle.info')->getAllBundleInfo(); foreach ($entity_types as $entity_type_id => $entity_type) { if (isset($bundles[$entity_type_id])) { foreach ($bundles[$entity_type_id] as $bundle_id => $bundle) { xmlsitemap_link_bundle_delete($entity_type_id, $bundle_id); } } } } /** * Change the primary key of the 'xmlsitemap' table to include the language. */ function xmlsitemap_update_8001() { \Drupal::database()->schema()->dropPrimaryKey('xmlsitemap'); \Drupal::database()->schema()->addPrimaryKey('xmlsitemap', [ 'id', 'type', 'language', ]); \Drupal::database()->schema()->dropIndex('xmlsitemap', 'language'); } /** * Update the path of the frontpage link. */ function xmlsitemap_update_8002() { \Drupal::database()->update('xmlsitemap') ->fields(['loc' => '/']) ->condition('type', 'frontpage') ->execute(); } /** * Update the path of the frontpage link in case it was reset again. */ function xmlsitemap_update_8003() { xmlsitemap_update_8002(); }