diff --git a/.changeset/fuzzy-kiwis-decide.md b/.changeset/fuzzy-kiwis-decide.md
new file mode 100644
index 000000000..87aa92376
--- /dev/null
+++ b/.changeset/fuzzy-kiwis-decide.md
@@ -0,0 +1,5 @@
+---
+"@headstartwp/headstartwp": patch
+---
+
+fix: hreflangs tags on multilingual sites
diff --git a/packages/next/src/components/Yoast.tsx b/packages/next/src/components/Yoast.tsx
index 6cc0641a3..233ad0bf1 100644
--- a/packages/next/src/components/Yoast.tsx
+++ b/packages/next/src/components/Yoast.tsx
@@ -77,6 +77,10 @@ export function Yoast({ seo, useHtml = false }: Props) {
props.href = convertUrl(props.href, hostUrl, sourceUrl);
}
+ if (props.rel === 'alternate') {
+ props.href = convertUrl(props.href, hostUrl, sourceUrl);
+ }
+
if (props.property === 'og:url') {
props.content = convertUrl(props.content, hostUrl, sourceUrl);
}
diff --git a/projects/wp-nextjs/.env b/projects/wp-nextjs/.env
index ab27393b7..b013281ea 100644
--- a/projects/wp-nextjs/.env
+++ b/projects/wp-nextjs/.env
@@ -1,2 +1,2 @@
NEXT_PUBLIC_HEADLESS_WP_URL=https://js1.10up.com
-HOST_URL=https://js1.10up.com
\ No newline at end of file
+NEXT_PUBLIC_HOST_URL=https://js1.10up.com
\ No newline at end of file
diff --git a/projects/wp-nextjs/.env.development b/projects/wp-nextjs/.env.development
index e56d0dcff..7974a7168 100644
--- a/projects/wp-nextjs/.env.development
+++ b/projects/wp-nextjs/.env.development
@@ -1,5 +1,5 @@
NEXT_PUBLIC_HEADLESS_WP_URL=http://localhost:8888
-HOST_URL=http://localhost:3000
+NEXT_PUBLIC_HOST_URL=http://localhost:3000
ENABLE_POLYLANG_INTEGRATION=false
ENABLE_REQUEST_DEBUG=true
ENABLE_REDIRECT_DEBUG=true
diff --git a/projects/wp-nextjs/headstartwp.config.js b/projects/wp-nextjs/headstartwp.config.js
index f11555d7c..84b4c0492 100644
--- a/projects/wp-nextjs/headstartwp.config.js
+++ b/projects/wp-nextjs/headstartwp.config.js
@@ -8,7 +8,7 @@ module.exports = {
* The WordPress Source Url
*/
sourceUrl: process.env.NEXT_PUBLIC_HEADLESS_WP_URL,
- hostUrl: process.env.HOST_URL,
+ hostUrl: process.env.NEXT_PUBLIC_HOST_URL,
customPostTypes: [
// this is just an example
{
@@ -43,7 +43,7 @@ module.exports = {
enable: true,
},
polylang: {
- enable: process.env?.ENABLE_POLYLANG_INTEGRATION === 'true',
+ enable: process?.env?.NEXT_PUBLIC_ENABLE_POLYLANG_INTEGRATION === 'true',
},
},
diff --git a/projects/wp-nextjs/next.config.js b/projects/wp-nextjs/next.config.js
index a1a9f944c..6dd4239ec 100644
--- a/projects/wp-nextjs/next.config.js
+++ b/projects/wp-nextjs/next.config.js
@@ -14,10 +14,9 @@ const nextConfig = {
ignoreDuringBuilds: true,
},
};
-
// if you are not using polylang integration you can remove this code
// if you are replace the locales with the ones you are using
-if (process.env?.ENABLE_POLYLANG_INTEGRATION === 'true') {
+if (process.env?.NEXT_PUBLIC_ENABLE_POLYLANG_INTEGRATION === 'true') {
nextConfig.i18n = {
locales: ['en', 'pt'],
defaultLocale: 'en',
diff --git a/wp/headless-wp/.wp-env.json b/wp/headless-wp/.wp-env.json
index 1d48e55a2..13524fa13 100644
--- a/wp/headless-wp/.wp-env.json
+++ b/wp/headless-wp/.wp-env.json
@@ -4,5 +4,16 @@
"../local-plugin",
"https://downloads.wordpress.org/plugin/wordpress-seo.22.1.zip",
"https://downloads.wordpress.org/plugin/safe-redirect-manager.2.1.1.zip"
- ]
+ ],
+ "env": {
+ "tests": {
+ "plugins": [
+ ".",
+ "../local-plugin",
+ "https://downloads.wordpress.org/plugin/wordpress-seo.22.1.zip",
+ "https://downloads.wordpress.org/plugin/safe-redirect-manager.2.1.1.zip",
+ "https://downloads.wordpress.org/plugin/polylang.3.5.4.zip"
+ ]
+ }
+}
}
diff --git a/wp/headless-wp/composer.json b/wp/headless-wp/composer.json
index b095beae8..c33775371 100644
--- a/wp/headless-wp/composer.json
+++ b/wp/headless-wp/composer.json
@@ -25,6 +25,14 @@
"reference": "trunk"
}
}
+ },
+ {
+ "type": "composer",
+ "url": "https://wpackagist.org",
+ "only": [
+ "wpackagist-plugin/*",
+ "wpackagist-theme/*"
+ ]
}
],
"require-dev": {
@@ -56,7 +64,9 @@
},
"extra": {
"installer-paths": {
- "vendor/{$name}/": ["type:wordpress-plugin"]
+ "vendor/{$name}/": [
+ "type:wordpress-plugin"
+ ]
}
}
}
diff --git a/wp/headless-wp/composer.lock b/wp/headless-wp/composer.lock
index b128a4dd3..e05ab27b6 100644
--- a/wp/headless-wp/composer.lock
+++ b/wp/headless-wp/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "605e4a4bc9887f52888269f17af4203b",
+ "content-hash": "b21c0dd8f2ff8c117af692b0d6499d90",
"packages": [
{
"name": "composer/installers",
@@ -153,16 +153,16 @@
},
{
"name": "yoast/wordpress-seo",
- "version": "22.1",
+ "version": "22.2",
"source": {
"type": "git",
"url": "https://github.com/Yoast-dist/wordpress-seo.git",
- "reference": "2b76a34e4289ed777faf38e09e71c0660719f389"
+ "reference": "9702b0674264b432c14fc24e0c5c15baa05fbecf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Yoast-dist/wordpress-seo/zipball/2b76a34e4289ed777faf38e09e71c0660719f389",
- "reference": "2b76a34e4289ed777faf38e09e71c0660719f389",
+ "url": "https://api.github.com/repos/Yoast-dist/wordpress-seo/zipball/9702b0674264b432c14fc24e0c5c15baa05fbecf",
+ "reference": "9702b0674264b432c14fc24e0c5c15baa05fbecf",
"shasum": ""
},
"require": {
@@ -222,7 +222,7 @@
"source": "https://github.com/Yoast/wordpress-seo",
"wiki": "https://github.com/Yoast/wordpress-seo/wiki"
},
- "time": "2024-02-20T09:24:04+00:00"
+ "time": "2024-03-05T09:06:40+00:00"
}
],
"packages-dev": [
@@ -268,16 +268,16 @@
},
{
"name": "antecedent/patchwork",
- "version": "2.1.27",
+ "version": "2.1.28",
"source": {
"type": "git",
"url": "https://github.com/antecedent/patchwork.git",
- "reference": "16a1ab81559aabf14acb616141e801b32777f085"
+ "reference": "6b30aff81ebadf0f2feb9268d3e08385cebcc08d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/antecedent/patchwork/zipball/16a1ab81559aabf14acb616141e801b32777f085",
- "reference": "16a1ab81559aabf14acb616141e801b32777f085",
+ "url": "https://api.github.com/repos/antecedent/patchwork/zipball/6b30aff81ebadf0f2feb9268d3e08385cebcc08d",
+ "reference": "6b30aff81ebadf0f2feb9268d3e08385cebcc08d",
"shasum": ""
},
"require": {
@@ -310,9 +310,9 @@
],
"support": {
"issues": "https://github.com/antecedent/patchwork/issues",
- "source": "https://github.com/antecedent/patchwork/tree/2.1.27"
+ "source": "https://github.com/antecedent/patchwork/tree/2.1.28"
},
- "time": "2023-12-03T18:46:49+00:00"
+ "time": "2024-02-06T09:26:11+00:00"
},
{
"name": "automattic/vipwpcs",
@@ -824,16 +824,16 @@
},
{
"name": "nikic/php-parser",
- "version": "v5.0.0",
+ "version": "v5.0.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc"
+ "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
- "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13",
+ "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13",
"shasum": ""
},
"require": {
@@ -876,26 +876,27 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2"
},
- "time": "2024-01-07T17:17:35+00:00"
+ "time": "2024-03-05T20:51:40+00:00"
},
{
"name": "phar-io/manifest",
- "version": "2.0.3",
+ "version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phar-io/manifest.git",
- "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
- "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
"shasum": ""
},
"require": {
"ext-dom": "*",
+ "ext-libxml": "*",
"ext-phar": "*",
"ext-xmlwriter": "*",
"phar-io/version": "^3.0.1",
@@ -936,9 +937,15 @@
"description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
"support": {
"issues": "https://github.com/phar-io/manifest/issues",
- "source": "https://github.com/phar-io/manifest/tree/2.0.3"
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
},
- "time": "2021-07-20T11:28:43+00:00"
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
},
{
"name": "phar-io/version",
@@ -993,16 +1000,16 @@
},
{
"name": "php-stubs/wordpress-stubs",
- "version": "v6.4.1",
+ "version": "v6.4.3",
"source": {
"type": "git",
"url": "https://github.com/php-stubs/wordpress-stubs.git",
- "reference": "6d6063cf9464a306ca2a0529705d41312b08500b"
+ "reference": "6105bdab2f26c0204fe90ecc53d5684754550e8f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/6d6063cf9464a306ca2a0529705d41312b08500b",
- "reference": "6d6063cf9464a306ca2a0529705d41312b08500b",
+ "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/6105bdab2f26c0204fe90ecc53d5684754550e8f",
+ "reference": "6105bdab2f26c0204fe90ecc53d5684754550e8f",
"shasum": ""
},
"require-dev": {
@@ -1011,9 +1018,9 @@
"php": "^7.4 || ~8.0.0",
"php-stubs/generator": "^0.8.3",
"phpdocumentor/reflection-docblock": "^5.3",
- "phpstan/phpstan": "^1.10.12",
+ "phpstan/phpstan": "^1.10.49",
"phpunit/phpunit": "^9.5",
- "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.8"
+ "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.11"
},
"suggest": {
"paragonie/sodium_compat": "Pure PHP implementation of libsodium",
@@ -1034,9 +1041,9 @@
],
"support": {
"issues": "https://github.com/php-stubs/wordpress-stubs/issues",
- "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.4.1"
+ "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.4.3"
},
- "time": "2023-11-10T00:33:47+00:00"
+ "time": "2024-02-11T18:56:19+00:00"
},
{
"name": "php-stubs/wordpress-tests-stubs",
@@ -1420,16 +1427,16 @@
},
{
"name": "phpunit/php-code-coverage",
- "version": "9.2.30",
+ "version": "9.2.31",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089"
+ "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089",
- "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965",
+ "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965",
"shasum": ""
},
"require": {
@@ -1486,7 +1493,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30"
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31"
},
"funding": [
{
@@ -1494,7 +1501,7 @@
"type": "github"
}
],
- "time": "2023-12-22T06:47:57+00:00"
+ "time": "2024-03-02T06:37:42+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -1739,16 +1746,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "9.6.16",
+ "version": "9.6.17",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "3767b2c56ce02d01e3491046f33466a1ae60a37f"
+ "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3767b2c56ce02d01e3491046f33466a1ae60a37f",
- "reference": "3767b2c56ce02d01e3491046f33466a1ae60a37f",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd",
+ "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd",
"shasum": ""
},
"require": {
@@ -1822,7 +1829,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.16"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.17"
},
"funding": [
{
@@ -1838,20 +1845,20 @@
"type": "tidelift"
}
],
- "time": "2024-01-19T07:03:14+00:00"
+ "time": "2024-02-23T13:14:51+00:00"
},
{
"name": "sebastian/cli-parser",
- "version": "1.0.1",
+ "version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/cli-parser.git",
- "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
+ "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
- "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
+ "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b",
"shasum": ""
},
"require": {
@@ -1886,7 +1893,7 @@
"homepage": "https://github.com/sebastianbergmann/cli-parser",
"support": {
"issues": "https://github.com/sebastianbergmann/cli-parser/issues",
- "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2"
},
"funding": [
{
@@ -1894,7 +1901,7 @@
"type": "github"
}
],
- "time": "2020-09-28T06:08:49+00:00"
+ "time": "2024-03-02T06:27:43+00:00"
},
{
"name": "sebastian/code-unit",
@@ -2140,16 +2147,16 @@
},
{
"name": "sebastian/diff",
- "version": "4.0.5",
+ "version": "4.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131"
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
- "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc",
+ "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc",
"shasum": ""
},
"require": {
@@ -2194,7 +2201,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
- "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5"
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6"
},
"funding": [
{
@@ -2202,7 +2209,7 @@
"type": "github"
}
],
- "time": "2023-05-07T05:35:17+00:00"
+ "time": "2024-03-02T06:30:58+00:00"
},
{
"name": "sebastian/environment",
@@ -2269,16 +2276,16 @@
},
{
"name": "sebastian/exporter",
- "version": "4.0.5",
+ "version": "4.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d"
+ "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
- "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72",
+ "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72",
"shasum": ""
},
"require": {
@@ -2334,7 +2341,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
- "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5"
+ "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6"
},
"funding": [
{
@@ -2342,20 +2349,20 @@
"type": "github"
}
],
- "time": "2022-09-14T06:03:37+00:00"
+ "time": "2024-03-02T06:33:00+00:00"
},
{
"name": "sebastian/global-state",
- "version": "5.0.6",
+ "version": "5.0.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "bde739e7565280bda77be70044ac1047bc007e34"
+ "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34",
- "reference": "bde739e7565280bda77be70044ac1047bc007e34",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9",
+ "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9",
"shasum": ""
},
"require": {
@@ -2398,7 +2405,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
- "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6"
+ "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7"
},
"funding": [
{
@@ -2406,7 +2413,7 @@
"type": "github"
}
],
- "time": "2023-08-02T09:26:13+00:00"
+ "time": "2024-03-02T06:35:11+00:00"
},
{
"name": "sebastian/lines-of-code",
@@ -2864,16 +2871,16 @@
},
{
"name": "squizlabs/php_codesniffer",
- "version": "3.8.1",
+ "version": "3.9.0",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
- "reference": "14f5fff1e64118595db5408e946f3a22c75807f7"
+ "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/14f5fff1e64118595db5408e946f3a22c75807f7",
- "reference": "14f5fff1e64118595db5408e946f3a22c75807f7",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/d63cee4890a8afaf86a22e51ad4d97c91dd4579b",
+ "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b",
"shasum": ""
},
"require": {
@@ -2940,20 +2947,20 @@
"type": "open_collective"
}
],
- "time": "2024-01-11T20:47:48+00:00"
+ "time": "2024-02-16T15:06:51+00:00"
},
{
"name": "theseer/tokenizer",
- "version": "1.2.2",
+ "version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
- "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
- "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
"shasum": ""
},
"require": {
@@ -2982,7 +2989,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
- "source": "https://github.com/theseer/tokenizer/tree/1.2.2"
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
},
"funding": [
{
@@ -2990,7 +2997,7 @@
"type": "github"
}
],
- "time": "2023-11-20T00:12:19+00:00"
+ "time": "2024-03-03T12:36:25+00:00"
},
{
"name": "wordpress/wordpress-develop",
diff --git a/wp/headless-wp/includes/classes/Integrations/Polylang/PolylangYoastPresenter.php b/wp/headless-wp/includes/classes/Integrations/Polylang/PolylangYoastPresenter.php
new file mode 100644
index 000000000..10fc4fd0b
--- /dev/null
+++ b/wp/headless-wp/includes/classes/Integrations/Polylang/PolylangYoastPresenter.php
@@ -0,0 +1,158 @@
+get();
+
+ if ( ! empty( $hreflangs ) ) {
+ return $hreflangs;
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Get hreflang tags for the current post.
+ * https://polylang.pro/hreflang-tag-attributes-and-polylang-everything-you-need-to-know
+ *
+ * @return string Hreflang tags or empty string.
+ */
+ public function get() {
+ $source = $this->presentation->source;
+
+ if ( ! $source instanceof \WP_Post && ! $source instanceof \WP_Term ) {
+ return '';
+ }
+
+ $languages = pll_languages_list();
+
+ if ( 2 > count( $languages ) ) {
+ return '';
+ }
+
+ $hreflangs_output = '';
+ $hreflangs_urls = [];
+
+ $post_id = $source instanceof \WP_Post ? $source->ID : false;
+ $translations = [];
+ $is_taxonomy = false;
+ $is_homepage = false;
+ $term = null;
+
+ $paged = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_NUMBER_INT );
+
+ // Don't output anything on paged archives: see https://wordpress.org/support/topic/hreflang-on-page2
+ // Don't output anything on paged pages and paged posts
+ if ( 1 < $paged ) {
+ return '';
+ }
+
+ // Get Term or Post translations.
+ if ( $source instanceof \WP_term && pll_is_translated_taxonomy( $source->taxonomy ) ) {
+ $term = $source;
+ $translations = pll_get_term_translations( $term->term_id );
+ $is_taxonomy = true;
+ } else {
+ $translations = pll_get_post_translations( $post_id );
+ }
+
+ if ( empty( $translations ) ) {
+ return '';
+ }
+
+ // Herflangs for archive first pages.
+ if ( $is_taxonomy ) {
+ foreach ( $translations as $language => $translated_id ) {
+ // Get term object for translations and only include hreflang tags if term has posts.
+ $term_obj = $translated_id === $term->term_id ? $term : get_term( $translated_id, $term->taxonomy );
+ if ( 0 === $term_obj->count ) {
+ continue;
+ }
+
+ $hreflangs_urls[ $language ] = get_term_link( $translated_id );
+ }
+ } else {
+ $homepages = $this->get_homepage_post_ids();
+
+ // Herflangs for single posts.
+ foreach ( $translations as $language => $translated_id ) {
+
+ if ( in_array( $translated_id, $homepages, true ) ) {
+ $is_homepage = true;
+ }
+
+ // Only add hreflang tags for published posts.
+ if ( 'publish' !== get_post_status( $translated_id ) ) {
+ continue;
+ }
+
+ $hreflangs_urls[ $language ] = get_the_permalink( $translated_id );
+ }
+ }
+
+ // Do not add hreflang tags if page has no translations, count is 1 as it includes the existing post.
+ if ( empty( $hreflangs_urls ) || ( is_array( $hreflangs_urls ) && count( $hreflangs_urls ) <= 1 ) ) {
+ return '';
+ }
+
+ // Polylang generates an x-default hreflang only for the homepage only as recommended by Google.
+ $polylang_settings = get_option( 'polylang' );
+ if ( $is_homepage && ! $polylang_settings['hide_default'] && $polylang_settings['force_lang'] < 3 ) {
+ $hreflangs_urls['x-default'] = home_url( '/' );
+ }
+
+ /**
+ * Filters the list of rel hreflang attributes
+ *
+ * @param array $hreflangs_urls Array of urls with language codes as keys
+ */
+ $hreflangs_urls = apply_filters( 'tenup_headless_wp_hreflang_attributes', $hreflangs_urls );
+
+ // Build the hreflang tags if we have translations.
+ foreach ( $hreflangs_urls as $lang => $url ) {
+ $hreflangs_output .= sprintf( '' . "\n", esc_url( $url ), esc_attr( $lang ) );
+ }
+
+ return $hreflangs_output;
+ }
+
+ /**
+ * Return list of homepage IDs for default lang and translated homepages.
+ *
+ * @return array List of homepage IDs.
+ */
+ public function get_homepage_post_ids() {
+ $home_id = get_option( 'page_on_front' );
+ if ( empty( $home_id ) ) {
+ return [];
+ }
+
+ $result = [ $home_id ];
+
+ if ( function_exists( 'pll_get_post_translations' ) ) {
+ $result = pll_get_post_translations( $home_id ); // includes the default homepage lang.
+ }
+
+ return $result;
+ }
+}
diff --git a/wp/headless-wp/includes/classes/Integrations/YoastSEO.php b/wp/headless-wp/includes/classes/Integrations/YoastSEO.php
index 060aebd71..f4bdd7dd6 100644
--- a/wp/headless-wp/includes/classes/Integrations/YoastSEO.php
+++ b/wp/headless-wp/includes/classes/Integrations/YoastSEO.php
@@ -33,6 +33,9 @@ public function register() {
add_filter( 'wpseo_title', [ $this, 'override_search_title' ], 10, 1 );
add_filter( 'wpseo_opengraph_title', [ $this, 'override_search_title' ], 10, 1 );
add_filter( 'wpseo_opengraph_url', [ $this, 'override_search_canonical' ], 10, 1 );
+
+ // Introduce hereflangs presenter to Yoast list of presenters.
+ add_action( 'rest_api_init', [ $this, 'wpseo_rest_api_hreflang_presenter' ], 10, 0 );
}
/**
@@ -288,4 +291,35 @@ public function override_search_canonical( $canonical ) {
return $canonical;
}
+
+ /**
+ * Register custom presenter to handle hreflang tags in Yoast REST response.
+ * Called on rest_api_init
+ *
+ * Polylang adds hreflang tags by hooking into wp_head which only runs on the front end on a
+ * traditional WordPress setup.
+ *
+ * @return array
+ */
+ public function wpseo_rest_api_hreflang_presenter() {
+
+ $enable_hreflang = apply_filters( 'tenup_headless_wp_enable_hreflangs', true );
+
+ if ( ! $enable_hreflang ) {
+ return;
+ }
+
+ add_filter(
+ 'wpseo_frontend_presenters',
+ function ( $presenters ) {
+ if ( ! class_exists( '\HeadlessWP\Integrations\Polylang\PolylangYoastPresenter' ) ) {
+ return $presenters;
+ }
+
+ $presenters[] = new \HeadlessWP\Integrations\Polylang\PolylangYoastPresenter();
+
+ return $presenters;
+ }
+ );
+ }
}
diff --git a/wp/headless-wp/tests/php/bootstrap.php b/wp/headless-wp/tests/php/bootstrap.php
index 4883880e8..9c01bfdc0 100644
--- a/wp/headless-wp/tests/php/bootstrap.php
+++ b/wp/headless-wp/tests/php/bootstrap.php
@@ -18,6 +18,7 @@
require_once PROJECT_ROOT . '/vendor/autoload.php';
require_once PROJECT_ROOT . '/vendor/yoast/wp-test-utils/src/WPIntegration/bootstrap-functions.php';
+
$_tests_dir = WPIntegration\get_path_to_wp_test_dir();
require_once $_tests_dir . '/includes/functions.php';
@@ -38,8 +39,11 @@ function tests_add_filter( ...$args ) {}
* Manually load the plugin being tested.
*/
function _manually_load_plugin() {
+ require_once WP_PLUGIN_DIR . '/polylang/polylang.php';
+ require_once WP_PLUGIN_DIR . '/wordpress-seo/wp-seo.php';
require_once dirname( __DIR__, 2 ) . '/plugin.php';
}
+
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
// Require all files in the fixtures directory.
@@ -48,3 +52,5 @@ function _manually_load_plugin() {
}
WPIntegration\bootstrap_it();
+
+require_once PROJECT_ROOT . '/tests/php/includes/PLLUnitTestCase.php';
diff --git a/wp/headless-wp/tests/php/includes/PLLUnitTestCase.php b/wp/headless-wp/tests/php/includes/PLLUnitTestCase.php
new file mode 100644
index 000000000..eefc6ac0c
--- /dev/null
+++ b/wp/headless-wp/tests/php/includes/PLLUnitTestCase.php
@@ -0,0 +1,152 @@
+options = \PLL_Install::get_default_options();
+ self::$polylang->options['hide_default'] = 0; // Force option to pre 2.1.5 value otherwise phpunit tests break on Travis
+ self::$polylang->model = new \PLL_Admin_Model( self::$polylang->options );
+ self::$polylang->links_model = self::$polylang->model->get_links_model(); // We always need a links model due to PLL_Language::set_home_url()
+
+ $_SERVER['SCRIPT_FILENAME'] = '/index.php'; // To pass the test in PLL_Choose_Lang::init() by default
+
+ require_once POLYLANG_DIR . '/include/api.php';
+ $GLOBALS['polylang'] = &self::$polylang;
+ }
+
+ /**
+ * Clears up the test class
+ *
+ * @return void
+ */
+ public static function wpTearDownAfterClass() {
+ self::delete_all_languages();
+ }
+
+ /**
+ * Sets up each test
+ *
+ * @return void
+ */
+ public function setUp(): void {
+ parent::setUp();
+
+ add_filter( 'wp_doing_ajax', '__return_false' );
+ }
+
+ /**
+ * Clears up each test
+ *
+ * @return void
+ */
+ public function tearDown(): void {
+ parent::tearDown();
+
+ unset( $GLOBALS['wp_settings_errors'] );
+ self::$polylang->model->clean_languages_cache(); // We must do it before database ROLLBACK otherwhise it is impossible to delete the transient
+ }
+
+ /**
+ * Helper function to create a language
+ *
+ * @param string $locale The locale to create
+ * @param array $args Arguments for the language
+ *
+ * @return void
+ */
+ protected static function create_language( $locale, $args = [] ) {
+ $languages = include POLYLANG_DIR . '/settings/languages.php';
+ $values = $languages[ $locale ];
+
+ $values['slug'] = $values['code'];
+ $values['rtl'] = (int) ( 'rtl' === $values['dir'] );
+ $values['term_group'] = 0; // default term_group
+
+ $args = array_merge( $values, $args );
+ self::$polylang->model->add_language( $args );
+ unset( $GLOBALS['wp_settings_errors'] ); // Clean "errors"
+ }
+
+ /**
+ * A helper function to delete all languages
+ *
+ * @return void
+ */
+ protected static function delete_all_languages() {
+ $languages = self::$polylang->model->get_languages_list();
+ if ( is_array( $languages ) ) {
+ // Delete the default categories first
+ $tt = wp_get_object_terms( get_option( 'default_category' ), 'term_translations' );
+ $terms = self::$polylang->model->term->get_translations( get_option( 'default_category' ) );
+
+ wp_delete_term( $tt, 'term_translations' );
+
+ foreach ( $terms as $t ) {
+ wp_delete_term( $t, 'category' );
+ }
+
+ foreach ( $languages as $lang ) {
+ self::$polylang->model->delete_language( $lang->term_id );
+ unset( $GLOBALS['wp_settings_errors'] );
+ }
+ }
+ }
+
+ /**
+ * Switches to the given language
+ *
+ * @param string $lang The language to switch to
+ *
+ * @return void
+ */
+ protected static function switch_language( string $lang ): void {
+ $language = \PLL()->model->get_language( $lang );
+
+ if ( false !== $language ) {
+ \PLL()->curlang = $language;
+ }
+ }
+
+ /**
+ * Backport assertNotFalse to PHPUnit 3.6.12 which only runs in PHP 5.2.
+ *
+ * @param bool $condition The candition
+ * @param string $message The message
+ */
+ public static function assertNotFalse( $condition, $message = '' ): void {
+ if ( version_compare( phpversion(), '5.3', '<' ) ) {
+ self::assertThat( $condition, self::logicalNot( self::isFalse() ), $message );
+ } else {
+ parent::assertNotFalse( $condition, $message );
+ }
+ }
+}
diff --git a/wp/headless-wp/tests/php/includes/TestGutenbergIntegration.php b/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php
similarity index 100%
rename from wp/headless-wp/tests/php/includes/TestGutenbergIntegration.php
rename to wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php
diff --git a/wp/headless-wp/tests/php/tests/TestPolylangIntegration.php b/wp/headless-wp/tests/php/tests/TestPolylangIntegration.php
new file mode 100644
index 000000000..ba4778d91
--- /dev/null
+++ b/wp/headless-wp/tests/php/tests/TestPolylangIntegration.php
@@ -0,0 +1,260 @@
+links_model );
+ self::$polylang->init();
+
+ self::$rest_server = rest_get_server();
+ }
+
+ /**
+ * Sets up the tests
+ *
+ * @return void
+ */
+ public function setUp(): void {
+ parent::setUp();
+
+ /**
+ * The rewrite class
+ *
+ * @var \WP_Rewrite $wp_rewrite
+ */
+ global $wp_rewrite;
+
+ $this->wp_rewrite = $wp_rewrite;
+
+ /**
+ * Change the permalink structure
+ */
+ $this->wp_rewrite->init();
+ $this->wp_rewrite->set_permalink_structure( '/%postname%/' );
+ }
+
+ /**
+ * Returns the yoast head for a post
+ *
+ * @param integer $post_id The post id
+ * @param string $post_type The post type
+ *
+ * @return string
+ */
+ protected function getYoastHeadForPost( int $post_id, string $post_type = 'post' ): string {
+ $post_type = get_post_type_object( $post_type );
+ $request = new WP_REST_Request( 'GET', "/wp/v2/$post_type->rest_base/$post_id" );
+ $response = rest_do_request( $request );
+ $data = self::$rest_server->response_to_data( $response, false );
+
+ return $data['yoast_head'] ?? '';
+ }
+
+ /**
+ * Returns the yoast head for a term
+ *
+ * @param string $post_type The post type we're fetching from
+ * @param int $term_id The term id
+ *
+ * @return string
+ */
+ protected function getYoastHeadForTerm( string $post_type, int $term_id ): string {
+ $post_type = get_post_type_object( $post_type );
+ $request = new WP_REST_Request( 'GET', "/wp/v2/$post_type->rest_base", [ 'categories' => $term_id ] );
+ $response = rest_do_request( $request );
+ $data = self::$rest_server->response_to_data( $response, true );
+
+ return $data[0]['_embedded']['wp:term'][0][0]['yoast_head'] ?? '';
+ }
+
+ /**
+ * Test hreflang on single posts
+ *
+ * @return void
+ */
+ public function test_hreflang_on_single_posts() {
+ $english_post = $this->factory()->post->create_and_get(
+ [
+ 'post_title' => '[EN] Post',
+ 'post_status' => 'publish',
+ 'post_content' => 'english post',
+ 'post_type' => 'post',
+ ]
+ );
+
+ \pll_set_post_language( $english_post->ID, 'en' );
+
+ $portuguese_post = $this->factory()->post->create_and_get(
+ [
+ 'post_title' => '[PT_BR] Post',
+ 'post_status' => 'publish',
+ 'post_content' => 'portugese post',
+ 'post_type' => 'post',
+ ]
+ );
+
+ \pll_set_post_language( $portuguese_post->ID, 'pt' );
+
+ \pll_save_post_translations(
+ [
+ 'en' => $english_post->ID,
+ 'pt' => $portuguese_post->ID,
+ ]
+ );
+
+ $yoast_head = $this->getYoastHeadForPost( $english_post->ID );
+
+ $this->assertNotFalse( strpos( $yoast_head, '' ), 'hreflang was not found' );
+ $this->assertNotFalse( strpos( $yoast_head, '' ), 'hreflang was not found' );
+
+ $yoast_head = $this->getYoastHeadForPost( $portuguese_post->ID );
+
+ $this->assertNotFalse( strpos( $yoast_head, '' ), 'hreflang was not found' );
+ $this->assertNotFalse( strpos( $yoast_head, '' ), 'hreflang was not found' );
+ }
+
+ /**
+ * Test hreflang on homepage
+ *
+ * @return void
+ */
+ public function test_hreflang_on_homepage() {
+ $english_page = $this->factory()->post->create(
+ [
+ 'post_title' => '[EN] home page',
+ 'post_status' => 'publish',
+ 'post_content' => 'english page',
+ 'post_type' => 'page',
+ ]
+ );
+
+ \pll_set_post_language( $english_page, 'en' );
+
+ $portuguese_page = $this->factory()->post->create(
+ [
+ 'post_title' => '[PT_BR] home Page',
+ 'post_status' => 'publish',
+ 'post_content' => 'portugese page',
+ 'post_type' => 'page',
+ ]
+ );
+
+ \pll_set_post_language( $portuguese_page, 'pt' );
+
+ \pll_save_post_translations(
+ [
+ 'en' => $english_page,
+ 'pt' => $portuguese_page,
+ ]
+ );
+
+ update_option( 'page_on_front', $english_page );
+
+ $yoast_head = $this->getYoastHeadForPost( $english_page, 'page' );
+
+ $this->assertNotFalse( strpos( $yoast_head, '' ), 'hreflang was not found' );
+ $this->assertNotFalse( strpos( $yoast_head, '' ), 'hreflang was not found' );
+ $this->assertNotFalse( strpos( $yoast_head, '' ), 'hreflang was not found' );
+ }
+
+ /**
+ * Tests hrelang on taxonomy archives
+ *
+ * @return void
+ */
+ public function test_hreflang_on_taxonomy_archive() {
+ $cat_en = $this->factory()->term->create_and_get(
+ [
+ 'taxonomy' => 'category',
+ ]
+ );
+
+ $cat_en_id = $cat_en->term_id;
+
+ \pll_set_term_language( $cat_en_id, 'en' );
+
+ $cat_pt = $this->factory()->term->create_and_get(
+ [
+ 'taxonomy' => 'category',
+ ]
+ );
+
+ $cat_pt_id = $cat_pt->term_id;
+
+ \pll_set_term_language( $cat_pt_id, 'pt' );
+
+ $posts_en = $this->factory()->post->create_many(
+ 5,
+ [
+ 'post_category' => [ $cat_en_id ],
+ ]
+ );
+
+ foreach ( $posts_en as $post_en ) {
+ \pll_set_post_language( $post_en, 'en' );
+ }
+
+ $posts_pt = $this->factory()->post->create_many(
+ 5,
+ [
+ 'post_category' => [ $cat_pt_id ],
+ ]
+ );
+
+ foreach ( $posts_pt as $post_pt ) {
+ \pll_set_post_language( $post_pt, 'pt' );
+ }
+
+ \pll_save_term_translations(
+ [
+ 'en' => $cat_en_id,
+ 'pt' => $cat_pt_id,
+ ]
+ );
+
+ $yoast_head = $this->getYoastHeadForTerm( 'post', $cat_pt_id );
+
+ $this->assertNotFalse( strpos( $yoast_head, sprintf( '', $cat_en_id ) ), 'hreflang was not found' );
+ $this->assertNotFalse( strpos( $yoast_head, sprintf( '', $cat_pt_id ) ), 'hreflang was not found' );
+ }
+}
diff --git a/wp/headless-wp/tests/php/includes/TestPreview.php b/wp/headless-wp/tests/php/tests/TestPreview.php
similarity index 100%
rename from wp/headless-wp/tests/php/includes/TestPreview.php
rename to wp/headless-wp/tests/php/tests/TestPreview.php