craftcms / cms

Build bespoke content experiences with Craft.
https://craftcms.com
Other
3.22k stars 626 forks source link

[5.x]: The `.language()` entry query parameter doesn't account for site tokens #15767

Open mmikkel opened 2 hours ago

mmikkel commented 2 hours ago

What happened?

Description

If the .language() entry query param is passed a language code that no sites use, Craft will throw an InvalidArgumentException "Invalid language" – which is generally fine.

However, this can also happen when previewing content in disabled sites, if there are no enabled sites using the language code passed to .language(); presumably because the .language() param logic does not account for languages from disabled sites even if there is a siteToken in the URL.

Effectively, this makes it impossible to preview content in disabled sites in cases where there is an entry query using the .language() query param with a language code only used by the disabled site.

Stack trace for the InvalidArgumentException is here: https://pastebin.com/t1HtpLaZ

Steps to reproduce

  1. Create a couple of sites. Have one of them use Swedish language (sv), and disable that site.
  2. Add the following Twig code to a template that will be rendered when (pre)viewing entries in the Swedish site:
    {% set entries = craft.entries.language('sv').all() %}
  3. Create an entry in the disabled Swedish site, and preview it

Expected behavior

It should be possible to preview content in disabled sites, even if the .language() param is used with the language that the disabled site uses (and no other enabled sites use)

Actual behavior

Craft throws an InvalidArgumentException if the .language() param is passed a language code that no enabled sites use, even if there is a valid siteToken in the request URL.

Craft CMS version

5.4.4

PHP version

No response

Operating system and version

No response

Database type and version

No response

Image driver and version

No response

Installed plugins and versions

-

mmikkel commented 2 hours ago

...for now, I've hotfixed the issue by overriding the sites component via config/app.php, with the following class:

class Sites extends \craft\services\Sites
{
    public function getSitesByLanguage(string $language, ?bool $withDisabled = null): array
    {
        if (!$withDisabled && Craft::$app->getRequest()->hasValidSiteToken()) {
            $withDisabled = true;
        }
        return parent::getSitesByLanguage($language, $withDisabled);
    }
}