From b74cd8161bcf5e968d2d77cb68d42137a9cea07c Mon Sep 17 00:00:00 2001 From: Steve Worley Date: Tue, 22 Aug 2023 20:25:45 +1000 Subject: [PATCH 1/2] Support simple sitemap 4.1 Add hook_requirements and sitemap_manager. Update version management. Lint. Update modules/quant_sitemap/src/SitemapManager.php Co-authored-by: Stuart Rowlands <1256274+stooit@users.noreply.github.com> Update modules/quant_sitemap/src/SitemapManager.php Co-authored-by: Stuart Rowlands <1256274+stooit@users.noreply.github.com> Update modules/quant_sitemap/src/SitemapManager.php Co-authored-by: Stuart Rowlands <1256274+stooit@users.noreply.github.com> Update xmlsitemap integration. Update requirements and availability check. Update casing. Lint. Update unit test. Lint. --- modules/quant_sitemap/quant_sitemap.install | 26 +++ modules/quant_sitemap/quant_sitemap.module | 9 +- .../quant_sitemap/quant_sitemap.services.yml | 10 +- .../EventSubscriber/CollectionSubscriber.php | 81 +------ modules/quant_sitemap/src/SitemapManager.php | 201 ++++++++++++++++ .../src/Unit/CollectionSubscriberTest.php | 66 ------ .../tests/src/Unit/SitemapManagerTest.php | 220 ++++++++++++++++++ src/Commands/QuantDrushCommands.php | 5 +- 8 files changed, 472 insertions(+), 146 deletions(-) create mode 100644 modules/quant_sitemap/quant_sitemap.install create mode 100644 modules/quant_sitemap/src/SitemapManager.php delete mode 100644 modules/quant_sitemap/tests/src/Unit/CollectionSubscriberTest.php create mode 100644 modules/quant_sitemap/tests/src/Unit/SitemapManagerTest.php diff --git a/modules/quant_sitemap/quant_sitemap.install b/modules/quant_sitemap/quant_sitemap.install new file mode 100644 index 00000000..79dbc987 --- /dev/null +++ b/modules/quant_sitemap/quant_sitemap.install @@ -0,0 +1,26 @@ +isAvailable(); + + $requirements['quant_sitemap'] = [ + 'title' => t('Quant Sitemap'), + 'severity' => $available ? REQUIREMENT_OK : REQUIREMENT_WARNING, + 'description' => $reason, + ]; + + return $requirements; +} diff --git a/modules/quant_sitemap/quant_sitemap.module b/modules/quant_sitemap/quant_sitemap.module index 757124e8..998d292d 100644 --- a/modules/quant_sitemap/quant_sitemap.module +++ b/modules/quant_sitemap/quant_sitemap.module @@ -12,11 +12,18 @@ use Drupal\quant\Form\SeedForm; * Implements hook_form_FORM_ID_alter(). */ function quant_sitemap_form_quant_seed_form_alter(&$form, FormStateInterface $form_state) { + [$available, $reason] = \Drupal::service('quant_sitemap.sitemap_manager')->isAvailable(); + + if (!$available) { + \Drupal::messenger()->addError($reason); + } + $form['export_sitemap'] = [ '#type' => 'checkbox', '#title' => t('Sitemaps'), - '#description' => t('Export all sitemap variants to quant'), + '#description' => t('Export all sitemap variants to Quant'), '#default_value' => \Drupal::config(SeedForm::SETTINGS)->get('export_sitemap'), + '#disabled' => !$available, ]; $form['#submit'][] = 'quant_sitemap_settings_submit'; diff --git a/modules/quant_sitemap/quant_sitemap.services.yml b/modules/quant_sitemap/quant_sitemap.services.yml index 2c3f1cf5..77973dd0 100644 --- a/modules/quant_sitemap/quant_sitemap.services.yml +++ b/modules/quant_sitemap/quant_sitemap.services.yml @@ -1,9 +1,15 @@ services: - quant_sitemap.collection_subscriber: - class: Drupal\quant_sitemap\EventSubscriber\CollectionSubscriber + quant_sitemap.sitemap_manager: + class: Drupal\quant_sitemap\SitemapManager arguments: - '@module_handler' - '@entity_type.manager' + - '@extension.list.module' + + quant_sitemap.collection_subscriber: + class: Drupal\quant_sitemap\EventSubscriber\CollectionSubscriber + arguments: + - '@quant_sitemap.sitemap_manager' tags: - { name: 'event_subscriber' } diff --git a/modules/quant_sitemap/src/EventSubscriber/CollectionSubscriber.php b/modules/quant_sitemap/src/EventSubscriber/CollectionSubscriber.php index c07846ed..53404690 100644 --- a/modules/quant_sitemap/src/EventSubscriber/CollectionSubscriber.php +++ b/modules/quant_sitemap/src/EventSubscriber/CollectionSubscriber.php @@ -5,8 +5,7 @@ use Drupal\quant\Event\CollectRoutesEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Drupal\quant\Event\QuantCollectionEvents; -use Drupal\Core\Extension\ModuleHandler; -use Drupal\Core\Entity\EntityTypeManager; +use Drupal\quant_sitemap\SitemapManager; /** * Collection subscriber for Sitemap routes. @@ -16,9 +15,9 @@ class CollectionSubscriber implements EventSubscriberInterface { /** * The core module handler. * - * @var Drupal\Core\Extension\ModuleHandler + * @var Drupal\quant_sitemap\SitemapManager */ - protected $moduleHandler; + protected $manager; /** * The entity type manager. @@ -30,9 +29,8 @@ class CollectionSubscriber implements EventSubscriberInterface { /** * {@inheritdoc} */ - public function __construct(ModuleHandler $module_handler, EntityTypeManager $entity_type_manager) { - $this->moduleHandler = $module_handler; - $this->entityTypeManager = $entity_type_manager; + public function __construct(SitemapManager $manager) { + $this->manager = $manager; } /** @@ -43,63 +41,6 @@ public static function getSubscribedEvents() { return $events; } - /** - * Get the simple sitemap manager. - * - * @return Drupal\simple_sitemap\SimplesitemapManager - * The sitemap manager. - */ - public function getSitemapManager() { - return \Drupal::service('simple_sitemap.manager'); - } - - /** - * The entity type manager. - * - * @return Drupal\Core\Entity\EntityTypeManager - * The entity type manager. - */ - public function getEntityTypeManager() { - return $this->entityTypeManager; - } - - /** - * Simple sitemap support. - * - * Simple sitemap allows you to define accessible sitemap routes. This - * is always prefixed by the variant ID. As this is not a standard - * content entity we need to use the provided manager to load the values. - * - * @return array - * A list of routes that sitemaps are accessible by. - */ - public function getSimpleSitemapItems() : array { - $items = ['/sitemap.xml']; - foreach ($this->getSitemapManager()->getSitemapVariants() as $variant => $def) { - $items[] = "/$variant/sitemap.xml"; - } - return $items; - } - - /** - * XMLSitemap support. - * - * XMLSitemap only allows base level sitemaps with language prefixes. - * Each enabled language context can only have one sitemap, the admin - * UI returns an error if you try to double up. - * - * @return array - * A list of routes that sitemaps are accessible by. - */ - public function getXmlsitemapItems() : array { - $items = []; - $sitemaps = $this->getEntityTypeManager()->getStorage('xmlsitemap')->loadMultiple(); - foreach ($sitemaps as $sitemap) { - $items[] = "/{$sitemap->language()->getId()}/sitemap.xml"; - } - return $items; - } - /** * Collect the sitemap routes. */ @@ -107,17 +48,7 @@ public function collectRoutes(CollectRoutesEvent $event) { if (empty($event->getFormState()->getValue('export_sitemap'))) { return; } - - $items = []; - - if ($this->moduleHandler->moduleExists('simple_sitemap')) { - $items = $this->getSimpleSitemapItems(); - } - elseif ($this->moduleHandler->moduleExists('xmlsitemap')) { - $items = $this->getXmlsitemapItems(); - } - - foreach ($items as $route) { + foreach ($this->manager->getSitemaps() as $route) { $event->queueItem(['route' => $route]); } } diff --git a/modules/quant_sitemap/src/SitemapManager.php b/modules/quant_sitemap/src/SitemapManager.php new file mode 100644 index 00000000..bf204366 --- /dev/null +++ b/modules/quant_sitemap/src/SitemapManager.php @@ -0,0 +1,201 @@ +moduleHandler = $module_handler; + $this->entityTypeManager = $entity_type_manager; + $this->moduleList = $modules; + } + + /** + * Getter for module handler property. + * + * @return Drupal\Core\Extension\ModuleHandler + * The module handler. + */ + public function getModuleHandler() { + return $this->moduleHandler; + } + + /** + * Getter for module list. + * + * @return Drupal\Core\Extension\ModuleExtensionList + * The module extension list. + */ + public function getModuleList() { + return $this->moduleList; + } + + /** + * Getter for entity type manager. + * + * @return Drupal\Core\Entity\EntityTypeManager + * The entity type manager. + */ + public function getEntityTypeManager() { + return $this->entityTypeManager; + } + + /** + * Determine if the sitemap integration is available for this site. + * + * @return array + * The status and a reason for it not being available. + */ + public function isAvailable() : array { + $reason = $this->t('quant_sitemap requires simple_sitemap or xmlsitemap'); + + if ($this->getModuleHandler()->moduleExists('simple_sitemap')) { + $module = $this->getModuleList()->get('simple_sitemap'); + if (!empty($module) && version_compare($module->info['version'], self::SIMPLE_SITEMAP_MINIMUM_VERSION, '>=')) { + return [ + TRUE, + $this->t('simple_sitemap is a supported version (@ver)', [ + '@ver' => $module->info['version'], + ]), + ]; + } + else { + $reason = $this->t('quant_sitemap requires simple_sitemap to be version >= @ver', [ + '@ver' => self::SIMPLE_SITEMAP_MINIMUM_VERSION, + ]); + } + } + + if ($this->moduleHandler->moduleExists('xmlsitemap')) { + $module = $this->moduleList->get('xmlsitemap'); + if (!empty($module) && version_compare($module->info['version'], self::XMLSITEMAP_MINIMUM_VERSION, '>=')) { + return [ + TRUE, + $this->t('xmlsitemap is a supported version (@ver)', [ + '@ver' => $module->info['version'], + ]), + ]; + } + else { + $reason = $this->t('quant_sitemap requires xmlsitemap to be version >= @ver', [ + '@ver' => self::XMLSITEMAP_MINIMUM_VERSION, + ]); + } + } + + return [FALSE, $reason]; + } + + /** + * Get configured sitemaps based on available sources. + * + * @return array + * A list of sitemap paths. + */ + public function getSitemaps() : array { + [$available] = $this->isAvailable(); + if (!$available) { + return []; + } + + if ($this->getModuleHandler()->moduleExists('simple_sitemap')) { + return $this->getSimpleSitemaps(); + } + + if ($this->getModuleHandler()->moduleExists('xmlsitemap')) { + return $this->getXmlSitemaps(); + } + + return []; + } + + /** + * Get the simple sitemap manager. + * + * @return Drupal\simple_sitemap\Manager\EntityManager + * The sitemap manager. + */ + protected function getSitemapManager() { + return \Drupal::service('simple_sitemap.entity_manager'); + } + + /** + * Get sitemap items from simple_sitemap. + * + * @return array + * A list of sitemap items. + */ + protected function getSimpleSitemaps() : array { + $items = ['/sitemap.xml', '/sitemap_generator/default/sitemap.xsl']; + foreach ($this->getSitemapManager()->getAllBundleSettings() as $variant => $def) { + if ($variant == "default") { + continue; + } + $items[] = "/$variant/sitemap.xml"; + } + return $items; + } + + /** + * Get sitemap items from xmlsitemap. + * + * @return array + * A list of sitemap paths. + */ + protected function getXmlSitemaps() : array { + $items = ['/sitemap.xml', '/sitemap.xsl']; + $sitemaps = $this->getEntityTypeManager()->getStorage('xmlsitemap')->loadMultiple(); + $lang_code = \Drupal::service('language.default')->get()->getId(); + + foreach ($sitemaps as $sitemap) { + $context = $sitemap->getContext(); + if (empty($context['language']) || $context['language'] == $lang_code) { + // Default langcode is always served via sitemap.xml as a hard coded + // link from xmlsitemap and this will result in a 404 — so we just + // skip the default langauge for now. + continue; + } + $items[] = "/{$context['language']}/sitemap.xml"; + } + return $items; + } + +} diff --git a/modules/quant_sitemap/tests/src/Unit/CollectionSubscriberTest.php b/modules/quant_sitemap/tests/src/Unit/CollectionSubscriberTest.php deleted file mode 100644 index edfe8b66..00000000 --- a/modules/quant_sitemap/tests/src/Unit/CollectionSubscriberTest.php +++ /dev/null @@ -1,66 +0,0 @@ -prophesize(CollectionSubscriber::class); - $storage = $this->prophesize(StorageBase::class); - $entities = []; - - foreach (['en', 'es'] as $lang) { - $l = $this->prophesize(Language::class); - $l->getId()->willReturn($lang); - $entity = $this->prophesize(XmlSitemap::class); - $entity->language()->willReturn($l); - $entities[] = $entity; - } - - $storage->loadMultiple()->willReturn($entities); - $stub->getEntityTypeManager()->willReturn($storage); - - $this->assertEquals([ - '/en/sitemap.xml', - '/es/sitemap.xml', - ], $stub->getXmlsitemapItems()); - } - - /** - * Ensure simple sitemap items are generated. - */ - public function testGetSimpleSitemapItems() { - $sitemap_manager = $this->prophesize(SimplesitemapManager::class); - $variants = [ - 'default' => [], - 'sample' => [], - ]; - $sitemap_manager->getSitemapVariants()->willReturn($variants); - - $stub = $this->prophesize(CollectionSubscriber::class); - $stub->getSitemapManager()->willReturn($sitemap_manager); - - $this->assertEquals([ - '/sitemap.xml', - '/default/sitemap.xml', - '/sample/sitemap.xml', - ], $stub->getSimpleSitemapItems()); - } - -} diff --git a/modules/quant_sitemap/tests/src/Unit/SitemapManagerTest.php b/modules/quant_sitemap/tests/src/Unit/SitemapManagerTest.php new file mode 100644 index 00000000..d23b18c2 --- /dev/null +++ b/modules/quant_sitemap/tests/src/Unit/SitemapManagerTest.php @@ -0,0 +1,220 @@ +createMock(ModuleHandler::class); + $module_handler_mock->expects($this->once()) + ->method('moduleExists') + ->with('simple_sitemap') + ->willReturn(TRUE); + + $module = new \StdClass(); + $module->info = ['version' => "4.1.6"]; + + $module_list_mock = $this->createMock(ModuleExtensionList::class); + $module_list_mock->expects($this->once()) + ->method('get') + ->with('simple_sitemap') + ->willReturn($module); + + $entity_manager_mock = $this->getMockBuilder(EntityTypeManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $manager = new SitemapManager($module_handler_mock, $entity_manager_mock, $module_list_mock); + $result = $manager->isAvailable(); + $this->assertTrue($result[0]); + } + + /** + * Verify quant sitemap compatibilty with simple_sitemap. + */ + public function testSimpleSitemapUnsupportedVersion() { + $module_handler_mock = $this->createMock(ModuleHandler::class); + $module_handler_mock->expects($this->any()) + ->method('moduleExists') + ->willReturnMap([ + ['simple_sitemap', TRUE], + ['xmlsitemap', FALSE], + ]); + + $module = new \StdClass(); + $module->info = ['version' => "3.9"]; + + $module_list_mock = $this->createMock(ModuleExtensionList::class); + $module_list_mock->expects($this->once()) + ->method('get') + ->with('simple_sitemap') + ->willReturn($module); + + $entity_manager_mock = $this->getMockBuilder(EntityTypeManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $manager = new SitemapManager($module_handler_mock, $entity_manager_mock, $module_list_mock); + $result = $manager->isAvailable(); + $this->assertFalse($result[0]); + } + + /** + * Verify quant sitemap compatibilty with xmlsitemap. + */ + public function testXmlsitemapSupportedVersion() { + $module_handler_mock = $this->createMock(ModuleHandler::class); + $module_handler_mock->expects($this->any()) + ->method('moduleExists') + ->willReturnMap([ + ['simple_sitemap', FALSE], + ['xmlsitemap', TRUE], + ]); + + $module = new \StdClass(); + $module->info = ['version' => "8.x-1.5"]; + + $module_list_mock = $this->createMock(ModuleExtensionList::class); + $module_list_mock->expects($this->once()) + ->method('get') + ->with('xmlsitemap') + ->willReturn($module); + + $entity_manager_mock = $this->getMockBuilder(EntityTypeManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $manager = new SitemapManager($module_handler_mock, $entity_manager_mock, $module_list_mock); + $result = $manager->isAvailable(); + $this->assertTrue($result[0]); + } + + /** + * Verify quant sitemap compatibilty with xmlsitemap. + */ + public function testXmlsitemapUnsupportedVersion() { + $module_handler_mock = $this->createMock(ModuleHandler::class); + $module_handler_mock->expects($this->any()) + ->method('moduleExists') + ->willReturnMap([ + ['simple_sitemap', FALSE], + ['xmlsitemap', TRUE], + ]); + + $module = new \StdClass(); + $module->info = ['version' => "8.x-1.2"]; + + $module_list_mock = $this->createMock(ModuleExtensionList::class); + $module_list_mock->expects($this->once()) + ->method('get') + ->with('xmlsitemap') + ->willReturn($module); + + $entity_manager_mock = $this->getMockBuilder(EntityTypeManager::class) + ->disableOriginalConstructor() + ->getMock(); + + $manager = new SitemapManager($module_handler_mock, $entity_manager_mock, $module_list_mock); + $result = $manager->isAvailable(); + $this->assertFalse($result[0]); + } + + /** + * Test that if no sitemap module is available the module behaves correctly. + */ + public function testSitemapUnavailable() { + $module_handler_mock = $this->createMock(ModuleHandler::class); + $module_handler_mock->expects($this->any()) + ->method('moduleExists') + ->willReturnMap([ + ['simple_sitemap', FALSE], + ['xmlsitemap', FALSE], + ]); + + $module_list_mock = $this->createMock(ModuleExtensionList::class); + $entity_manager_mock = $this->createMock(EntityTypeManager::class); + + $manager = $this->getMockBuilder(SitemapManager::class) + ->setConstructorArgs([ + $module_handler_mock, + $entity_manager_mock, + $module_list_mock, + ]) + ->setMethods(['isAvailable']) + ->getMock(); + + $manager->expects($this->once()) + ->method('isAvailable') + ->willReturn([FALSE, '']); + + $result = $manager->getSitemaps(); + + $this->assertEquals([], $result); + + } + + /** + * Test that simple sitemap items generate as expected. + */ + public function testSimpleSitemapListItems() { + $module_handler_mock = $this->createMock(ModuleHandler::class); + $module_handler_mock->expects($this->once()) + ->method('moduleExists') + ->with('simple_sitemap') + ->willReturn(TRUE); + + $module_list_mock = $this->createMock(ModuleExtensionList::class); + $entity_manager_mock = $this->createMock(EntityTypeManager::class); + + $simplesitemap_manager = $this->createMock(SimpleSitemapManager::class); + $simplesitemap_manager->expects($this->once()) + ->method('getAllBundleSettings') + ->willReturn([ + 'default' => [], + 'es' => [], + ]); + + $manager = $this->getMockBuilder(SitemapManager::class) + ->setConstructorArgs([ + $module_handler_mock, + $entity_manager_mock, + $module_list_mock, + ]) + ->setMethods(['isAvailable', 'getSitemapManager']) + ->getMock(); + + $manager->expects($this->once()) + ->method('isAvailable') + ->willReturn([TRUE, '']); + + $manager->expects($this->once()) + ->method('getSitemapManager') + ->willReturn($simplesitemap_manager); + + $result = $manager->getSitemaps(); + + $this->assertEquals([ + '/sitemap.xml', + '/sitemap_generator/default/sitemap.xsl', + '/es/sitemap.xml', + ], $result); + } + +} diff --git a/src/Commands/QuantDrushCommands.php b/src/Commands/QuantDrushCommands.php index fca57c72..76b8c75d 100644 --- a/src/Commands/QuantDrushCommands.php +++ b/src/Commands/QuantDrushCommands.php @@ -83,9 +83,10 @@ public function message($options = ['threads' => 5]) { if (file_exists($lockFilePath)) { $this->output()->writeln("Seeding bailed. Another seed run is in progress."); return; - } else { + } + else { // No lock currently present. Create new lock file. - file_put_contents($lockFilePath, null); + file_put_contents($lockFilePath, NULL); } for ($i = 0; $i < $options['threads']; $i++) { From 7c7e3418716f2835be273b9781e3aa2e785a25c1 Mon Sep 17 00:00:00 2001 From: Steve Worley Date: Fri, 25 Aug 2023 12:01:53 +1000 Subject: [PATCH 2/2] Update message. --- modules/quant_sitemap/src/SitemapManager.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/quant_sitemap/src/SitemapManager.php b/modules/quant_sitemap/src/SitemapManager.php index bf204366..6d29287b 100644 --- a/modules/quant_sitemap/src/SitemapManager.php +++ b/modules/quant_sitemap/src/SitemapManager.php @@ -92,7 +92,7 @@ public function isAvailable() : array { if (!empty($module) && version_compare($module->info['version'], self::SIMPLE_SITEMAP_MINIMUM_VERSION, '>=')) { return [ TRUE, - $this->t('simple_sitemap is a supported version (@ver)', [ + $this->t('simple_sitemap is installed at a supported version (@ver)', [ '@ver' => $module->info['version'], ]), ]; @@ -109,7 +109,7 @@ public function isAvailable() : array { if (!empty($module) && version_compare($module->info['version'], self::XMLSITEMAP_MINIMUM_VERSION, '>=')) { return [ TRUE, - $this->t('xmlsitemap is a supported version (@ver)', [ + $this->t('xmlsitemap is installed at a supported version (@ver)', [ '@ver' => $module->info['version'], ]), ];