Simplesitemap.php 20.6 KB
Newer Older
1 2
<?php

Pawel G's avatar
Pawel G committed
3
namespace Drupal\simple_sitemap;
4

Pawel G's avatar
Pawel G committed
5 6 7
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Path\PathValidator;
Pawel G's avatar
Pawel G committed
8 9
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Datetime\DateFormatter;
10
use Drupal\Component\Datetime\Time;
11 12
use Drupal\simple_sitemap\Batch\Batch;
use Drupal\Core\Extension\ModuleHandler;
13

14
/**
Pawel G's avatar
Pawel G committed
15
 * Class Simplesitemap
Pawel G's avatar
Pawel G committed
16
 * @package Drupal\simple_sitemap
17 18 19
 */
class Simplesitemap {

Pawel G's avatar
Pawel G committed
20 21 22
  /**
   * @var \Drupal\simple_sitemap\SitemapGenerator
   */
23
  protected $sitemapGenerator;
Pawel G's avatar
Pawel G committed
24 25 26 27

  /**
   * @var \Drupal\simple_sitemap\EntityHelper
   */
28
  protected $entityHelper;
Pawel G's avatar
Pawel G committed
29 30 31 32

  /**
   * @var \Drupal\Core\Config\ConfigFactory
   */
33
  protected $configFactory;
Pawel G's avatar
Pawel G committed
34 35 36 37

  /**
   * @var \Drupal\Core\Database\Connection
   */
38
  protected $db;
Pawel G's avatar
Pawel G committed
39 40 41 42

  /**
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
43
  protected $entityTypeManager;
Pawel G's avatar
Pawel G committed
44 45 46 47

  /**
   * @var \Drupal\Core\Path\PathValidator
   */
48
  protected $pathValidator;
Pawel G's avatar
Pawel G committed
49

50 51 52 53 54
  /**
   * @var \Drupal\Core\Datetime\DateFormatter
   */
  protected $dateFormatter;

55 56 57 58 59
  /**
   * @var \Drupal\Component\Datetime\Time
   */
  protected $time;

60 61 62 63 64 65 66 67 68 69
  /**
   * @var \Drupal\simple_sitemap\Batch\Batch
   */
  protected $batch;

  /**
   * @var \Drupal\Core\Extension\ModuleHandler
   */
  protected $moduleHandler;

Pawel G's avatar
Pawel G committed
70 71 72
  /**
   * @var array
   */
Pawel G's avatar
Pawel G committed
73
  protected static $allowedLinkSettings = [
74
    'entity' => ['index', 'priority', 'changefreq', 'include_images'],
75 76 77
    'custom' => ['priority', 'changefreq'],
  ];

Pawel G's avatar
Pawel G committed
78 79 80 81
  /**
   * @var array
   */
  protected static $linkSettingDefaults = [
82
    'index' => 1,
83
    'priority' => 0.5,
84
    'changefreq' => '',
85
    'include_images' => 0,
86
  ];
87

88 89 90 91 92 93
  protected static $generatorServices = [
    'simple_sitemap.custom_url_generator',
    'simple_sitemap.entity_url_generator',
    'simple_sitemap.arbitrary_url_generator',
  ];

94 95
  /**
   * Simplesitemap constructor.
Pawel G's avatar
Pawel G committed
96
   * @param \Drupal\simple_sitemap\SitemapGenerator $sitemapGenerator
97
   * @param \Drupal\simple_sitemap\EntityHelper $entityHelper
Pawel G's avatar
Pawel G committed
98 99 100 101 102
   * @param \Drupal\Core\Config\ConfigFactory $configFactory
   * @param \Drupal\Core\Database\Connection $database
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   * @param \Drupal\Core\Path\PathValidator $pathValidator
   * @param \Drupal\Core\Datetime\DateFormatter $dateFormatter
103
   * @param \Drupal\Component\Datetime\Time $time
104 105
   * @param \Drupal\simple_sitemap\Batch\Batch $batch
   * @param \Drupal\Core\Extension\ModuleHandler $module_handler
106
   */
107
  public function __construct(
Pawel G's avatar
Pawel G committed
108
    SitemapGenerator $sitemapGenerator,
109
    EntityHelper $entityHelper,
Pawel G's avatar
Pawel G committed
110
    ConfigFactory $configFactory,
Pawel G's avatar
Pawel G committed
111 112 113
    Connection $database,
    EntityTypeManagerInterface $entityTypeManager,
    PathValidator $pathValidator,
114
    DateFormatter $dateFormatter,
115 116 117
    Time $time,
    Batch $batch,
    ModuleHandler $module_handler
118 119
  ) {
    $this->sitemapGenerator = $sitemapGenerator;
120
    $this->entityHelper = $entityHelper;
Pawel G's avatar
Pawel G committed
121
    $this->configFactory = $configFactory;
122 123
    $this->db = $database;
    $this->entityTypeManager = $entityTypeManager;
124
    $this->pathValidator = $pathValidator;
125
    $this->dateFormatter = $dateFormatter;
126
    $this->time = $time;
127 128
    $this->batch = $batch;
    $this->moduleHandler = $module_handler;
129 130
  }

131
  /**
132 133
   * Returns a specific sitemap setting or a default value if setting does not
   * exist.
Pawel G's avatar
Pawel G committed
134
   *
135 136 137 138 139 140 141 142
   * @param string $name
   *   Name of the setting, like 'max_links'.
   *
   * @param mixed $default
   *   Value to be returned if the setting does not exist in the configuration.
   *
   * @return mixed
   *   The current setting from configuration or a default value.
Pawel G's avatar
Pawel G committed
143
   */
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
  public function getSetting($name, $default = FALSE) {
    $setting = $this->configFactory
      ->get('simple_sitemap.settings')
      ->get($name);
    return NULL !== $setting ? $setting : $default;
  }

