3 namespace Drupal\simple_sitemap;
5 use Drupal\simple_sitemap\Form\FormHelper;
6 use Drupal\Core\Database\Connection;
7 use Drupal\Core\Entity\EntityTypeManagerInterface;
8 use Drupal\Core\Path\PathValidator;
9 use Drupal\Core\Entity\Query\QueryFactory;
10 use Drupal\Core\Config\ConfigFactory;
11 use Drupal\Core\Datetime\DateFormatter;
15 * @package Drupal\simple_sitemap
20 * @var \Drupal\simple_sitemap\SitemapGenerator
22 protected $sitemapGenerator;
25 * @var \Drupal\simple_sitemap\EntityHelper
27 protected $entityHelper;
30 * @var \Drupal\Core\Config\ConfigFactory
32 protected $configFactory;
35 * @var \Drupal\Core\Database\Connection
40 * @var \Drupal\Core\Entity\Query\QueryFactory
42 protected $entityQuery;
45 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
47 protected $entityTypeManager;
50 * @var \Drupal\Core\Path\PathValidator
52 protected $pathValidator;
57 protected static $allowed_link_settings = [
58 'entity' => ['index', 'priority'],
59 'custom' => ['priority'],
63 * Simplesitemap constructor.
64 * @param \Drupal\simple_sitemap\SitemapGenerator $sitemapGenerator
65 * @param \Drupal\simple_sitemap\EntityHelper $entityHelper
66 * @param \Drupal\Core\Config\ConfigFactory $configFactory
67 * @param \Drupal\Core\Database\Connection $database
68 * @param \Drupal\Core\Entity\Query\QueryFactory $entityQuery
69 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
70 * @param \Drupal\Core\Path\PathValidator $pathValidator
71 * @param \Drupal\Core\Datetime\DateFormatter $dateFormatter
73 public function __construct(
74 SitemapGenerator $sitemapGenerator,
75 EntityHelper $entityHelper,
76 ConfigFactory $configFactory,
78 QueryFactory $entityQuery,
79 EntityTypeManagerInterface $entityTypeManager,
80 PathValidator $pathValidator,
81 DateFormatter $dateFormatter
83 $this->sitemapGenerator = $sitemapGenerator;
84 $this->entityHelper = $entityHelper;
85 $this->configFactory = $configFactory;
86 $this->db = $database;
87 $this->entityQuery = $entityQuery;
88 $this->entityTypeManager = $entityTypeManager;
89 $this->pathValidator = $pathValidator;
90 $this->dateFormatter = $dateFormatter;
94 * Returns a specific sitemap setting or a default value if setting does not
98 * Name of the setting, like 'max_links'.
100 * @param mixed $default
101 * Value to be returned if the setting does not exist in the configuration.
104 * The current setting from configuration or a default value.
106 public function getSetting($name, $default = FALSE) {
107 $setting = $this->configFactory
108 ->get('simple_sitemap.settings')
110 return NULL !== $setting ? $setting : $default;
114 * Stores a specific sitemap setting in configuration.
116 * @param string $name
117 * Setting name, like 'max_links'.
118 * @param mixed $setting
119 * The setting to be saved.
123 public function saveSetting($name, $setting) {
124 $this->configFactory->getEditable("simple_sitemap.settings")
125 ->set($name, $setting)->save();
130 * Returns the whole sitemap, a requested sitemap chunk,
131 * or the sitemap index file.
133 * @param int $chunk_id
135 * @return string|false
136 * If no sitemap id provided, either a sitemap index is returned, or the
137 * whole sitemap, if the amount of links does not exceed the max links
138 * setting. If a sitemap id is provided, a sitemap chunk is returned. False
139 * if sitemap is not retrievable from the database.
141 public function getSitemap($chunk_id = NULL) {
142 $chunk_info = $this->fetchSitemapChunkInfo();
144 if (NULL === $chunk_id || !isset($chunk_info[$chunk_id])) {
146 if (count($chunk_info) > 1) {
147 // Return sitemap index, if there are multiple sitemap chunks.
148 return $this->getSitemapIndex($chunk_info);
151 // Return sitemap if there is only one chunk.
152 return count($chunk_info) === 1
153 && isset($chunk_info[SitemapGenerator::FIRST_CHUNK_INDEX])
154 ? $this->fetchSitemapChunk(SitemapGenerator::FIRST_CHUNK_INDEX)
160 // Return specific sitemap chunk.
161 return $this->fetchSitemapChunk($chunk_id)->sitemap_string;
166 * Fetches all sitemap chunk timestamps keyed by chunk ID.
169 * An array containing chunk creation timestamps keyed by chunk ID.
171 protected function fetchSitemapChunkInfo() {
173 ->query("SELECT id, sitemap_created FROM {simple_sitemap}")
174 ->fetchAllAssoc('id');
178 * Fetches a single sitemap chunk by ID.
184 * A sitemap chunk object.
186 private function fetchSitemapChunk($id) {
187 return $this->db->query('SELECT * FROM {simple_sitemap} WHERE id = :id',
188 [':id' => $id])->fetchObject();
192 * Generates the sitemap for all languages and saves it to the db.
194 * @param string $from
195 * Can be 'form', 'cron', 'drush' or 'nobatch'.
196 * This decides how the batch process is to be run.
198 public function generateSitemap($from = 'form') {
199 $this->sitemapGenerator
200 ->setGenerator($this)
201 ->setGenerateFrom($from)
206 * Generates and returns the sitemap index as string.
208 * @param array $chunk_info
209 * Array containing chunk creation timestamps keyed by chunk ID.
214 protected function getSitemapIndex($chunk_info) {
215 return $this->sitemapGenerator
216 ->setGenerator($this)
217 ->generateSitemapIndex($chunk_info);
221 * Returns a 'time ago' string of last timestamp generation.
223 * @return string|false
224 * Formatted timestamp of last sitemap generation, otherwise FALSE.
226 public function getGeneratedAgo() {
227 $chunks = $this->fetchSitemapChunkInfo();
228 if (isset($chunks[SitemapGenerator::FIRST_CHUNK_INDEX]->sitemap_created)) {
229 return $this->dateFormatter
230 ->formatInterval(REQUEST_TIME - $chunks[SitemapGenerator::FIRST_CHUNK_INDEX]
237 * Enables sitemap support for an entity type. Enabled entity types show
238 * sitemap settings on their bundle setting forms. If an enabled entity type
239 * features bundles (e.g. 'node'), it needs to be set up with
240 * setBundleSettings() as well.
242 * @param string $entity_type_id
243 * Entity type id like 'node'.
247 public function enableEntityType($entity_type_id) {
248 $enabled_entity_types = $this->getSetting('enabled_entity_types');
249 if (!in_array($entity_type_id, $enabled_entity_types)) {
250 $enabled_entity_types[] = $entity_type_id;
251 $this->saveSetting('enabled_entity_types', $enabled_entity_types);
257 * Disables sitemap support for an entity type. Disabling support for an
258 * entity type deletes its sitemap settings permanently and removes sitemap
259 * settings from entity forms.
261 * @param string $entity_type_id
262 * Entity type id like 'node'.
266 public function disableEntityType($entity_type_id) {
268 // Updating settings.
269 $enabled_entity_types = $this->getSetting('enabled_entity_types');
270 if (FALSE !== ($key = array_search($entity_type_id, $enabled_entity_types))) {
271 unset ($enabled_entity_types[$key]);
272 $this->saveSetting('enabled_entity_types', array_values($enabled_entity_types));
275 // Deleting inclusion settings.
276 $config_names = $this->configFactory->listAll("simple_sitemap.bundle_settings.$entity_type_id");
277 foreach($config_names as $config_name) {
278 $this->configFactory->getEditable($config_name)->delete();
281 // Deleting entity overrides.
282 $this->removeEntityInstanceSettings($entity_type_id);
287 * Sets sitemap settings for a non-bundle entity type (e.g. user) or a bundle
288 * of an entity type (e.g. page).
290 * @param string $entity_type_id
291 * Entity type id like 'node' the bundle belongs to.
292 * @param string $bundle_name
293 * Name of the bundle. NULL if entity type has no bundles.
294 * @param array $settings
295 * An array of sitemap settings for this bundle/entity type.
296 * Example: ['index' => TRUE, 'priority' => 0.5].
300 public function setBundleSettings($entity_type_id, $bundle_name = NULL, $settings) {
302 $bundle_name = empty($bundle_name) ? $entity_type_id : $bundle_name;
304 foreach($settings as $setting_key => $setting) {
305 if ($setting_key == 'index') {
306 $setting = intval($setting);
309 ->getEditable("simple_sitemap.bundle_settings.$entity_type_id.$bundle_name")
310 ->set($setting_key, $setting)
313 //todo: Use addLinkSettings()?
315 // Delete entity overrides which are identical to new bundle setting.
316 $sitemap_entity_types = $this->entityHelper->getSitemapEntityTypes();
317 if (isset($sitemap_entity_types[$entity_type_id])) {
318 $entity_type = $sitemap_entity_types[$entity_type_id];
319 $keys = $entity_type->getKeys();
322 $keys['bundle'] = $entity_type_id == 'menu_link_content' ? 'menu_name' : $keys['bundle'];
324 $query = $this->entityQuery->get($entity_type_id);
325 if (!$this->entityHelper->entityTypeIsAtomic($entity_type_id)) {
326 $query->condition($keys['bundle'], $bundle_name);
328 $entity_ids = $query->execute();
330 $bundle_settings = $this->configFactory
331 ->get("simple_sitemap.bundle_settings.$entity_type_id.$bundle_name");
333 $query = $this->db->select('simple_sitemap_entity_overrides', 'o')
334 ->fields('o', ['id', 'inclusion_settings'])
335 ->condition('o.entity_type', $entity_type_id);
336 if (!empty($entity_ids)) {
337 $query->condition('o.entity_id', $entity_ids, 'IN');
340 foreach($query->execute()->fetchAll() as $result) {
342 $instance_settings = unserialize($result->inclusion_settings);
343 foreach ($instance_settings as $setting_key => $instance_setting) {
344 if ($instance_setting != $bundle_settings->get($setting_key)) {
350 $this->db->delete('simple_sitemap_entity_overrides')
351 ->condition('id', $result->id)
363 * Gets sitemap settings for an entity bundle, a non-bundle entity type or for
364 * all entity types and their bundles.
366 * @param string|null $entity_type_id
367 * If set to null, sitemap settings for all entity types and their bundles
369 * @param string|null $bundle_name
371 * @return array|false
372 * Array of sitemap settings for an entity bundle, a non-bundle entity type
373 * or for all entity types and their bundles.
374 * False if entity type does not exist.
376 public function getBundleSettings($entity_type_id = NULL, $bundle_name = NULL) {
377 if (NULL !== $entity_type_id) {
378 $bundle_name = empty($bundle_name) ? $entity_type_id : $bundle_name;
379 $settings = $this->configFactory
380 ->get("simple_sitemap.bundle_settings.$entity_type_id.$bundle_name")
382 $bundle_settings = !empty($settings) ? $settings : FALSE;
385 $config_names = $this->configFactory->listAll("simple_sitemap.bundle_settings");
386 $bundle_settings = [];
387 foreach($config_names as $config_name) {
388 $config_name_parts = explode('.', $config_name);
389 $bundle_settings[$config_name_parts[2]][$config_name_parts[3]]
390 = $this->configFactory->get($config_name)->get();
393 return $bundle_settings;
397 * Overrides entity bundle/entity type sitemap settings for a single entity.
399 * @param string $entity_type_id
401 * @param array $settings
405 public function setEntityInstanceSettings($entity_type_id, $id, $settings) {
407 $entity = $this->entityTypeManager->getStorage($entity_type_id)->load($id);
408 $bundle_name = $this->entityHelper->getEntityInstanceBundleName($entity);
409 $bundle_settings = $this->configFactory
410 ->get("simple_sitemap.bundle_settings.$entity_type_id.$bundle_name")
413 if (!empty($bundle_settings)) {
415 // Check if overrides are different from bundle setting before saving.
417 foreach ($settings as $key => $setting) {
418 if ($setting != $bundle_settings[$key]) {
423 // Save overrides for this entity if something is different.
425 $this->db->merge('simple_sitemap_entity_overrides')
427 'entity_type' => $entity_type_id,
430 'entity_type' => $entity_type_id,
432 'inclusion_settings' => serialize($settings),
436 // Else unset override.
438 $this->removeEntityInstanceSettings($entity_type_id, $id);
448 * Gets sitemap settings for an entity instance which overrides the sitemap
449 * settings of its bundle, or bundle settings, if they are not overridden.
451 * @param string $entity_type_id
454 * @return array|false
456 public function getEntityInstanceSettings($entity_type_id, $id) {
457 $results = $this->db->select('simple_sitemap_entity_overrides', 'o')
458 ->fields('o', ['inclusion_settings'])
459 ->condition('o.entity_type', $entity_type_id)
460 ->condition('o.entity_id', $id)
464 if (!empty($results)) {
465 return unserialize($results);
468 $entity = $this->entityTypeManager->getStorage($entity_type_id)
470 return $this->getBundleSettings(
472 $this->entityHelper->getEntityInstanceBundleName($entity)
478 * Removes sitemap settings for an entity that overrides the sitemap settings
481 * @param string $entity_type_id
482 * @param string|null $entity_ids
486 public function removeEntityInstanceSettings($entity_type_id, $entity_ids = NULL) {
487 $query = $this->db->delete('simple_sitemap_entity_overrides')
488 ->condition('entity_type', $entity_type_id);
489 if (NULL !== $entity_ids) {
490 $entity_ids = !is_array($entity_ids) ? [$entity_ids] : $entity_ids;
491 $query->condition('entity_id', $entity_ids, 'IN');
498 * Checks if an entity bundle (or a non-bundle entity type) is set to be
499 * indexed in the sitemap settings.
501 * @param string $entity_type_id
502 * @param string|null $bundle_name
506 public function bundleIsIndexed($entity_type_id, $bundle_name = NULL) {
507 $settings = $this->getBundleSettings($entity_type_id, $bundle_name);
508 return !empty($settings['index']);
512 * Checks if an entity type is enabled in the sitemap settings.
514 * @param string $entity_type_id
518 public function entityTypeIsEnabled($entity_type_id) {
519 return in_array($entity_type_id, $this->getSetting('enabled_entity_types', []));
523 * Stores a custom path along with its sitemap settings to configuration.
525 * @param string $path
526 * @param array $settings
530 public function addCustomLink($path, $settings) {
531 if (!$this->pathValidator->isValid($path)) {
535 if ($path[0] != '/') {
540 $custom_links = $this->getCustomLinks();
541 foreach ($custom_links as $key => $link) {
542 if ($link['path'] == $path) {
547 $link_key = isset($link_key) ? $link_key : count($custom_links);
548 $custom_links[$link_key]['path'] = $path;
549 $this->addLinkSettings('custom', $settings, $custom_links[$link_key]); //todo: dirty
550 $this->configFactory->getEditable("simple_sitemap.custom")
551 ->set('links', $custom_links)->save();
558 protected function addLinkSettings($type, $settings, &$target) {
559 foreach ($settings as $setting_key => $setting) {
560 if (in_array($setting_key, self::$allowed_link_settings[$type])) {
561 switch ($setting_key) {
563 if (!FormHelper::isValidPriority($setting)) {
569 // todo: add index check.
571 $target[$setting_key] = $setting;
577 * Returns an array of custom paths and their sitemap settings.
581 public function getCustomLinks() {
582 $custom_links = $this->configFactory
583 ->get('simple_sitemap.custom')
585 return $custom_links !== NULL ? $custom_links : [];
589 * Returns settings for a custom path added to the sitemap settings.
591 * @param string $path
593 * @return array|false
595 public function getCustomLink($path) {
596 foreach ($this->getCustomLinks() as $key => $link) {
597 if ($link['path'] == $path) {
605 * Removes a custom path from the sitemap settings.
607 * @param string $path
611 public function removeCustomLink($path) {
612 $custom_links = $this->getCustomLinks();
613 foreach ($custom_links as $key => $link) {
614 if ($link['path'] == $path) {
615 unset($custom_links[$key]);
616 $custom_links = array_values($custom_links);
617 $this->configFactory->getEditable("simple_sitemap.custom")
618 ->set('links', $custom_links)->save();
626 * Removes all custom paths from the sitemap settings.
630 public function removeCustomLinks() {
631 $this->configFactory->getEditable("simple_sitemap.custom")
632 ->set('links', [])->save();