From b429b0f7e39c15f6d3ca4d211751fb65249e719b Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Mon, 4 Dec 2023 22:13:48 -0500 Subject: [PATCH 01/17] chore: Version 3.4.68 --- CHANGELOG.md | 4 ++++ composer.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fc1a94e3..776502742 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # SEOmatic Changelog +## 3.4.68 - UNRELEASED +### Changed +* If the Site URL Override feature is used, pass along the parameters, too, when building the URL ([#950](https://github.com/nystudio107/craft-seomatic/issues/950)) + ## 3.4.67 - 2023.11.28 ### Added * Switch over to Vite `^5.0.0` & Node `^20.0.0` for the buildchain diff --git a/composer.json b/composer.json index fd6e0c8d0..8643b6aa6 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "nystudio107/craft-seomatic", "description": "SEOmatic facilitates modern SEO best practices & implementation for Craft CMS 3. It is a turnkey SEO system that is comprehensive, powerful, and flexible.", "type": "craft-plugin", - "version": "3.4.67", + "version": "3.4.68", "keywords": [ "craft", "cms", From 6b317fcdc8490139b08fc6af241dd9d2055d0677 Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Mon, 4 Dec 2023 22:14:11 -0500 Subject: [PATCH 02/17] refactor: If the Site URL Override feature is used, pass along the parameters, too, when building the URL ([#950](https://github.com/nystudio107/craft-seomatic/issues/950)) --- src/helpers/UrlHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/UrlHelper.php b/src/helpers/UrlHelper.php index 402f990c1..a27ea197a 100644 --- a/src/helpers/UrlHelper.php +++ b/src/helpers/UrlHelper.php @@ -51,7 +51,7 @@ public static function siteUrl(string $path = '', $params = null, string $scheme $url = rtrim($url, '/'); } - return $url; + return DynamicMeta::sanitizeUrl(parent::urlWithParams($url, $params), false, false); } return DynamicMeta::sanitizeUrl(parent::siteUrl($path, $params, $scheme, $siteId), false, false); From 23ff111115839b8f452aed6f2ff8c1cc1584d3a7 Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Tue, 5 Dec 2023 11:47:19 -0500 Subject: [PATCH 03/17] refactor: Removed the automatic Google Sitemap ping endpoint, since [Google has deprecated it and will be removing it entirely soon](https://developers.google.com/search/blog/2023/06/sitemaps-lastmod-ping) ([#1392](https://github.com/nystudio107/craft-seomatic/issues/1392)) --- src/services/Sitemaps.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/Sitemaps.php b/src/services/Sitemaps.php index d9c518090..505ead09c 100644 --- a/src/services/Sitemaps.php +++ b/src/services/Sitemaps.php @@ -49,7 +49,6 @@ class Sitemaps extends Component implements SitemapInterface const SEOMATIC_SITEMAPCUSTOM_CONTAINER = Seomatic::SEOMATIC_HANDLE . SitemapCustomTemplate::TEMPLATE_TYPE; const SEARCH_ENGINE_SUBMISSION_URLS = [ - 'google' => 'https://www.google.com/ping?sitemap=', ]; // Protected Properties From f228ffd0eedd9c1a9be054cadf708805b909bb0f Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Tue, 5 Dec 2023 11:47:28 -0500 Subject: [PATCH 04/17] chore: Version 3.4.68 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 776502742..8d317ae10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 3.4.68 - UNRELEASED ### Changed * If the Site URL Override feature is used, pass along the parameters, too, when building the URL ([#950](https://github.com/nystudio107/craft-seomatic/issues/950)) +* Removed the automatic Google Sitemap ping endpoint, since [Google has deprecated it and will be removing it entirely soon](https://developers.google.com/search/blog/2023/06/sitemaps-lastmod-ping) ([#1392](https://github.com/nystudio107/craft-seomatic/issues/1392)) ## 3.4.67 - 2023.11.28 ### Added From 4ce85d388bfe0fb811413f09ecddb4683da04fe6 Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Wed, 6 Dec 2023 12:55:28 -0500 Subject: [PATCH 05/17] chore: Version 3.4.68 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d317ae10..9a9eb26ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # SEOmatic Changelog ## 3.4.68 - UNRELEASED +### Added +* Allow the `config/seomatic.php` `siteUrlOverride` to be set to either a string, or an array of site URLs, indexed by the site handle for overriding complex headless multi-site Craft setups ([#1376](https://github.com/nystudio107/craft-seomatic/issues/1376)) + ### Changed * If the Site URL Override feature is used, pass along the parameters, too, when building the URL ([#950](https://github.com/nystudio107/craft-seomatic/issues/950)) * Removed the automatic Google Sitemap ping endpoint, since [Google has deprecated it and will be removing it entirely soon](https://developers.google.com/search/blog/2023/06/sitemaps-lastmod-ping) ([#1392](https://github.com/nystudio107/craft-seomatic/issues/1392)) From 90ed2cd335f3470596e9cea708379bc991e0cb4d Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Wed, 6 Dec 2023 12:56:14 -0500 Subject: [PATCH 06/17] feat: Allow the `config/seomatic.php` `siteUrlOverride` to be set to either a string, or an array of site URLs, indexed by the site handle for overriding complex headless multi-site Craft setups ([#1376](https://github.com/nystudio107/craft-seomatic/issues/1376)) --- src/config.php | 10 +++- src/helpers/DynamicMeta.php | 6 +- src/helpers/UrlHelper.php | 39 ++++++++++++- src/models/Settings.php | 11 +++- .../settings/plugin/_includes/advanced.twig | 55 ++++++++++++------- 5 files changed, 92 insertions(+), 29 deletions(-) diff --git a/src/config.php b/src/config.php index 23bd464b4..fa04cbac2 100644 --- a/src/config.php +++ b/src/config.php @@ -9,8 +9,6 @@ * @copyright Copyright (c) 2017 nystudio107 */ -use nystudio107\seomatic\base\SeoElementInterface; - /** * @author nystudio107 * @package Seomatic @@ -81,7 +79,7 @@ // If `devMode` is on, prefix the with this string 'devModeTitlePrefix' => '🚧 ', - // Prefix the Control Panel <title> with this string + // Prefix the Control Panel <title> with this string 'cpTitlePrefix' => '⚙ ', // If `devMode` is on, prefix the Control Panel <title> with this string @@ -141,6 +139,12 @@ // SEOmatic uses the Craft `siteUrl` to generate the external URLs. If you // are using it in a non-standard environment, such as a headless GraphQL or // ElementAPI server, you can override what it uses for the `siteUrl` below. + // This can be either a simple string, or an array of strings indexed by the site + // handle, for multi-site setups. e.g.: + // 'siteUrlOverride' => [ + // 'default' => 'http://example.com/', + // 'spanish' => 'http://example.com/es/', + // ], 'siteUrlOverride' => '', // The duration of the SEOmatic meta cache in seconds. Null means always cached until explicitly broken diff --git a/src/helpers/DynamicMeta.php b/src/helpers/DynamicMeta.php index 8b64a8df9..ae1110772 100644 --- a/src/helpers/DynamicMeta.php +++ b/src/helpers/DynamicMeta.php @@ -334,7 +334,11 @@ public static function addMetaJsonLdBreadCrumbs(int $siteId = null) Craft::error($e->getMessage(), __METHOD__); } if (!empty(Seomatic::$settings->siteUrlOverride)) { - $siteUrl = Seomatic::$settings->siteUrlOverride; + try { + $siteUrl = UrlHelper::getSiteUrlOverrideSetting($siteId); + } catch (\Throwable $e) { + // That's okay + } } $siteUrl = $siteUrl ?: '/'; /** @var $crumbs BreadcrumbList */ diff --git a/src/helpers/UrlHelper.php b/src/helpers/UrlHelper.php index a27ea197a..bc4f9179f 100644 --- a/src/helpers/UrlHelper.php +++ b/src/helpers/UrlHelper.php @@ -35,7 +35,11 @@ class UrlHelper extends CraftUrlHelper */ public static function siteUrl(string $path = '', $params = null, string $scheme = null, int $siteId = null): string { - $siteUrl = Seomatic::$settings->siteUrlOverride; + try { + $siteUrl = self::getSiteUrlOverrideSetting($siteId); + } catch (\Throwable $e) { + // That's okay + } if (!empty($siteUrl)) { $siteUrl = MetaValue::parseString($siteUrl); // Extract out just the path part @@ -183,6 +187,39 @@ public static function urlHasSubDir(string $url): bool return !empty(parse_url(trim($url, '/'), PHP_URL_PATH)); } + /** + * Return the siteUrlOverride setting, which can be a string or an array of site URLs + * indexed by the site handle + * + * @param int|null $siteId + * @return string + * @throws Exception + * @throws SiteNotFoundException + */ + public static function getSiteUrlOverrideSetting(?int $siteId = null): string + { + // If the override is a string, just return it + $siteUrlOverride = Seomatic::$settings->siteUrlOverride; + if (is_string($siteUrlOverride)) { + return $siteUrlOverride; + } + // If the override is an array, pluck the appropriate one by handle + if (is_array($siteUrlOverride)) { + $sites = Craft::$app->getSites(); + $site = $sites->getCurrentSite(); + if ($siteId !== null) { + $site = $sites->getSiteById($siteId, true); + if (!$site) { + throw new Exception('Invalid site ID: ' . $siteId); + } + } + + return $siteUrlOverride[$site->handle] ?? ''; + } + + return ''; + } + // Protected Methods // ========================================================================= diff --git a/src/models/Settings.php b/src/models/Settings.php index 230d25ac2..633dea709 100644 --- a/src/models/Settings.php +++ b/src/models/Settings.php @@ -200,11 +200,16 @@ class Settings extends VarsModel public $generatorEnabled = true; /** - * @var string + * @var string|array * SEOmatic uses the Craft `siteUrl` to generate the external URLs. If you * are using it in a non-standard environment, such as a headless GraphQL or * ElementAPI server, you can override what it uses for the `siteUrl` below. - */ + * This can be either a simple string, or an array of strings indexed by the site + * handle, for multi-site setups. e.g.: + * 'siteUrlOverride' => [ + * 'default' => 'http://example.com/', + * 'spanish' => 'http://example.com/es/', + * ], */ public $siteUrlOverride = ''; /** @@ -308,7 +313,7 @@ public function rules(): array ['maxTitleLength', 'default', 'value' => 70], ['maxDescriptionLength', 'integer', 'min' => 10], ['maxDescriptionLength', 'default', 'value' => 155], - ['siteUrlOverride', 'string'], + ['siteUrlOverride', 'safe'], ['siteUrlOverride', 'default', 'value' => ''], [ [ diff --git a/src/templates/settings/plugin/_includes/advanced.twig b/src/templates/settings/plugin/_includes/advanced.twig index f27897aa8..b8a44ac5a 100644 --- a/src/templates/settings/plugin/_includes/advanced.twig +++ b/src/templates/settings/plugin/_includes/advanced.twig @@ -34,28 +34,41 @@ errors: settings.getErrors("lowercaseCanonicalUrl"), }) }} - {% if seomatic.helper.craft31 %} - {{ forms.autosuggestField({ - label: "Site URL Override"|t("seomatic"), - instructions: "SEOmatic uses the Craft `siteUrl` to generate the external URLs. If you are using it in a non-standard environment, such as a headless GraphQL or ElementAPI server, you can override what it uses for the `siteUrl` below."|t("seomatic"), - suggestEnvVars: true, - suggestAliases: true, - id: "siteUrlOverride", - name: "siteUrlOverride", - value: settings.siteUrlOverride, - warning: configWarning("siteUrlOverride", "seomatic"), - errors: settings.getErrors("siteUrlOverride"), - }) }} + {% if settings.siteUrlOverride is iterable %} + <div class="field"> + <div class="heading"> + <label> + {{ "Site URL Override"|t("seomatic") }} + </label> + </div> + <div class="instructions"> + <p>{{ "The Site URL Override is overriden as an array for a multi-site setup in the `config/seomatic.php` file."|t("seomatic") }}</p> + </div> + </div> {% else %} - {{ forms.textField({ - label: "Site URL Override"|t("seomatic"), - instructions: "SEOmatic uses the Craft `siteUrl` to generate the external URLs. If you are using it in a non-standard environment, such as a headless GraphQL or ElementAPI server, you can override what it uses for the `siteUrl` below."|t("seomatic"), - id: "siteUrlOverride", - name: "siteUrlOverride", - value: settings.siteUrlOverride, - warning: configWarning("siteUrlOverride", "seomatic"), - errors: settings.getErrors("siteUrlOverride"), - }) }} + {% if seomatic.helper.craft31 %} + {{ forms.autosuggestField({ + label: "Site URL Override"|t("seomatic"), + instructions: "SEOmatic uses the Craft `siteUrl` to generate the external URLs. If you are using it in a non-standard environment, such as a headless GraphQL or ElementAPI server, you can override what it uses for the `siteUrl` below."|t("seomatic"), + suggestEnvVars: true, + suggestAliases: true, + id: "siteUrlOverride", + name: "siteUrlOverride", + value: settings.siteUrlOverride, + warning: configWarning("siteUrlOverride", "seomatic"), + errors: settings.getErrors("siteUrlOverride"), + }) }} + {% else %} + {{ forms.textField({ + label: "Site URL Override"|t("seomatic"), + instructions: "SEOmatic uses the Craft `siteUrl` to generate the external URLs. If you are using it in a non-standard environment, such as a headless GraphQL or ElementAPI server, you can override what it uses for the `siteUrl` below."|t("seomatic"), + id: "siteUrlOverride", + name: "siteUrlOverride", + value: settings.siteUrlOverride, + warning: configWarning("siteUrlOverride", "seomatic"), + errors: settings.getErrors("siteUrlOverride"), + }) }} + {% endif %} {% endif %} {{ forms.selectField({ From 1a21843883836c0bcf3cc154ac7763daabca28e6 Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Wed, 6 Dec 2023 15:38:44 -0500 Subject: [PATCH 07/17] chore: SEOmatic now requires at least Craft CMS `^3.2.0` or later --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8643b6aa6..5d3b667a7 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "vlucas/phpdotenv": "^3.0" }, "require": { - "craftcms/cms": "^3.1.19", + "craftcms/cms": "^3.2.0", "nystudio107/craft-plugin-vite": "^1.0.31", "nystudio107/craft-code-editor": "^1.0.0", "php-science/textrank": "^1.0.3", From 1a99baa8f247e2edcc2dc530b898a46c63828ea5 Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Wed, 6 Dec 2023 15:44:04 -0500 Subject: [PATCH 08/17] refactor: Switch over to listening for element changes via `Element::EVENT_AFTER_PROPAGATE` events instead of `Elements::EVENT_AFTER_SAVE_ELEMENT` and have it check the `Element::$resaving` attribute instead of `Model::$scenario` = `SCENARIO_ESSENTIALS` to determine whether sitemap queue jobs should be created ([#1388](https://github.com/nystudio107/craft-seomatic/issues/1388)) --- src/Seomatic.php | 13 +++++++------ src/services/MetaBundles.php | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Seomatic.php b/src/Seomatic.php index 017651ef0..b7caa24df 100644 --- a/src/Seomatic.php +++ b/src/Seomatic.php @@ -20,6 +20,7 @@ use craft\errors\SiteNotFoundException; use craft\events\DefineGqlTypeFieldsEvent; use craft\events\ElementEvent; +use craft\events\ModelEvent; use craft\events\PluginEvent; use craft\events\RegisterCacheOptionsEvent; use craft\events\RegisterComponentTypesEvent; @@ -464,17 +465,17 @@ function (RegisterComponentTypesEvent $event) { $event->types[] = Seomatic_MetaField::class; } ); - // Handler: Elements::EVENT_AFTER_SAVE_ELEMENT + // Handler: Element::EVENT_AFTER_PROPAGATE Event::on( - Elements::class, - Elements::EVENT_AFTER_SAVE_ELEMENT, - function (ElementEvent $event) { + Element::class, + Element::EVENT_AFTER_PROPAGATE, + static function (ModelEvent $event) { Craft::debug( - 'Elements::EVENT_AFTER_SAVE_ELEMENT', + 'Element::EVENT_AFTER_PROPAGATE', __METHOD__ ); /** @var $element Element */ - $element = $event->element; + $element = $event->sender; self::$plugin->metaBundles->invalidateMetaBundleByElement( $element, $event->isNew diff --git a/src/services/MetaBundles.php b/src/services/MetaBundles.php index 1a5f3c597..65b5432bf 100644 --- a/src/services/MetaBundles.php +++ b/src/services/MetaBundles.php @@ -748,7 +748,7 @@ public function invalidateMetaBundleByElement($element, bool $isNew = false) $this->updateMetaBundle($metaBundle, $sourceSiteId); if ($metaBundle && $metaBundle->metaSitemapVars->sitemapUrls - && $element->scenario !== Element::SCENARIO_ESSENTIALS + && !$element->resaving && Seomatic::$settings->regenerateSitemapsAutomatically) { $sitemapInvalidated = true; Seomatic::$plugin->sitemaps->invalidateSitemapCache( @@ -763,7 +763,7 @@ public function invalidateMetaBundleByElement($element, bool $isNew = false) // If we've invalidated a meta bundle, we need to invalidate the sitemap index, too if ($metaBundleInvalidated && $sitemapInvalidated - && $element->scenario !== Element::SCENARIO_ESSENTIALS) { + && !$element->resaving) { Seomatic::$plugin->sitemaps->invalidateSitemapIndexCache(); } } From 5af6dbb535c3f563815f6296b0133a98f3cfb6d2 Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Wed, 6 Dec 2023 15:44:16 -0500 Subject: [PATCH 09/17] chore: Version 3.4.68 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a9eb26ea..e1d2c62ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,12 @@ ## 3.4.68 - UNRELEASED ### Added +* SEOmatic now requires at least Craft CMS `^3.2.0` or later + * Allow the `config/seomatic.php` `siteUrlOverride` to be set to either a string, or an array of site URLs, indexed by the site handle for overriding complex headless multi-site Craft setups ([#1376](https://github.com/nystudio107/craft-seomatic/issues/1376)) ### Changed +* Switch over to listening for element changes via `Element::EVENT_AFTER_PROPAGATE` events instead of `Elements::EVENT_AFTER_SAVE_ELEMENT` and have it check the `Element::$resaving` attribute instead of `Model::$scenario` = `SCENARIO_ESSENTIALS` to determine whether sitemap queue jobs should be created ([#1388](https://github.com/nystudio107/craft-seomatic/issues/1388)) * If the Site URL Override feature is used, pass along the parameters, too, when building the URL ([#950](https://github.com/nystudio107/craft-seomatic/issues/950)) * Removed the automatic Google Sitemap ping endpoint, since [Google has deprecated it and will be removing it entirely soon](https://developers.google.com/search/blog/2023/06/sitemaps-lastmod-ping) ([#1392](https://github.com/nystudio107/craft-seomatic/issues/1392)) From 0b627b7bec70816a0ed145370ca5dc40107add6f Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Wed, 6 Dec 2023 22:27:50 -0500 Subject: [PATCH 10/17] chore: Version 3.4.68 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1d2c62ea..6fbb8fc26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## 3.4.68 - UNRELEASED ### Added * SEOmatic now requires at least Craft CMS `^3.2.0` or later - +* Added a `EVENT_INCLUDE_SITEMAP_ENTRY` event to allow plugins or modules to determine whether entries should be added to the sitemap or not ([#1393](https://github.com/nystudio107/craft-seomatic/issues/1393)) * Allow the `config/seomatic.php` `siteUrlOverride` to be set to either a string, or an array of site URLs, indexed by the site handle for overriding complex headless multi-site Craft setups ([#1376](https://github.com/nystudio107/craft-seomatic/issues/1376)) ### Changed From 78c89d8c25f89d20375dd3163832b727e1c2bead Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Wed, 6 Dec 2023 22:28:52 -0500 Subject: [PATCH 11/17] feat: Added a `EVENT_INCLUDE_SITEMAP_ENTRY` event to allow plugins or modules to determine whether entries should be added to the sitemap or not ([#1393](https://github.com/nystudio107/craft-seomatic/issues/1393)) --- src/events/IncludeSitemapEntryEvent.php | 42 +++++++++++++++++++++++++ src/helpers/Sitemap.php | 34 +++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/events/IncludeSitemapEntryEvent.php diff --git a/src/events/IncludeSitemapEntryEvent.php b/src/events/IncludeSitemapEntryEvent.php new file mode 100644 index 000000000..a022a9c0d --- /dev/null +++ b/src/events/IncludeSitemapEntryEvent.php @@ -0,0 +1,42 @@ +<?php +/** + * SEOmatic plugin for Craft CMS 3.x + * + * A turnkey SEO implementation for Craft CMS that is comprehensive, powerful, + * and flexible + * + * @link https://nystudio107.com + * @copyright Copyright (c) 2017 nystudio107 + */ + +namespace nystudio107\seomatic\events; + +use craft\base\ElementInterface; +use nystudio107\seomatic\models\MetaBundle; +use yii\base\Event; + +/** + * @author nystudio107 + * @package Seomatic + * @since 3.4.68 + */ +class IncludeSitemapEntryEvent extends Event +{ + // Properties + // ========================================================================= + + /** + * @var ElementInterface The Craft element corresponding to the sitemap entry + */ + public $element; + + /** + * @var MetaBundle The SEOmatic MetaBundle corresponding to the sitemap entry + */ + public $metaBundle; + + /** + * @var bool Whether to include the sitemap entry. + */ + public $include; +} diff --git a/src/helpers/Sitemap.php b/src/helpers/Sitemap.php index 09d8c7d8c..25deba13f 100644 --- a/src/helpers/Sitemap.php +++ b/src/helpers/Sitemap.php @@ -13,12 +13,14 @@ use craft\models\SiteGroup; use nystudio107\fastcgicachebust\FastcgiCacheBust; use nystudio107\seomatic\base\SeoElementInterface; +use nystudio107\seomatic\events\IncludeSitemapEntryEvent; use nystudio107\seomatic\fields\SeoSettings; use nystudio107\seomatic\helpers\Field as FieldHelper; use nystudio107\seomatic\jobs\GenerateSitemap; use nystudio107\seomatic\models\MetaBundle; use nystudio107\seomatic\models\SitemapTemplate; use nystudio107\seomatic\Seomatic; +use yii\base\Event; use yii\base\Exception; use yii\caching\TagDependency; use yii\helpers\Html; @@ -30,6 +32,29 @@ */ class Sitemap { + /** + * @event IncludeSitemapEntryEvent The event that is triggered when an entry is + * about to be included in a sitemap + * + * --- + * ```php + * use nystudio107\seomatic\events\IncludeSitemapEntryEvent; + * use nystudio107\seomatic\helpers\Sitemap; + * use yii\base\Event; + * Event::on(Sitemap::class, Sitemap::EVENT_INCLUDE_SITEMAP_ENTRY, function(IncludeSitemapEntryEvent $e) { + * $e->include = false; + * }); + * ``` + */ + const EVENT_INCLUDE_SITEMAP_ENTRY = 'includeSitemapEntry'; + + /** + * Generate a sitemap with the passed in $params + * + * @param array $params + * @return void + * @throws \craft\errors\SiteNotFoundException + */ public static function generateSitemap(array $params) { /** @var \craft\queue\Queue $queue */ @@ -173,8 +198,15 @@ public static function generateSitemap(array $params) if (Seomatic::$craft34) { $enabled = $element->getEnabledForSite($metaBundle->sourceSiteId); } + $enabled = $enabled && $path !== null && $metaBundle->metaSitemapVars->sitemapUrls && $robotsEnabled; + $event = new IncludeSitemapEntryEvent([ + 'include' => $enabled, + 'element' => $element, + 'metaBundle' => $metaBundle, + ]); + Event::trigger(self::class, self::EVENT_INCLUDE_SITEMAP_ENTRY, $event); // Only add in a sitemap entry if it meets our criteria - if ($enabled && $path !== null && $metaBundle->metaSitemapVars->sitemapUrls && $robotsEnabled) { + if ($event->include) { // Get the url and canonicalUrl try { $url = UrlHelper::siteUrl($path, null, null, $metaBundle->sourceSiteId); From 0f1b0f2de874e986c130e6330a7899edb0b3e85f Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Wed, 6 Dec 2023 22:33:29 -0500 Subject: [PATCH 12/17] docs: Document `IncludeSitemapEntryEvent` --- docs/docs/advanced.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/docs/advanced.md b/docs/docs/advanced.md index 050b5ae6c..c0ae0afce 100644 --- a/docs/docs/advanced.md +++ b/docs/docs/advanced.md @@ -101,6 +101,21 @@ Event::on(MetaContainers::class, MetaContainers::EVENT_INVALIDATE_CONTAINER_CACH }); ``` +### IncludeSitemapEntryEvent + + const EVENT_INCLUDE_SITEMAP_ENTRY = 'IncludeSitemapEntryEvent'; + +The event that is triggered when an entry is about to be included in a sitemap. + +```php + use nystudio107\seomatic\events\IncludeSitemapEntryEvent; + use nystudio107\seomatic\helpers\Sitemap; + use yii\base\Event; + Event::on(Sitemap::class, Sitemap::EVENT_INCLUDE_SITEMAP_ENTRY, function(IncludeSitemapEntryEvent $e) { + $e->include = false; + }); +``` + ### RegisterSitemapUrlsEvent const EVENT_REGISTER_SITEMAP_URLS = 'registerSitemapUrls'; From 1cdd96b67012beb8937d1a2587dabe3366962999 Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Thu, 7 Dec 2023 12:44:50 -0500 Subject: [PATCH 13/17] =?UTF-8?q?fix:=20Ensure=20that=20`null`=20isn?= =?UTF-8?q?=E2=80=99t=20passed=20in=20via=20`$params`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/helpers/UrlHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/UrlHelper.php b/src/helpers/UrlHelper.php index bc4f9179f..ca7f2b923 100644 --- a/src/helpers/UrlHelper.php +++ b/src/helpers/UrlHelper.php @@ -55,7 +55,7 @@ public static function siteUrl(string $path = '', $params = null, string $scheme $url = rtrim($url, '/'); } - return DynamicMeta::sanitizeUrl(parent::urlWithParams($url, $params), false, false); + return DynamicMeta::sanitizeUrl(parent::urlWithParams($url, $params ?? []), false, false); } return DynamicMeta::sanitizeUrl(parent::siteUrl($path, $params, $scheme, $siteId), false, false); From bece072f85b25bd18450c6d1b3b830627d5c8c8d Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Fri, 8 Dec 2023 19:08:52 -0500 Subject: [PATCH 14/17] refactor: Combine the `$url` & `$path` in such a way that overlapping path segments are merged together ([#1376](https://github.com/nystudio107/craft-seomatic/issues/1376)) --- src/helpers/UrlHelper.php | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/helpers/UrlHelper.php b/src/helpers/UrlHelper.php index ca7f2b923..3fad51af7 100644 --- a/src/helpers/UrlHelper.php +++ b/src/helpers/UrlHelper.php @@ -45,7 +45,7 @@ public static function siteUrl(string $path = '', $params = null, string $scheme // Extract out just the path part $parts = self::decomposeUrl($path); $path = $parts['path'] . $parts['suffix']; - $url = rtrim($siteUrl, '/') . '/' . ltrim($path, '/'); + $url = self::mergeUrlWithPath($siteUrl, $path); // Handle trailing slashes properly for generated URLs $generalConfig = Craft::$app->getConfig()->getGeneral(); if ($generalConfig->addTrailingSlashesToUrls && !preg_match('/\.[^\/]+$/', $url)) { @@ -61,6 +61,33 @@ public static function siteUrl(string $path = '', $params = null, string $scheme return DynamicMeta::sanitizeUrl(parent::siteUrl($path, $params, $scheme, $siteId), false, false); } + /** + * Merge the $url and $path together, combining any overlapping path segments + * + * @param string $url + * @param string $path + * @return string + */ + public static function mergeUrlWithPath(string $url, string $path): string + { + $overlap = 0; + $url = rtrim($url, '/'); + $path = ltrim($path, '/'); + $urlLength = strlen($url); + $urlOffset = $urlLength; + $pathLength = strlen($path); + $pathOffset = 0; + while ($urlOffset > 0 && $pathOffset < $pathLength) { + $urlOffset--; + $pathOffset++; + if (str_starts_with($path, substr($url, $urlOffset, $pathOffset))) { + $overlap = $pathOffset; + } + } + + return $url . '/' . ltrim(substr($path, $overlap), '/'); + } + /** * Return the page trigger and the value of the page trigger (null if it doesn't exist) * From 355eb3ef45a9d23a0994e64778fa9b5698d3c8c5 Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Fri, 8 Dec 2023 20:51:08 -0500 Subject: [PATCH 15/17] refactor: Remove unneeded `$urlLength` variable --- src/helpers/UrlHelper.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/helpers/UrlHelper.php b/src/helpers/UrlHelper.php index 3fad51af7..da8c9dcfd 100644 --- a/src/helpers/UrlHelper.php +++ b/src/helpers/UrlHelper.php @@ -73,8 +73,7 @@ public static function mergeUrlWithPath(string $url, string $path): string $overlap = 0; $url = rtrim($url, '/'); $path = ltrim($path, '/'); - $urlLength = strlen($url); - $urlOffset = $urlLength; + $urlOffset = strlen($url); $pathLength = strlen($path); $pathOffset = 0; while ($urlOffset > 0 && $pathOffset < $pathLength) { From 3768ab3f55f5074d59e6ab043e55c1aec73bb6ff Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Sun, 10 Dec 2023 18:28:51 -0500 Subject: [PATCH 16/17] refactor: Trim smarter --- src/helpers/UrlHelper.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/helpers/UrlHelper.php b/src/helpers/UrlHelper.php index da8c9dcfd..5ab918480 100644 --- a/src/helpers/UrlHelper.php +++ b/src/helpers/UrlHelper.php @@ -71,8 +71,6 @@ public static function siteUrl(string $path = '', $params = null, string $scheme public static function mergeUrlWithPath(string $url, string $path): string { $overlap = 0; - $url = rtrim($url, '/'); - $path = ltrim($path, '/'); $urlOffset = strlen($url); $pathLength = strlen($path); $pathOffset = 0; @@ -84,7 +82,7 @@ public static function mergeUrlWithPath(string $url, string $path): string } } - return $url . '/' . ltrim(substr($path, $overlap), '/'); + return rtrim($url, '/') . '/' . ltrim(substr($path, $overlap), '/'); } /** From e3f044f73252f8dde337de2043def6f2171b1cb4 Mon Sep 17 00:00:00 2001 From: Andrew Welch <andrew@nystudio107.com> Date: Tue, 12 Dec 2023 15:03:06 -0500 Subject: [PATCH 17/17] chore: Version 3.4.68 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fbb8fc26..4163f72d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # SEOmatic Changelog -## 3.4.68 - UNRELEASED +## 3.4.68 - 2023.12.12 ### Added * SEOmatic now requires at least Craft CMS `^3.2.0` or later * Added a `EVENT_INCLUDE_SITEMAP_ENTRY` event to allow plugins or modules to determine whether entries should be added to the sitemap or not ([#1393](https://github.com/nystudio107/craft-seomatic/issues/1393))