  /**
   * Stores a specific sitemap setting in configuration.
   *
   * @param string $name
   *   Setting name, like 'max_links'.
   * @param mixed $setting
   *   The setting to be saved.
   *
   * @return $this
   */
  public function saveSetting($name, $setting) {
    $this->configFactory->getEditable("simple_sitemap.settings")
      ->set($name, $setting)->save();
    return $this;
  }

  /**
   * Returns the whole sitemap, a requested sitemap chunk,
   * or the sitemap index file.
   *
   * @param int $chunk_id
   *
   * @return string|false
   *   If no sitemap id provided, either a sitemap index is returned, or the
   *   whole sitemap, if the amount of links does not exceed the max links
   *   setting. If a sitemap id is provided, a sitemap chunk is returned. False
   *   if sitemap is not retrievable from the database.
   */
  public function getSitemap($chunk_id = NULL) {
    $chunk_info = $this->fetchSitemapChunkInfo();

    if (NULL === $chunk_id || !isset($chunk_info[$chunk_id])) {

      if (count($chunk_info) > 1) {
        // Return sitemap index, if there are multiple sitemap chunks.
        return $this->getSitemapIndex($chunk_info);
      }
      else {
        // Return sitemap if there is only one chunk.
        return count($chunk_info) === 1
        && isset($chunk_info[SitemapGenerator::FIRST_CHUNK_INDEX])
          ? $this->fetchSitemapChunk(SitemapGenerator::FIRST_CHUNK_INDEX)
            ->sitemap_string
          : FALSE;
      }
    }
    else {
      // Return specific sitemap chunk.
      return $this->fetchSitemapChunk($chunk_id)->sitemap_string;
    }
  }

  /**
   * Fetches all sitemap chunk timestamps keyed by chunk ID.
   *
   * @return array
   *   An array containing chunk creation timestamps keyed by chunk ID.
   */
  protected function fetchSitemapChunkInfo() {
210
    return $this->db
211
      ->query("SELECT id, sitemap_created FROM {simple_sitemap}")
212 213 214
      ->fetchAllAssoc('id');
  }

215 216 217 218 219 220 221 222 223
  /**
   * Fetches a single sitemap chunk by ID.
   *
   * @param int $id
   *   The chunk ID.
   *
   * @return object
   *   A sitemap chunk object.
   */
224
  protected function fetchSitemapChunk($id) {
225 226 227 228 229
    return $this->db->query('SELECT * FROM {simple_sitemap} WHERE id = :id',
      [':id' => $id])->fetchObject();
  }

