Issue #3079398 by Dave Reid: Allow support for more entity types.

parent 6d458e79
......@@ -2,15 +2,15 @@
namespace Drupal\xmlsitemap\Form;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\State\StateInterface;
/**
* Configure what entities will be included in sitemap.
......@@ -88,40 +88,41 @@ class XmlSitemapEntitiesSettingsForm extends ConfigFormBase {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$entity_types = $this->entityTypeManager->getDefinitions();
$labels = [];
$default = [];
$bundles = $this->entityTypeBundleInfo->getAllBundleInfo();
foreach ($entity_types as $entity_type_id => $entity_type) {
if (!$entity_type instanceof ContentEntityTypeInterface || !isset($bundles[$entity_type_id])) {
continue;
}
$form = parent::buildForm($form, $form_state);
$labels[$entity_type_id] = $entity_type->getLabel() ?: $entity_type_id;
}
// Create the list of possible entity types.
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
$entity_types = array_filter($this->entityTypeManager->getDefinitions(), 'xmlsitemap_is_entity_type_supported');
// Create the list of options as well as the default values based on which
// entity types have enabled configuration already.
$labels = array_map(function (EntityTypeInterface $entityType) {
return $entityType->getLabel();
}, $entity_types);
asort($labels);
$form['#labels'] = $labels;
$defaults = array_keys(array_filter(array_map(function (EntityTypeInterface $entityType) {
return xmlsitemap_link_entity_check_enabled($entityType->id());
}, $entity_types)));
$form['entity_types'] = [
'#title' => $this->t('Custom sitemap entities settings'),
'#type' => 'checkboxes',
'#options' => $labels,
'#default_value' => $default,
'#default_value' => $defaults,
];
$form['settings'] = ['#tree' => TRUE];
foreach ($labels as $entity_type_id => $label) {
$entity_type = $entity_types[$entity_type_id];
$bundle_label = $entity_type->getBundleLabel() ?: $label;
$bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
$form['settings'][$entity_type_id] = [
'#type' => 'container',
'#entity_type' => $entity_type_id,
'#bundle_label' => $entity_type->getBundleLabel() ? $entity_type->getBundleLabel() : $label,
'#title' => $entity_type->getBundleLabel() ? $entity_type->getBundleLabel() : $label,
'#bundle_label' => $bundle_label,
'#title' => $bundle_label,
'#states' => [
'visible' => [
':input[name="entity_types[' . $entity_type_id . ']"]' => ['checked' => TRUE],
......@@ -134,7 +135,7 @@ class XmlSitemapEntitiesSettingsForm extends ConfigFormBase {
'#default_value' => [],
'#header' => [
[
'data' => $entity_type->getBundleLabel() ? $entity_type->getBundleLabel() : $label,
'data' => $bundle_label,
'class' => ['bundle'],
],
[
......@@ -142,11 +143,11 @@ class XmlSitemapEntitiesSettingsForm extends ConfigFormBase {
'class' => ['operations'],
],
],
'#empty' => $this->t('No content available.'),
],
'#access' => !empty($bundles),
];
foreach ($bundles[$entity_type_id] as $bundle => $bundle_info) {
foreach ($bundles as $bundle => $bundle_info) {
$form['settings'][$entity_type_id][$bundle]['settings'] = [
'#type' => 'item',
'#label' => $bundle_info['label'],
......@@ -171,15 +172,8 @@ class XmlSitemapEntitiesSettingsForm extends ConfigFormBase {
],
];
$form['settings'][$entity_type_id]['types']['#default_value'][$bundle] = xmlsitemap_link_bundle_check_enabled($entity_type_id, $bundle);
if (xmlsitemap_link_bundle_check_enabled($entity_type_id, $bundle)) {
$default[$entity_type_id] = $entity_type_id;
}
}
}
$form['entity_types']['#default_value'] = $default;
$form = parent::buildForm($form, $form_state);
$form['actions']['submit']['#value'] = $this->t('Save');
return $form;
}
......
......@@ -72,7 +72,7 @@ class XmlSitemapUserFunctionalTest extends XmlSitemapTestBase {
$this->assertSession()->fieldExists('xmlsitemap[priority]');
$this->assertSession()->fieldExists('xmlsitemap[changefreq]');
$this->drupalGet('user/' . $this->normal_user->id() . '/edit');
$this->drupalGet('user/' . $this->normal_user->id() . '/edit');
$this->assertSession()->fieldExists('xmlsitemap[status]');
$this->assertSession()->fieldExists('xmlsitemap[priority]');
$this->assertSession()->fieldExists('xmlsitemap[changefreq]');
......
......@@ -21,19 +21,14 @@
function hook_xmlsitemap_link_info() {
return [
'mymodule' => [
'label' => 'My module',
'base table' => 'mymodule',
'entity keys' => [
// Primary ID key on {base table}.
'id' => 'myid',
// Subtype key on {base table}.
'bundle' => 'mysubtype',
],
'path callback' => 'mymodule_path',
'label' => 'My module items',
// If your items can be grouped into unique "bundles", add the following
// information.
'bundle label' => t('Subtype name'),
'bundles' => [
'mysubtype1' => [
'label' => t('My subtype 1'),
// If your bundles have an administrative UI, list it.
'admin' => [
'real path' => 'admin/settings/mymodule/mysubtype1/edit',
'access arguments' => ['administer mymodule'],
......@@ -47,11 +42,11 @@ function hook_xmlsitemap_link_info() {
'xmlsitemap' => [
// Callback function to take an array of IDs and save them as sitemap
// links.
'process callback' => '',
'process callback' => 'mymodule_xmlsitemap_process_links',
// Callback function used in batch API for rebuilding all links.
'rebuild callback' => '',
'rebuild callback' => 'mymodule_xmlsitemap_rebuild_links',
// Callback function called from the XML sitemap settings page.
'settings callback' => '',
'settings callback' => 'mymodule_xmlsitemap_settings',
],
],
];
......
......@@ -20,6 +20,7 @@ use Drupal\Core\Entity\ContentEntityFormInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\FileSystemInterface;
......@@ -632,6 +633,76 @@ function _xmlsitemap_delete_recursive($path, $delete_root = FALSE) {
return $filesystem->delete($path);
}
/**
* Implements hook_entity_type_build().
*/
function xmlsitemap_entity_type_build(array &$entity_types) {
// Mark some specific core entity types as not supported by XML sitemap.
// If a site wants to undo this, they may use hook_entity_type_alter().
$unsupported_types = [
// Custom blocks.
'block_content',
// Comments.
'comment',
// Shortcut items.
'shortcut',
];
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
foreach ($unsupported_types as $entity_type_id) {
if (isset($entity_types[$entity_type_id])) {
$entity_types[$entity_type_id]->set('xmlsitemap', FALSE);
}
}
}
/**
* Determines if an entity type can be listed in the XML sitemap as links.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*
* @return bool
* TRUE if the entity type can be used, or FALSE otherwise.
*/
function xmlsitemap_is_entity_type_supported(EntityTypeInterface $entity_type) {
// If the XML sitemap status in the entity type annotation has been set then
// return that first. This will allow modules to bypass the logic below if
// needed.
$status = $entity_type->get('xmlsitemap');
if ($status !== NULL) {
return $status;
}
// Skip if the entity type is not a content entity type.
if (!($entity_type instanceof ContentEntityTypeInterface)) {
return FALSE;
}
// Skip if the entity type is not "viewable"
if (!$entity_type->hasViewBuilderClass()) {
return FALSE;
}
// Skip if the entity type is internal (and not considered public).
if ($entity_type->isInternal()) {
return FALSE;
}
// Skip if the entity type does not have a canonical URL.
if (!$entity_type->hasLinkTemplate('canonical') && !$entity_type->getUriCallback()) {
return FALSE;
}
// Skip if the entity type as a bundle entity type but does not yet have
// any bundles created.
if ($entity_type->getBundleEntityType() && !\Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type->id())) {
return FALSE;
}
return TRUE;
}
/**
* Returns information about supported sitemap link types.
*
......@@ -654,9 +725,7 @@ function xmlsitemap_get_link_info($type = NULL, $reset = FALSE) {
if ($reset) {
$link_info = NULL;
foreach (\Drupal::languageManager()->getLanguages() as $lang) {
\Drupal::cache()->delete('xmlsitemap:link_info:' . $lang->getId());
}
\Drupal::service('cache_tags.invalidator')->invalidateTags(['xmlsitemap']);
}
if (!isset($link_info)) {
......@@ -667,34 +736,36 @@ function xmlsitemap_get_link_info($type = NULL, $reset = FALSE) {
else {
$link_info = [];
$entity_types = \Drupal::entityTypeManager()->getDefinitions();
foreach ($entity_types as $key => $entity_type) {
if (!xmlsitemap_is_entity_type_supported($entity_type)) {
continue;
}
$link_info[$key] = [
'label' => $entity_type->getLabel(),
'type' => $entity_type->id(),
'base table' => $entity_type->getBaseTable(),
'bundles' => \Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type->id()),
'bundle label' => $entity_type->getBundleLabel(),
'entity keys' => [
'id' => $entity_type->getKey('id'),
'bundle' => $entity_type->getKey('bundle'),
],
];
$uri_callback = $entity_type->getUriCallback();
if (empty($uri_callback) || !isset($entity_type->xmlsitemap)) {
// Remove any non URL-able or XML sitemap un-supported entities.
}
foreach (\Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type->id()) as $bundle) {
if (!isset($bundle['xmlsitemap'])) {
// Remove any un-supported entity bundles.
}
}
}
$link_info = array_merge($link_info, \Drupal::moduleHandler()->invokeAll('xmlsitemap_link_info'));
foreach ($link_info as $key => &$info) {
$info += [
'type' => $key,
'base table' => FALSE,
'bundles' => [],
'xmlsitemap' => [
'process callback' => 'xmlsitemap_xmlsitemap_process_entity_links',
],
];
if (!isset($info['xmlsitemap']['rebuild callback']) && !empty($info['base table']) && $entity_types[$key]->getKey('id')) {
if (!isset($info['xmlsitemap']['process callback']) && !empty($info['entity keys'])) {
$info['xmlsitemap']['process callback'] = 'xmlsitemap_xmlsitemap_process_entity_links';
}
if (!isset($info['xmlsitemap']['rebuild callback']) && !empty($info['entity keys']['id'])) {
$info['xmlsitemap']['rebuild callback'] = 'xmlsitemap_rebuild_batch_fetch';
}
foreach ($info['bundles'] as $bundle => &$bundle_info) {
......@@ -705,9 +776,23 @@ function xmlsitemap_get_link_info($type = NULL, $reset = FALSE) {
}
}
\Drupal::moduleHandler()->alter('xmlsitemap_link_info', $link_info);
ksort($link_info);
// Sort the entity types by label.
uasort($link_info, function ($a, $b) {
// Put frontpage first.
if ($a['type'] === 'frontpage') {
return -1;
}
if ($b['type'] === 'frontpage') {
return 1;
}
return strnatcmp($a['label'], $b['label']);
});
// Cache by language since this info contains translated strings.
\Drupal::cache()->set($cid, $link_info, Cache::PERMANENT, ['xmlsitemap']);
// Also include entity type tags since this is tied to entity and bundle
// information.
\Drupal::cache()->set($cid, $link_info, Cache::PERMANENT, ['xmlsitemap', 'entity_types', 'entity_bundles']);
}
}
......
<?php
/**
* @file
* Post update functions for XML Sitemap.
*/
/**
* Force cache to be cleared with new hook_entity_type_build().
*
* @see https://www.drupal.org/project/xmlsitemap/issues/3079398
*/
function xmlsitemap_post_update_entity_type_build_hook() {
// Empty post-update hook.
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment