Simplesitemap.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  1. <?php
  2. namespace Drupal\simple_sitemap;
  3. use Drupal\Core\Database\Connection;
  4. use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
  5. use Drupal\Core\Entity\EntityTypeManagerInterface;
  6. use Drupal\simple_sitemap\Queue\QueueWorker;
  7. use Drupal\Core\Path\PathValidator;
  8. use Drupal\Core\Config\ConfigFactory;
  9. use Drupal\Core\Datetime\DateFormatter;
  10. use Drupal\Component\Datetime\Time;
  11. use Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorBase;
  12. /**
  13. * Class Simplesitemap
  14. * @package Drupal\simple_sitemap
  15. */
  16. class Simplesitemap {
  17. /**
  18. * @var \Drupal\simple_sitemap\EntityHelper
  19. */
  20. protected $entityHelper;
  21. /**
  22. * @var \Drupal\simple_sitemap\SimplesitemapSettings
  23. */
  24. protected $settings;
  25. /**
  26. * @var \Drupal\simple_sitemap\SimplesitemapManager
  27. */
  28. protected $manager;
  29. /**
  30. * @var \Drupal\Core\Config\ConfigFactory
  31. */
  32. protected $configFactory;
  33. /**
  34. * @var \Drupal\Core\Database\Connection
  35. */
  36. protected $db;
  37. /**
  38. * @var \Drupal\Core\Entity\EntityTypeManagerInterface
  39. */
  40. protected $entityTypeManager;
  41. /**
  42. * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
  43. */
  44. protected $entityTypeBundleInfo;
  45. /**
  46. * @var \Drupal\Core\Path\PathValidator
  47. */
  48. protected $pathValidator;
  49. /**
  50. * @var \Drupal\Core\Datetime\DateFormatter
  51. */
  52. protected $dateFormatter;
  53. /**
  54. * @var \Drupal\Component\Datetime\Time
  55. */
  56. protected $time;
  57. /**
  58. * @var \Drupal\simple_sitemap\Queue\QueueWorker
  59. */
  60. protected $queueWorker;
  61. /**
  62. * @var array
  63. */
  64. protected $variants;
  65. /**
  66. * @var array
  67. */
  68. protected static $allowedLinkSettings = [
  69. 'entity' => ['index', 'priority', 'changefreq', 'include_images'],
  70. 'custom' => ['priority', 'changefreq'],
  71. ];
  72. /**
  73. * @var array
  74. */
  75. protected static $linkSettingDefaults = [
  76. 'index' => FALSE,
  77. 'priority' => '0.5',
  78. 'changefreq' => '',
  79. 'include_images' => FALSE,
  80. ];
  81. /**
  82. * Simplesitemap constructor.
  83. * @param \Drupal\simple_sitemap\EntityHelper $entity_helper
  84. * @param \Drupal\simple_sitemap\SimplesitemapSettings $settings
  85. * @param \Drupal\simple_sitemap\SimplesitemapManager $manager
  86. * @param \Drupal\Core\Config\ConfigFactory $config_factory
  87. * @param \Drupal\Core\Database\Connection $database
  88. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
  89. * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
  90. * @param \Drupal\Core\Path\PathValidator $path_validator
  91. * @param \Drupal\Core\Datetime\DateFormatter $date_formatter
  92. * @param \Drupal\Component\Datetime\Time $time
  93. * @param \Drupal\simple_sitemap\Queue\QueueWorker $queue_worker
  94. */
  95. public function __construct(
  96. EntityHelper $entity_helper,
  97. SimplesitemapSettings $settings,
  98. SimplesitemapManager $manager,
  99. ConfigFactory $config_factory,
  100. Connection $database,
  101. EntityTypeManagerInterface $entity_type_manager,
  102. EntityTypeBundleInfoInterface $entity_type_bundle_info,
  103. PathValidator $path_validator,
  104. DateFormatter $date_formatter,
  105. Time $time,
  106. QueueWorker $queue_worker
  107. ) {
  108. $this->entityHelper = $entity_helper;
  109. $this->settings = $settings;
  110. $this->manager = $manager;
  111. $this->configFactory = $config_factory;
  112. $this->db = $database;
  113. $this->entityTypeManager = $entity_type_manager;
  114. $this->entityTypeBundleInfo = $entity_type_bundle_info;
  115. $this->pathValidator = $path_validator;
  116. $this->dateFormatter = $date_formatter;
  117. $this->time = $time;
  118. $this->queueWorker = $queue_worker;
  119. }
  120. /**
  121. * Returns a specific sitemap setting or a default value if setting does not
  122. * exist.
  123. *
  124. * @param string $name
  125. * Name of the setting, like 'max_links'.
  126. *
  127. * @param mixed $default
  128. * Value to be returned if the setting does not exist in the configuration.
  129. *
  130. * @return mixed
  131. * The current setting from configuration or a default value.
  132. */
  133. public function getSetting($name, $default = FALSE) {
  134. return $this->settings->getSetting($name, $default);
  135. }
  136. /**
  137. * Stores a specific sitemap setting in configuration.
  138. *
  139. * @param string $name
  140. * Setting name, like 'max_links'.
  141. *
  142. * @param mixed $setting
  143. * The setting to be saved.
  144. *
  145. * @return $this
  146. */
  147. public function saveSetting($name, $setting) {
  148. $this->settings->saveSetting($name, $setting);
  149. return $this;
  150. }
  151. /**
  152. * @return \Drupal\simple_sitemap\Queue\QueueWorker
  153. */
  154. public function getQueueWorker() {
  155. return $this->queueWorker;
  156. }
  157. /**
  158. * @return \Drupal\simple_sitemap\SimplesitemapManager
  159. */
  160. public function getSitemapManager() {
  161. return $this->manager;
  162. }
  163. /**
  164. * @param array|string|true|null $variants
  165. * array: Array of variants to be set.
  166. * string: A particular variant to be set.
  167. * null: Default variant will be set.
  168. * true: All existing variants will be set.
  169. *
  170. * @return $this
  171. */
  172. public function setVariants($variants = NULL) {
  173. if (NULL === $variants) {
  174. $this->variants = FALSE !== ($default_variant = $this->getSetting('default_variant')) ? [$default_variant] : [];
  175. }
  176. elseif ($variants === TRUE) {
  177. $this->variants = array_keys(
  178. $this->manager->getSitemapVariants(NULL, FALSE));
  179. }
  180. else {
  181. $this->variants = (array) $variants;
  182. }
  183. return $this;
  184. }
  185. /**
  186. * Gets the currently set variants, the default variant, or all variants.
  187. *
  188. * @param bool $default_get_all
  189. * If true and no variants are set, all variants are returned. If false and
  190. * no variants are set, only the default variant is returned.
  191. *
  192. * @return array
  193. */
  194. protected function getVariants($default_get_all = TRUE) {
  195. if (NULL === $this->variants) {
  196. $this->setVariants($default_get_all ? TRUE : NULL);
  197. }
  198. return $this->variants;
  199. }
  200. /**
  201. * Returns the whole sitemap, a requested sitemap chunk,
  202. * or the sitemap index file.
  203. *
  204. * @param int $delta
  205. *
  206. * @return string|false
  207. * If no sitemap delta is provided, either a sitemap index is returned, or the
  208. * whole sitemap variant, if the amount of links does not exceed the max
  209. * links setting. If a sitemap delta is provided, a sitemap chunk is returned.
  210. * Returns false if the sitemap is not retrievable from the database.
  211. */
  212. public function getSitemap($delta = NULL) {
  213. $chunk_info = $this->fetchSitemapVariantInfo();
  214. if (empty($delta) || !isset($chunk_info[$delta])) {
  215. if (isset($chunk_info[SitemapGeneratorBase::INDEX_DELTA])) {
  216. // Return sitemap index if one exists.
  217. return $this->fetchSitemapChunk($chunk_info[SitemapGeneratorBase::INDEX_DELTA]->id)
  218. ->sitemap_string;
  219. }
  220. else {
  221. // Return sitemap chunk if there is only one chunk.
  222. return isset($chunk_info[SitemapGeneratorBase::FIRST_CHUNK_DELTA])
  223. ? $this->fetchSitemapChunk($chunk_info[SitemapGeneratorBase::FIRST_CHUNK_DELTA]->id)
  224. ->sitemap_string
  225. : FALSE;
  226. }
  227. }
  228. else {
  229. // Return specific sitemap chunk.
  230. return $this->fetchSitemapChunk($chunk_info[$delta]->id)->sitemap_string;
  231. }
  232. }
  233. /**
  234. * Fetches info about all published sitemap variants and their chunks.
  235. *
  236. * @return array
  237. * An array containing all published sitemap chunk IDs, deltas and creation
  238. * timestamps keyed by the currently set variants, or in case of only one
  239. * variant set the above keyed by sitemap delta.
  240. */
  241. protected function fetchSitemapVariantInfo() {
  242. $result = $this->db->select('simple_sitemap', 's')
  243. ->fields('s', ['id', 'delta', 'sitemap_created', 'type'])
  244. ->condition('s.status', 1)
  245. ->condition('s.type', $this->getVariants(), 'IN')
  246. ->execute();
  247. return count($this->getVariants()) > 1
  248. ? $result->fetchAllAssoc('type')
  249. : $result->fetchAllAssoc('delta');
  250. }
  251. /**
  252. * Fetches a single sitemap chunk by ID.
  253. *
  254. * @param int $id
  255. * The chunk ID.
  256. *
  257. * @return object
  258. * A sitemap chunk object.
  259. */
  260. protected function fetchSitemapChunk($id) {
  261. return $this->db->query('SELECT * FROM {simple_sitemap} WHERE id = :id',
  262. [':id' => $id])->fetchObject();
  263. }
  264. /**
  265. * Removes sitemap instances for the currently set variants.
  266. *
  267. * @return $this
  268. * @throws \Drupal\Component\Plugin\Exception\PluginException
  269. */
  270. public function removeSitemap() {
  271. $this->manager->removeSitemap($this->getVariants(FALSE));
  272. return $this;
  273. }
  274. /**
  275. * Generates all sitemaps.
  276. *
  277. * @param string $from
  278. * Can be 'form', 'drush', 'cron' and 'backend'.
  279. *
  280. * @return $this
  281. *
  282. * @throws \Drupal\Component\Plugin\Exception\PluginException
  283. *
  284. * @todo Respect $this->variants and generate for specific variants.
  285. * @todo Implement lock functionality.
  286. */
  287. public function generateSitemap($from = 'form') {
  288. switch($from) {
  289. case 'form':
  290. case 'drush':
  291. $this->queueWorker->batchGenerateSitemap($from);
  292. break;
  293. case 'cron':
  294. case 'backend':
  295. $this->queueWorker->generateSitemap($from);
  296. break;
  297. }
  298. return $this;
  299. }
  300. /**
  301. * Rebuilds the queue for the currently set variants.
  302. *
  303. * @return $this
  304. * @throws \Drupal\Component\Plugin\Exception\PluginException
  305. */
  306. public function rebuildQueue() {
  307. $this->queueWorker->rebuildQueue($this->getVariants());
  308. return $this;
  309. }
  310. /**
  311. * Returns a 'time ago' string of last timestamp generation.
  312. *
  313. * @param string|null $variant
  314. *
  315. * @return string|array|false
  316. * Formatted timestamp of last sitemap generation, otherwise FALSE.
  317. */
  318. /* public function getGeneratedAgo() {
  319. $chunks = $this->fetchSitemapVariantInfo();
  320. return isset($chunks[DefaultSitemapGenerator::FIRST_CHUNK_DELTA]->sitemap_created)
  321. ? $this->dateFormatter
  322. ->formatInterval($this->time->getRequestTime() - $chunks[DefaultSitemapGenerator::FIRST_CHUNK_DELTA]
  323. ->sitemap_created)
  324. : FALSE;
  325. }*/
  326. /**
  327. * Enables sitemap support for an entity type. Enabled entity types show
  328. * sitemap settings on their bundle setting forms. If an enabled entity type
  329. * features bundles (e.g. 'node'), it needs to be set up with
  330. * setBundleSettings() as well.
  331. *
  332. * @param string $entity_type_id
  333. * Entity type id like 'node'.
  334. *
  335. * @return $this
  336. */
  337. public function enableEntityType($entity_type_id) {
  338. $enabled_entity_types = $this->getSetting('enabled_entity_types');
  339. if (!in_array($entity_type_id, $enabled_entity_types)) {
  340. $enabled_entity_types[] = $entity_type_id;
  341. $this->saveSetting('enabled_entity_types', $enabled_entity_types);
  342. }
  343. return $this;
  344. }
  345. /**
  346. * Disables sitemap support for an entity type. Disabling support for an
  347. * entity type deletes its sitemap settings permanently and removes sitemap
  348. * settings from entity forms.
  349. *
  350. * @param string $entity_type_id
  351. *
  352. * @return $this
  353. */
  354. public function disableEntityType($entity_type_id) {
  355. // Updating settings.
  356. $enabled_entity_types = $this->getSetting('enabled_entity_types');
  357. if (FALSE !== ($key = array_search($entity_type_id, $enabled_entity_types))) {
  358. unset ($enabled_entity_types[$key]);
  359. $this->saveSetting('enabled_entity_types', array_values($enabled_entity_types));
  360. }
  361. // Deleting inclusion settings.
  362. $config_names = $this->configFactory->listAll('simple_sitemap.bundle_settings.');
  363. foreach ($config_names as $config_name) {
  364. $config_name_parts = explode('.', $config_name);
  365. if ($config_name_parts[3] === $entity_type_id) {
  366. $this->configFactory->getEditable($config_name)->delete();
  367. }
  368. }
  369. // Deleting entity overrides.
  370. $this->setVariants(TRUE)->removeEntityInstanceSettings($entity_type_id);
  371. return $this;
  372. }
  373. /**
  374. * Sets settings for bundle or non-bundle entity types. This is done for the
  375. * currently set variant.
  376. *
  377. * @param $entity_type_id
  378. * @param null $bundle_name
  379. * @param array $settings
  380. *
  381. * @return $this
  382. *
  383. * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
  384. * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
  385. *
  386. * @todo: enableEntityType automatically
  387. * @todo multiple variants
  388. */
  389. public function setBundleSettings($entity_type_id, $bundle_name = NULL, $settings = ['index' => TRUE]) {
  390. if (empty($variants = $this->getVariants(FALSE))) {
  391. return $this;
  392. }
  393. $bundle_name = NULL !== $bundle_name ? $bundle_name : $entity_type_id;
  394. if (!empty($old_settings = $this->getBundleSettings($entity_type_id, $bundle_name))) {
  395. $settings = array_merge($old_settings, $settings);
  396. }
  397. self::supplementDefaultSettings('entity', $settings);
  398. if ($settings != $old_settings) {
  399. // Save new bundle settings to configuration.
  400. $bundle_settings = $this->configFactory
  401. ->getEditable("simple_sitemap.bundle_settings.$variants[0].$entity_type_id.$bundle_name");
  402. foreach ($settings as $setting_key => $setting) {
  403. $bundle_settings->set($setting_key, $setting);
  404. }
  405. $bundle_settings->save();
  406. // Delete entity overrides which are identical to new bundle settings.
  407. $entity_ids = $this->entityHelper->getEntityInstanceIds($entity_type_id, $bundle_name);
  408. $query = $this->db->select('simple_sitemap_entity_overrides', 'o')
  409. ->fields('o', ['id', 'inclusion_settings'])
  410. ->condition('o.entity_type', $entity_type_id)
  411. ->condition('o.type', $variants[0]);
  412. if (!empty($entity_ids)) {
  413. $query->condition('o.entity_id', $entity_ids, 'IN');
  414. }
  415. $delete_instances = [];
  416. foreach ($query->execute()->fetchAll() as $result) {
  417. $delete = TRUE;
  418. $instance_settings = unserialize($result->inclusion_settings);
  419. foreach ($instance_settings as $setting_key => $instance_setting) {
  420. if ($instance_setting != $settings[$setting_key]) {
  421. $delete = FALSE;
  422. break;
  423. }
  424. }
  425. if ($delete) {
  426. $delete_instances[] = $result->id;
  427. }
  428. }
  429. if (!empty($delete_instances)) {
  430. $this->db->delete('simple_sitemap_entity_overrides')
  431. ->condition('id', $delete_instances, 'IN')
  432. ->execute();
  433. }
  434. }
  435. return $this;
  436. }
  437. /**
  438. * Gets settings for bundle or non-bundle entity types. This is done for the
  439. * currently set variants.
  440. *
  441. * @param string|null $entity_type_id
  442. * Limit the result set to a specific entity type.
  443. *
  444. * @param string|null $bundle_name
  445. * Limit the result set to a specific bundle name.
  446. *
  447. * @param bool $supplement_defaults
  448. * Supplements the result set with default custom link settings.
  449. *
  450. * @param bool $multiple_variants
  451. * If true, returns an array of results keyed by variant name, otherwise it
  452. * returns the result set for the first variant only.
  453. *
  454. * @return array|false
  455. * Array of settings or array of settings keyed by variant name. False if
  456. * entity type does not exist.
  457. */
  458. public function getBundleSettings($entity_type_id = NULL, $bundle_name = NULL, $supplement_defaults = TRUE, $multiple_variants = FALSE) {
  459. $all_bundle_settings = [];
  460. foreach ($variants = $this->getVariants(FALSE) as $variant) {
  461. if (NULL !== $entity_type_id) {
  462. $bundle_name = NULL !== $bundle_name ? $bundle_name : $entity_type_id;
  463. $bundle_settings = $this->configFactory
  464. ->get("simple_sitemap.bundle_settings.$variant.$entity_type_id.$bundle_name")
  465. ->get();
  466. // If not found and entity type is enabled, return default bundle settings.
  467. if (empty($bundle_settings) && $supplement_defaults) {
  468. if ($this->entityTypeIsEnabled($entity_type_id)
  469. && isset($this->entityTypeBundleInfo->getBundleInfo($entity_type_id)[$bundle_name])) {
  470. self::supplementDefaultSettings('entity', $bundle_settings);
  471. }
  472. else {
  473. $bundle_settings = NULL;
  474. }
  475. }
  476. }
  477. else {
  478. $config_names = $this->configFactory->listAll("simple_sitemap.bundle_settings.$variant.");
  479. $bundle_settings = [];
  480. foreach ($config_names as $config_name) {
  481. $config_name_parts = explode('.', $config_name);
  482. $bundle_settings[$config_name_parts[3]][$config_name_parts[4]] = $this->configFactory->get($config_name)->get();
  483. }
  484. // Supplement default bundle settings for all bundles not found in simple_sitemap.bundle_settings.*.* configuration.
  485. if ($supplement_defaults) {
  486. foreach ($this->entityHelper->getSupportedEntityTypes() as $type_id => $type_definition) {
  487. if ($this->entityTypeIsEnabled($type_id)) {
  488. foreach($this->entityTypeBundleInfo->getBundleInfo($type_id) as $bundle => $bundle_definition) {
  489. if (!isset($bundle_settings[$type_id][$bundle])) {
  490. self::supplementDefaultSettings('entity', $bundle_settings[$type_id][$bundle]);
  491. }
  492. }
  493. }
  494. }
  495. }
  496. }
  497. if ($multiple_variants) {
  498. if (!empty($bundle_settings)) {
  499. $all_bundle_settings[$variant] = $bundle_settings;
  500. }
  501. }
  502. else {
  503. return $bundle_settings;
  504. }
  505. }
  506. return $all_bundle_settings;
  507. }
  508. /**
  509. * Removes settings for bundle or a non-bundle entity types. This is done for
  510. * the currently set variants.
  511. *
  512. * @param string|null $entity_type_id
  513. * Limit the removal to a specific entity type.
  514. *
  515. * @param string|null $bundle_name
  516. * Limit the removal to a specific bundle name.
  517. *
  518. * @return $this
  519. *
  520. * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
  521. * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
  522. */
  523. public function removeBundleSettings($entity_type_id = NULL, $bundle_name = NULL) {
  524. if (empty($variants = $this->getVariants(FALSE))) {
  525. return $this;
  526. }
  527. if (NULL !== $entity_type_id) {
  528. $bundle_name = NULL !== $bundle_name ? $bundle_name : $entity_type_id;
  529. foreach ($variants as $variant) {
  530. $this->configFactory
  531. ->getEditable("simple_sitemap.bundle_settings.$variant.$entity_type_id.$bundle_name")->delete();
  532. }
  533. $this->removeEntityInstanceSettings($entity_type_id, (
  534. empty($ids)
  535. ? NULL
  536. : $this->entityHelper->getEntityInstanceIds($entity_type_id, $bundle_name)
  537. ));
  538. }
  539. else {
  540. foreach ($variants as $variant) {
  541. $config_names = $this->configFactory->listAll("simple_sitemap.bundle_settings.$variant.");
  542. foreach ($config_names as $config_name) {
  543. $this->configFactory->getEditable($config_name)->delete();
  544. }
  545. $this->removeEntityInstanceSettings();
  546. }
  547. }
  548. return $this;
  549. }
  550. /**
  551. * Supplements all missing link setting with default values.
  552. *
  553. * @param string $type
  554. * Can be 'entity' or 'custom'.
  555. *
  556. * @param array &$settings
  557. * @param array $overrides
  558. */
  559. public static function supplementDefaultSettings($type, &$settings, $overrides = []) {
  560. foreach (self::$allowedLinkSettings[$type] as $allowed_link_setting) {
  561. if (!isset($settings[$allowed_link_setting])
  562. && isset(self::$linkSettingDefaults[$allowed_link_setting])) {
  563. $settings[$allowed_link_setting] = isset($overrides[$allowed_link_setting])
  564. ? $overrides[$allowed_link_setting]
  565. : self::$linkSettingDefaults[$allowed_link_setting];
  566. }
  567. }
  568. }
  569. /**
  570. * Overrides sitemap settings for a single entity for the currently set
  571. * variants.
  572. *
  573. * @param string $entity_type_id
  574. * @param string $id
  575. * @param array $settings
  576. *
  577. * @return $this
  578. *
  579. * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
  580. * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
  581. */
  582. public function setEntityInstanceSettings($entity_type_id, $id, $settings) {
  583. if (empty($variants = $this->getVariants(FALSE))) {
  584. return $this;
  585. }
  586. $entity = $this->entityTypeManager->getStorage($entity_type_id)->load($id);
  587. $all_bundle_settings = $this->getBundleSettings(
  588. $entity_type_id, $this->entityHelper->getEntityInstanceBundleName($entity), TRUE, TRUE
  589. );
  590. foreach ($all_bundle_settings as $variant => $bundle_settings) {
  591. if (!empty($bundle_settings)) {
  592. // Check if overrides are different from bundle setting before saving.
  593. $override = FALSE;
  594. foreach ($settings as $key => $setting) {
  595. if (!isset($bundle_settings[$key]) || $setting != $bundle_settings[$key]) {
  596. $override = TRUE;
  597. break;
  598. }
  599. }
  600. // Save overrides for this entity if something is different.
  601. if ($override) {
  602. $this->db->merge('simple_sitemap_entity_overrides')
  603. ->keys([
  604. 'type' => $variant,
  605. 'entity_type' => $entity_type_id,
  606. 'entity_id' => $id])
  607. ->fields([
  608. 'type' => $variant,
  609. 'entity_type' => $entity_type_id,
  610. 'entity_id' => $id,
  611. 'inclusion_settings' => serialize(array_merge($bundle_settings, $settings))])
  612. ->execute();
  613. }
  614. // Else unset override.
  615. else {
  616. $this->removeEntityInstanceSettings($entity_type_id, $id);
  617. }
  618. }
  619. }
  620. return $this;
  621. }
  622. /**
  623. * Gets sitemap settings for an entity instance which overrides bundle
  624. * settings, or gets bundle settings, if they are not overridden. This is
  625. * done for the currently set variant.
  626. *
  627. * @param string $entity_type_id
  628. * @param string $id
  629. *
  630. * @return array|false
  631. * Array of entity instance settings or the settings of its bundle. False if
  632. * entity type does not exist.
  633. *
  634. * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
  635. * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
  636. *
  637. * @todo multiple variants
  638. */
  639. public function getEntityInstanceSettings($entity_type_id, $id) {
  640. if (empty($variants = $this->getVariants(FALSE))) {
  641. return FALSE;
  642. }
  643. $results = $this->db->select('simple_sitemap_entity_overrides', 'o')
  644. ->fields('o', ['inclusion_settings'])
  645. ->condition('o.type', $variants[0])
  646. ->condition('o.entity_type', $entity_type_id)
  647. ->condition('o.entity_id', $id)
  648. ->execute()
  649. ->fetchField();
  650. if (!empty($results)) {
  651. return unserialize($results);
  652. }
  653. else {
  654. $entity = $this->entityTypeManager->getStorage($entity_type_id)
  655. ->load($id);
  656. return $this->getBundleSettings(
  657. $entity_type_id,
  658. $this->entityHelper->getEntityInstanceBundleName($entity)
  659. );
  660. }
  661. }
  662. /**
  663. * Removes sitemap settings for entities that override bundle settings. This
  664. * is done for the currently set variants.
  665. *
  666. * @param string|null $entity_type_id
  667. * Limits the removal to a certain entity type.
  668. *
  669. * @param string|null $entity_ids
  670. * Limits the removal to entities with certain IDs.
  671. *
  672. * @return $this
  673. */
  674. public function removeEntityInstanceSettings($entity_type_id = NULL, $entity_ids = NULL) {
  675. if (empty($variants = $this->getVariants(FALSE))) {
  676. return $this;
  677. }
  678. $query = $this->db->delete('simple_sitemap_entity_overrides')
  679. ->condition('type', $variants, 'IN');
  680. if (NULL !== $entity_type_id) {
  681. $query->condition('entity_type', $entity_type_id);
  682. if (NULL !== $entity_ids) {
  683. $query->condition('entity_id', (array) $entity_ids, 'IN');
  684. }
  685. }
  686. $query->execute();
  687. return $this;
  688. }
  689. /**
  690. * Checks if an entity bundle (or a non-bundle entity type) is set to be
  691. * indexed for the currently set variant.
  692. *
  693. * @param string $entity_type_id
  694. * @param string|null $bundle_name
  695. *
  696. * @return bool
  697. *
  698. * @todo multiple variants?
  699. */
  700. public function bundleIsIndexed($entity_type_id, $bundle_name = NULL) {
  701. $settings = $this->getBundleSettings($entity_type_id, $bundle_name);
  702. return !empty($settings['index']);
  703. }
  704. /**
  705. * Checks if an entity type is enabled in the sitemap settings.
  706. *
  707. * @param string $entity_type_id
  708. *
  709. * @return bool
  710. */
  711. public function entityTypeIsEnabled($entity_type_id) {
  712. return in_array($entity_type_id, $this->getSetting('enabled_entity_types', []));
  713. }
  714. /**
  715. * Stores a custom path along with its settings to configuration for the
  716. * currently set variants.
  717. *
  718. * @param string $path
  719. *
  720. * @param array $settings
  721. * Settings that are not provided are supplemented by defaults.
  722. *
  723. * @return $this
  724. *
  725. * @todo Validate $settings and throw exceptions
  726. */
  727. public function addCustomLink($path, $settings = []) {
  728. if (empty($variants = $this->getVariants(FALSE))) {
  729. return $this;
  730. }
  731. if (!(bool) $this->pathValidator->getUrlIfValidWithoutAccessCheck($path)) {
  732. // todo: log error.
  733. return $this;
  734. }
  735. if ($path[0] !== '/') {
  736. // todo: log error.
  737. return $this;
  738. }
  739. $variant_links = $this->getCustomLinks(NULL, FALSE, TRUE);
  740. foreach ($variants as $variant) {
  741. $links = [];
  742. $link_key = 0;
  743. if (isset($variant_links[$variant])) {
  744. $links = $variant_links[$variant];
  745. $link_key = count($links);
  746. foreach ($links as $key => $link) {
  747. if ($link['path'] === $path) {
  748. $link_key = $key;
  749. break;
  750. }
  751. }
  752. }
  753. $links[$link_key] = ['path' => $path] + $settings;
  754. $this->configFactory->getEditable("simple_sitemap.custom_links.$variant")
  755. ->set('links', $links)->save();
  756. }
  757. return $this;
  758. }
  759. /**
  760. * Gets custom link settings for the currently set variants.
  761. *
  762. * @param string|null $path
  763. * Limits the result set by an internal path.
  764. *
  765. * @param bool $supplement_defaults
  766. * Supplements the result set with default custom link settings.
  767. *
  768. * @param bool $multiple_variants
  769. * If true, returns an array of results keyed by variant name, otherwise it
  770. * returns the result set for the first variant only.
  771. *
  772. * @return array|mixed|null
  773. */
  774. public function getCustomLinks($path = NULL, $supplement_defaults = TRUE, $multiple_variants = FALSE) {
  775. $all_custom_links = [];
  776. foreach ($variants = $this->getVariants(FALSE) as $variant) {
  777. $custom_links = $this->configFactory
  778. ->get("simple_sitemap.custom_links.$variant")
  779. ->get('links');
  780. $custom_links = !empty($custom_links) ? $custom_links : [];
  781. if (!empty($custom_links) && $path !== NULL) {
  782. foreach ($custom_links as $key => $link) {
  783. if ($link['path'] !== $path) {
  784. unset($custom_links[$key]);
  785. }
  786. }
  787. }
  788. if (!empty($custom_links) && $supplement_defaults) {
  789. foreach ($custom_links as $i => $link_settings) {
  790. self::supplementDefaultSettings('custom', $link_settings);
  791. $custom_links[$i] = $link_settings;
  792. }
  793. }
  794. $custom_links = $path !== NULL && !empty($custom_links)
  795. ? array_values($custom_links)[0]
  796. : array_values($custom_links);
  797. if (!empty($custom_links)) {
  798. if ($multiple_variants) {
  799. $all_custom_links[$variant] = $custom_links;
  800. }
  801. else {
  802. return $custom_links;
  803. }
  804. }
  805. }
  806. return $all_custom_links;
  807. }
  808. /**
  809. * Removes custom links from currently set variants.
  810. *
  811. * @param array|null $paths
  812. * Limits the removal to certain paths.
  813. *
  814. * @return $this
  815. */
  816. public function removeCustomLinks($paths = NULL) {
  817. if (empty($variants = $this->getVariants(FALSE))) {
  818. return $this;
  819. }
  820. if (NULL === $paths) {
  821. foreach ($variants as $variant) {
  822. $this->configFactory
  823. ->getEditable("simple_sitemap.custom_links.$variant")->delete();
  824. }
  825. }
  826. else {
  827. $variant_links = $this->getCustomLinks(NULL, FALSE, TRUE);
  828. foreach ($variant_links as $variant => $links) {
  829. $custom_links = $links;
  830. $save = FALSE;
  831. foreach ((array) $paths as $path) {
  832. foreach ($custom_links as $key => $link) {
  833. if ($link['path'] === $path) {
  834. unset($custom_links[$key]);
  835. $save = TRUE;
  836. break 2;
  837. }
  838. }
  839. }
  840. if ($save) {
  841. $this->configFactory->getEditable("simple_sitemap.custom_links.$variant")
  842. ->set('links', array_values($custom_links))->save();
  843. }
  844. }
  845. }
  846. return $this;
  847. }
  848. }