  /**
Pawel G's avatar
Pawel G committed
230
   * Generates the XML sitemap and saves it to the db.
231 232 233 234 235 236
   *
   * @param string $from
   *   Can be 'form', 'cron', 'drush' or 'nobatch'.
   *   This decides how the batch process is to be run.
   */
  public function generateSitemap($from = 'form') {
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254

    $this->batch->setBatchInfo([
      'base_url' => $this->getSetting('base_url', ''),
      'batch_process_limit' => $this->getSetting('batch_process_limit', NULL),
      'max_links' => $this->getSetting('max_links', 2000),
      'skip_untranslated' => $this->getSetting('skip_untranslated', FALSE),
      'remove_duplicates' => $this->getSetting('remove_duplicates', TRUE),
      'excluded_languages' => $this->getSetting('excluded_languages', []),
      'from' => $from,
    ]);

    $this->moduleHandler->alter('simple_sitemap_generator_services', self::$generatorServices);

    foreach (self::$generatorServices as $service) {
      $this->batch->addOperation($service);
    }

    $this->batch->start();
255 256 257 258 259
  }

  /**
   * Generates and returns the sitemap index as string.
   *
Pawel G's avatar
Pawel G committed
260 261
   * @param array $chunk_info
   *   Array containing chunk creation timestamps keyed by chunk ID.
262 263 264
   *
   * @return string
   *   The sitemap index.
265 266
   *
   * @todo Need to make sure response is cached.
267
   */
Pawel G's avatar
Pawel G committed
268
  protected function getSitemapIndex($chunk_info) {
269
    return $this->sitemapGenerator
Pawel G's avatar
Pawel G committed
270
      ->setSettings(['base_url' => $this->getSetting('base_url', '')])
Pawel G's avatar
Pawel G committed
271
      ->generateSitemapIndex($chunk_info);
272 273 274 275 276 277 278 279 280 281 282 283
  }

  /**
   * Returns a 'time ago' string of last timestamp generation.
   *
   * @return string|false
   *   Formatted timestamp of last sitemap generation, otherwise FALSE.
   */
  public function getGeneratedAgo() {
    $chunks = $this->fetchSitemapChunkInfo();
    if (isset($chunks[SitemapGenerator::FIRST_CHUNK_INDEX]->sitemap_created)) {
      return $this->dateFormatter
284
        ->formatInterval($this->time->getRequestTime() - $chunks[SitemapGenerator::FIRST_CHUNK_INDEX]
285 286 287 288 289
            ->sitemap_created);
    }
    return FALSE;
  }

290 291
  /**
   * Enables sitemap support for an entity type. Enabled entity types show
292 293
   * sitemap settings on their bundle setting forms. If an enabled entity type
   * features bundles (e.g. 'node'), it needs to be set up with
294 295 296
   * setBundleSettings() as well.
   *
   * @param string $entity_type_id
Pawel G's avatar
Pawel G committed
297
   *   Entity type id like 'node'.
298
   *
Pawel G's avatar
Pawel G committed
299
   * @return $this
300 301
   */
  public function enableEntityType($entity_type_id) {
302 303 304 305
    $enabled_entity_types = $this->getSetting('enabled_entity_types');
    if (!in_array($entity_type_id, $enabled_entity_types)) {
      $enabled_entity_types[] = $entity_type_id;
      $this->saveSetting('enabled_entity_types', $enabled_entity_types);
306
    }
Pawel G's avatar
Pawel G committed
307
    return $this;
308 309 310 311 312 313 314 315
  }

  /**
   * Disables sitemap support for an entity type. Disabling support for an
   * entity type deletes its sitemap settings permanently and removes sitemap
   * settings from entity forms.
   *
   * @param string $entity_type_id
316
   *  Entity type id like 'node'.
317
   *
Pawel G's avatar
Pawel G committed
318
   * @return $this
319 320
   */
  public function disableEntityType($entity_type_id) {
321 322 323

    // Updating settings.
    $enabled_entity_types = $this->getSetting('enabled_entity_types');
324
    if (FALSE !== ($key = array_search($entity_type_id, $enabled_entity_types))) {
325
      unset ($enabled_entity_types[$key]);
326
      $this->saveSetting('enabled_entity_types', array_values($enabled_entity_types));
327 328 329
    }

    // Deleting inclusion settings.
330
    $config_names = $this->configFactory->listAll("simple_sitemap.bundle_settings.$entity_type_id.");
331 332
    foreach($config_names as $config_name) {
      $this->configFactory->getEditable($config_name)->delete();
333
    }
334 335 336

    // Deleting entity overrides.
    $this->removeEntityInstanceSettings($entity_type_id);
Pawel G's avatar
Pawel G committed
337
    return $this;
338 339 340
  }

  /**
Pawel G's avatar
Pawel G committed
341
   * Sets sitemap settings for a non-bundle entity type (e.g. user) or a bundle
342 343 344
   * of an entity type (e.g. page).
   *
   * @param string $entity_type_id
Pawel G's avatar
Pawel G committed
345
   *   Entity type id like 'node' the bundle belongs to.
346
   * @param string $bundle_name
Pawel G's avatar
Pawel G committed
347
   *   Name of the bundle. NULL if entity type has no bundles.
348
   * @param array $settings
Pawel G's avatar
Pawel G committed
349
   *   An array of sitemap settings for this bundle/entity type.
350
   *   Example: ['index' => TRUE, 'priority' => 0.5, 'changefreq' => 'never', 'include_images' => FALSE].
Pawel G's avatar
Pawel G committed
351 352
   *
   * @return $this
353 354
   *
   * @todo: enableEntityType automatically
355
   */
356
  public function setBundleSettings($entity_type_id, $bundle_name = NULL, $settings = []) {
357
    $bundle_name = empty($bundle_name) ? $entity_type_id : $bundle_name;
358

359 360 361 362 363
    $old_settings = $this->getBundleSettings($entity_type_id, $bundle_name);
    $settings = !empty($old_settings) ? array_merge($old_settings, $settings) : $this->supplementDefaultSettings('entity', $settings);

    $bundle_settings = $this->configFactory
      ->getEditable("simple_sitemap.bundle_settings.$entity_type_id.$bundle_name");
364
    foreach($settings as $setting_key => $setting) {
365
      if ($setting_key === 'index') {
366 367
        $setting = intval($setting);
      }
368
      $bundle_settings->set($setting_key, $setting);
369
    }
370
    $bundle_settings->save();
371 372

    // Delete entity overrides which are identical to new bundle setting.
Pawel G's avatar
Pawel G committed
373
    $sitemap_entity_types = $this->entityHelper->getSupportedEntityTypes();
374 375 376
    if (isset($sitemap_entity_types[$entity_type_id])) {
      $entity_type = $sitemap_entity_types[$entity_type_id];
      $keys = $entity_type->getKeys();
377 378

      // Menu fix.
379 380
      $keys['bundle'] = $entity_type_id == 'menu_link_content' ? 'menu_name' : $keys['bundle'];

381
      $query = $this->entityTypeManager->getStorage($entity_type_id)->getQuery();
382
      if (!$this->entityHelper->entityTypeIsAtomic($entity_type_id)) {
383
        $query->condition($keys['bundle'], $bundle_name);
384
      }
385 386 387 388 389 390 391 392 393
      $entity_ids = $query->execute();

      $query = $this->db->select('simple_sitemap_entity_overrides', 'o')
        ->fields('o', ['id', 'inclusion_settings'])
        ->condition('o.entity_type', $entity_type_id);
      if (!empty($entity_ids)) {
        $query->condition('o.entity_id', $entity_ids, 'IN');
      }

394
      $delete_instances = [];
395
      foreach($query->execute()->fetchAll() as $result) {
396 397 398
        $delete = TRUE;
        $instance_settings = unserialize($result->inclusion_settings);
        foreach ($instance_settings as $setting_key => $instance_setting) {
399
          if ($instance_setting != $settings[$setting_key]) {
400 401 402 403 404
            $delete = FALSE;
            break;
          }
        }
        if ($delete) {
405
          $delete_instances[] = $result->id;
406
        }
407
      }
408 409 410 411 412
      if (!empty($delete_instances)) {
        $this->db->delete('simple_sitemap_entity_overrides')
          ->condition('id', $delete_instances, 'IN')
          ->execute();
      }
413
    }
414 415 416
    else {
      //todo: log error
    }
Pawel G's avatar
Pawel G committed
417
    return $this;
418 419
  }

420
  /**
421 422
   * Gets sitemap settings for an entity bundle, a non-bundle entity type or for
   * all entity types and their bundles.
423
   *
424 425 426
   * @param string|null $entity_type_id
   *  If set to null, sitemap settings for all entity types and their bundles
   *  are fetched.
427 428 429
   * @param string|null $bundle_name
   *
   * @return array|false
Pawel G's avatar
Pawel G committed
430 431
   *  Array of sitemap settings for an entity bundle, a non-bundle entity type
   *  or for all entity types and their bundles.
432
   *  False if entity type does not exist.
433
   */
434
  public function getBundleSettings($entity_type_id = NULL, $bundle_name = NULL) {
435
    if (NULL !== $entity_type_id) {
436
      $bundle_name = empty($bundle_name) ? $entity_type_id : $bundle_name;
437
      $bundle_settings = $this->configFactory
438 439
        ->get("simple_sitemap.bundle_settings.$entity_type_id.$bundle_name")
        ->get();
440
      return !empty($bundle_settings) ? $bundle_settings : FALSE;
441
    }
442
    else {
443
      $config_names = $this->configFactory->listAll("simple_sitemap.bundle_settings.");
444
      $all_settings = [];
445 446
      foreach($config_names as $config_name) {
        $config_name_parts = explode('.', $config_name);
447
        $all_settings[$config_name_parts[2]][$config_name_parts[3]] = $this->configFactory->get($config_name)->get();
448 449 450 451 452 453
      }
      return $all_settings;
    }
  }

  /**
454 455
   * Supplements all missing link setting with default values.
   *
456 457 458 459 460
   * @param string $type
   * @param array $settings
   * @return array
   */
  protected function supplementDefaultSettings($type, $settings) {
Pawel G's avatar
Pawel G committed
461
    foreach(self::$allowedLinkSettings[$type] as $allowed_link_setting) {
462
      if (!isset($settings[$allowed_link_setting])
Pawel G's avatar
Pawel G committed
463 464
        && isset(self::$linkSettingDefaults[$allowed_link_setting])) {
        $settings[$allowed_link_setting] = self::$linkSettingDefaults[$allowed_link_setting];
465 466
      }
    }
467
    return $settings;
468 469
  }

Pawel G's avatar
Pawel G committed
470 471 472
  /**
   * Overrides entity bundle/entity type sitemap settings for a single entity.
   *
Pawel G's avatar
Pawel G committed
473 474 475
   * @param string $entity_type_id
   * @param int $id
   * @param array $settings
Pawel G's avatar
Pawel G committed
476
   *
Pawel G's avatar
Pawel G committed
477 478
   * @return $this
   */
479
  public function setEntityInstanceSettings($entity_type_id, $id, $settings) {
480
    $entity = $this->entityTypeManager->getStorage($entity_type_id)->load($id);
481 482 483
    $bundle_settings = $this->getBundleSettings(
      $entity_type_id, $this->entityHelper->getEntityInstanceBundleName($entity)
    );
484
    if (!empty($bundle_settings)) {
485 486 487 488

      // Check if overrides are different from bundle setting before saving.
      $override = FALSE;
      foreach ($settings as $key => $setting) {
489
        if (!isset($bundle_settings[$key]) || $setting != $bundle_settings[$key]) {
490 491 492 493
          $override = TRUE;
          break;
        }
      }
Pawel G's avatar
Pawel G committed
494 495
      // Save overrides for this entity if something is different.
      if ($override) {
496 497 498 499 500 501 502
        $this->db->merge('simple_sitemap_entity_overrides')
          ->key([
            'entity_type' => $entity_type_id,
            'entity_id' => $id])
          ->fields([
            'entity_type' => $entity_type_id,
            'entity_id' => $id,
503
            'inclusion_settings' => serialize(array_merge($bundle_settings, $settings)),])
504
          ->execute();
505
      }
Pawel G's avatar
Pawel G committed
506 507
      // Else unset override.
      else {
508
        $this->removeEntityInstanceSettings($entity_type_id, $id);
509
      }
510 511 512
    }
    else {
      //todo: log error
513
    }
Pawel G's avatar
Pawel G committed
514
    return $this;
515 516
  }

Pawel G's avatar
Pawel G committed
517
  /**
518
   * Gets sitemap settings for an entity instance which overrides the sitemap
519
   * settings of its bundle, or bundle settings, if they are not overridden.
Pawel G's avatar
Pawel G committed
520
   *
Pawel G's avatar
Pawel G committed
521
   * @param string $entity_type_id
522
   * @param int $id
Pawel G's avatar
Pawel G committed
523
   *
524
   * @return array|false
Pawel G's avatar
Pawel G committed
525
   */
526 527 528 529 530 531 532 533 534
  public function getEntityInstanceSettings($entity_type_id, $id) {
    $results = $this->db->select('simple_sitemap_entity_overrides', 'o')
      ->fields('o', ['inclusion_settings'])
      ->condition('o.entity_type', $entity_type_id)
      ->condition('o.entity_id', $id)
      ->execute()
      ->fetchField();

    if (!empty($results)) {
535
      return unserialize($results);
536 537
    }
    else {
538 539 540 541 542 543
      $entity = $this->entityTypeManager->getStorage($entity_type_id)
        ->load($id);
      return $this->getBundleSettings(
        $entity_type_id,
        $this->entityHelper->getEntityInstanceBundleName($entity)
      );
544 545 546
    }
  }

547 548 549 550 551 552 553 554 555 556 557 558
  /**
   * Removes sitemap settings for an entity that overrides the sitemap settings
   * of its bundle.
   *
   * @param string $entity_type_id
   * @param string|null $entity_ids
   *
   * @return $this
   */
  public function removeEntityInstanceSettings($entity_type_id, $entity_ids = NULL) {
    $query = $this->db->delete('simple_sitemap_entity_overrides')
      ->condition('entity_type', $entity_type_id);
559
    if (NULL !== $entity_ids) {
560 561 562 563 564 565 566
      $entity_ids = !is_array($entity_ids) ? [$entity_ids] : $entity_ids;
      $query->condition('entity_id', $entity_ids, 'IN');
    }
    $query->execute();
    return $this;
  }

Pawel G's avatar
Pawel G committed
567 568 569 570
  /**
   * Checks if an entity bundle (or a non-bundle entity type) is set to be
   * indexed in the sitemap settings.
   *
571 572
   * @param string $entity_type_id
   * @param string|null $bundle_name
Pawel G's avatar
Pawel G committed
573
   *
Pawel G's avatar
Pawel G committed
574 575
   * @return bool
   */
576
  public function bundleIsIndexed($entity_type_id, $bundle_name = NULL) {
577 578 579 580
    $settings = $this->getBundleSettings($entity_type_id, $bundle_name);
    return !empty($settings['index']);
  }

Pawel G's avatar
Pawel G committed
581 582 583
  /**
   * Checks if an entity type is enabled in the sitemap settings.
   *
584
   * @param string $entity_type_id
Pawel G's avatar
Pawel G committed
585
   *
Pawel G's avatar
Pawel G committed
586 587
   * @return bool
   */
588
  public function entityTypeIsEnabled($entity_type_id) {
589
    return in_array($entity_type_id, $this->getSetting('enabled_entity_types', []));
590 591
  }

Pawel G's avatar
Pawel G committed
592
  /**
593
   * Stores a custom path along with its sitemap settings to configuration.
Pawel G's avatar
Pawel G committed
594
   *
Pawel G's avatar
Pawel G committed
595 596
   * @param string $path
   * @param array $settings
Pawel G's avatar
Pawel G committed
597
   *
Pawel G's avatar
Pawel G committed
598
   * @return $this
599 600
   *
   * @todo Validate $settings and throw exceptions
Pawel G's avatar
Pawel G committed
601
   */
602
  public function addCustomLink($path, $settings = []) {
Pawel G's avatar
Pawel G committed
603 604 605 606 607 608 609 610
    if (!$this->pathValidator->isValid($path)) {
      // todo: log error.
      return $this;
    }
    if ($path[0] != '/') {
      // todo: log error.
      return $this;
    }
Pawel G's avatar
Pawel G committed
611

612
    $custom_links = $this->getCustomLinks(FALSE);
Pawel G's avatar
Pawel G committed
613
    foreach ($custom_links as $key => $link) {
614
      if ($link['path'] === $path) {
615 616 617 618 619
        $link_key = $key;
        break;
      }
    }
    $link_key = isset($link_key) ? $link_key : count($custom_links);
620
    $custom_links[$link_key] = ['path' => $path] + $settings;
621
    $this->configFactory->getEditable("simple_sitemap.custom")
622
      ->set('links', $custom_links)->save();
Pawel G's avatar
Pawel G committed
623
    return $this;
624 625
  }

626 627 628
  /**
   * Returns an array of custom paths and their sitemap settings.
   *
629
   * @param bool $supplement_default_settings
630 631
   * @return array
   */
632
  public function getCustomLinks($supplement_default_settings = TRUE) {
633 634
    $custom_links = $this->configFactory
      ->get('simple_sitemap.custom')
635
      ->get('links');
636 637 638 639 640 641 642

    if ($supplement_default_settings) {
      foreach ($custom_links as $i => $link_settings) {
        $custom_links[$i] = $this->supplementDefaultSettings('custom', $link_settings);
      }
    }

643
    return $custom_links !== NULL ? $custom_links : [];
644 645
  }

Pawel G's avatar
Pawel G committed
646 647 648
  /**
   * Returns settings for a custom path added to the sitemap settings.
   *
Pawel G's avatar
Pawel G committed
649
   * @param string $path
Pawel G's avatar
Pawel G committed
650
   *
Pawel G's avatar
Pawel G committed
651
   * @return array|false
Pawel G's avatar
Pawel G committed
652
   */
653
  public function getCustomLink($path) {
654
    foreach ($this->getCustomLinks() as $key => $link) {
655
      if ($link['path'] === $path) {
656
        return $link;
657 658 659 660 661
      }
    }
    return FALSE;
  }

Pawel G's avatar
Pawel G committed
662 663 664
  /**
   * Removes a custom path from the sitemap settings.
   *
Pawel G's avatar
Pawel G committed
665
   * @param string $path
Pawel G's avatar
Pawel G committed
666
   *
Pawel G's avatar
Pawel G committed
667 668
   * @return $this
   */
669
  public function removeCustomLink($path) {
670
    $custom_links = $this->getCustomLinks(FALSE);
Pawel G's avatar
Pawel G committed
671
    foreach ($custom_links as $key => $link) {
672
      if ($link['path'] === $path) {
673 674
        unset($custom_links[$key]);
        $custom_links = array_values($custom_links);
675
        $this->configFactory->getEditable("simple_sitemap.custom")
676
          ->set('links', $custom_links)->save();
677
        break;
678 679
      }
    }
Pawel G's avatar
Pawel G committed
680
    return $this;
681 682
  }

Pawel G's avatar
Pawel G committed
683 684
  /**
   * Removes all custom paths from the sitemap settings.
Pawel G's avatar
Pawel G committed
685 686
   *
   * @return $this
Pawel G's avatar
Pawel G committed
687
   */
688
  public function removeCustomLinks() {
689
    $this->configFactory->getEditable("simple_sitemap.custom")
690
      ->set('links', [])->save();
Pawel G's avatar
Pawel G committed
691
    return $this;
692
  }
693
}