Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/cms 1234 add env var support to sites language settings #14235

Merged
5 changes: 4 additions & 1 deletion CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
- Entry types are now managed independently of sections.
- Entry types are no longer required to have a Title Format, if the Title field isn’t shown.
- Entry types now have a “Show the Slug field” setting. ([#13799](https://github.com/craftcms/cms/discussions/13799))
- Sites’ Language settings can now be set to environment variables. ([#14235](https://github.com/craftcms/cms/pull/14235), [#14135](https://github.com/craftcms/cms/discussions/14135))
- Added the “Addresses” field type. ([#11438](https://github.com/craftcms/cms/discussions/11438))
- Matrix fields now manage nested entries, rather than Matrix blocks. During the upgrade, existing Matrix block types will be converted to entry types; their nested fields will be made global; and Matrix blocks will be converted to entries.
- Matrix fields now have “Entry URI Format” and “Template” settings for each site.
Expand Down Expand Up @@ -110,7 +111,7 @@
- Migrations that modify the project config no longer need to worry about whether the same changes were already applied to the incoming project config YAML files.
- Selectize menus no longer apply special styling to options with the value `new`. The `_includes/forms/selectize.twig` control panel template should be used instead (or `craft\helpers\Cp::selectizeHtml()`/`selectizeFieldHtml()`), which will append an styled “Add” option when `addOptionFn` and `addOptionLabel` settings are passed. ([#11946](https://github.com/craftcms/cms/issues/11946))
- Added the `chip()`, `customSelect()`, `disclosureMenu()`, `elementCard()`, `elementChip()`, `elementIndex()`, `iconSvg()`, and `siteMenuItems()` global functions for control panel templates.
- Added the `colorSelect` and `colorSelectField` form macros.
- Added the `colorSelect`, `colorSelectField`, `languageMenu`, and `languageMenuField` form macros.
- The `assets/move-asset` and `assets/move-folder` actions no longer include `success` keys in responses. ([#12159](https://github.com/craftcms/cms/pull/12159))
- The `assets/upload` controller action now includes `errors` object in failure responses. ([#12159](https://github.com/craftcms/cms/pull/12159))
- Element action triggers’ `validateSelection()` and `activate()` methods are now passed an `elementIndex` argument, with a reference to the trigger’s corresponding element index.
Expand Down Expand Up @@ -293,6 +294,8 @@
- Added `craft\models\FieldLayout::getThumbField()`.
- Added `craft\models\FsListing::getAdjustedUri()`.
- Added `craft\models\Section::getCpEditUrl()`.
- Added `craft\models\Site::getLanguage()`.
- Added `craft\models\Site::setLanguage()`.
- Added `craft\models\Volume::$altTranslationKeyFormat`.
- Added `craft\models\Volume::$altTranslationMethod`.
- Added `craft\models\Volume::getSubpath()`.
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Release Notes for Craft CMS 5

## Unreleased

- Sites’ Language settings can now be set to environment variables. ([#14235](https://github.com/craftcms/cms/pull/14235), [#14135](https://github.com/craftcms/cms/discussions/14135))
- Added the `languageMenu` and `languageMenuField` form macros.
- Added `craft\models\Site::getLanguage()`.
- Added `craft\models\Site::setLanguage()`.

## 5.0.0-alpha.9 - 2024-01-29

- Added live conditional field support to inline-editable Matrix blocks. ([#14223](https://github.com/craftcms/cms/pull/14223))
Expand Down
2 changes: 1 addition & 1 deletion src/config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
'id' => 'CraftCMS',
'name' => 'Craft CMS',
'version' => '5.0.0-alpha.9',
'schemaVersion' => '5.0.0.16',
'schemaVersion' => '5.0.0.17',
'minVersionRequired' => '4.4.0',
'basePath' => dirname(__DIR__), // Defines the @app alias
'runtimePath' => '@storage/runtime', // Defines the @runtime alias
Expand Down
17 changes: 0 additions & 17 deletions src/controllers/SitesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -266,30 +266,13 @@ public function actionEditSite(?int $siteId = null, ?Site $siteModel = null, ?in
],
];

$languageOptions = [];
$languageId = Craft::$app->getLocale()->getLanguageID();

foreach (Craft::$app->getI18n()->getAllLocales() as $locale) {
$languageOptions[] = [
'label' => $locale->getDisplayName(Craft::$app->language),
'value' => $locale->id,
'data' => [
'data' => [
'hint' => $locale->id,
'keywords' => $locale->getLanguageID() !== $languageId ? $locale->getDisplayName() : false,
],
],
];
}

return $this->renderTemplate('settings/sites/_edit.twig', [
'brandNewSite' => $brandNewSite,
'title' => $title,
'crumbs' => $crumbs,
'site' => $siteModel,
'groupId' => $groupId,
'groupOptions' => $groupOptions,
'languageOptions' => $languageOptions,
]);
}

Expand Down
41 changes: 5 additions & 36 deletions src/controllers/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use craft\events\InvalidUserTokenEvent;
use craft\events\LoginFailureEvent;
use craft\events\UserEvent;
use craft\helpers\App;
use craft\helpers\ArrayHelper;
use craft\helpers\Assets;
use craft\helpers\Cp;
Expand Down Expand Up @@ -1059,61 +1060,29 @@ public function actionPreferences(): Response
$response = $this->asEditScreen($user, self::SCREEN_PREFERENCES);

$i18n = Craft::$app->getI18n();
$appLocales = $i18n->getAppLocales();
ArrayHelper::multisort($appLocales, fn(Locale $locale) => $locale->getDisplayName());
$languageId = Craft::$app->getLocale()->getLanguageID();

$languageOptions = array_map(fn(Locale $locale) => [
'label' => $locale->getDisplayName(Craft::$app->language),
'value' => $locale->id,
'data' => [
'data' => [
'hint' => $locale->getLanguageID() !== $languageId ? $locale->getDisplayName() : '',
'hintLang' => $locale->id,
],
],
], $appLocales);

// user language
$userLanguage = $user->getPreferredLanguage();

if (
!$userLanguage ||
!ArrayHelper::contains($appLocales, fn(Locale $locale) => $locale->id === $userLanguage)
!ArrayHelper::contains($i18n->getAppLocales(), fn(Locale $locale) => $locale->id === App::parseEnv($userLanguage))
) {
$userLanguage = Craft::$app->language;
}

// Formatting Locale
$allLocales = $i18n->getAllLocales();
ArrayHelper::multisort($allLocales, fn(Locale $locale) => $locale->getDisplayName());

$localeOptions = [
['label' => Craft::t('app', 'Same as language'), 'value' => ''],
];
array_push($localeOptions, ...array_map(fn(Locale $locale) => [
'label' => $locale->getDisplayName(Craft::$app->language),
'value' => $locale->id,
'data' => [
'data' => [
'hint' => $locale->getLanguageID() !== $languageId ? $locale->getDisplayName() : false,
'hintLang' => $locale->id,
],
],
], $allLocales));

// user locale
$userLocale = $user->getPreferredLocale();

if (
!$userLocale ||
!ArrayHelper::contains($allLocales, fn(Locale $locale) => $locale->id === $userLocale)
!ArrayHelper::contains($i18n->getAllLocales(), fn(Locale $locale) => $locale->id === App::parseEnv($userLocale))
) {
$userLocale = Craft::$app->getConfig()->getGeneral()->defaultCpLocale;
}

$response->action('users/save-preferences');
$response->contentTemplate('users/_preferences', compact(
'languageOptions',
'localeOptions',
'userLanguage',
'userLocale',
));
Expand Down
2 changes: 1 addition & 1 deletion src/migrations/Install.php
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ public function createTables(): void
'enabled' => $this->string()->notNull()->defaultValue('true'),
'name' => $this->string()->notNull(),
'handle' => $this->string()->notNull(),
'language' => $this->string(12)->notNull(),
'language' => $this->string()->notNull(),
'hasUrls' => $this->boolean()->defaultValue(false)->notNull(),
'baseUrl' => $this->string(),
'sortOrder' => $this->smallInteger()->unsigned(),
Expand Down
34 changes: 34 additions & 0 deletions src/migrations/m240129_150719_sites_language_amend_length.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace craft\migrations;

use Craft;
use craft\db\Migration;
use craft\db\Table;

/**
* m240129_150719_sites_language_amend_length migration.
*/
class m240129_150719_sites_language_amend_length extends Migration
{
/**
* @inheritdoc
*/
public function safeUp(): bool
{
if (Craft::$app->getDb()->tableExists(Table::SITES)) {
$this->alterColumn(Table::SITES, 'language', $this->string()->notNull());
}

return true;
}

/**
* @inheritdoc
*/
public function safeDown(): bool
{
echo "m240129_150719_sites_language_amend_length cannot be reverted.\n";
return false;
}
}
38 changes: 32 additions & 6 deletions src/models/Site.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* @property bool|string $enabled Enabled
* @property string|null $baseUrl The site’s base URL
* @property string $name The site’s name
* @property string $language The site’s language
* @author Pixel & Tonic, Inc. <support@pixelandtonic.com>
* @since 3.0.0
*/
Expand All @@ -46,11 +47,6 @@ class Site extends Model
*/
public ?string $handle = null;

/**
* @var string|null Name
*/
public ?string $language = null;

/**
* @var bool Primary site?
*/
Expand Down Expand Up @@ -102,6 +98,13 @@ class Site extends Model
*/
private bool|string $_enabled = true;

/**
* @var string|null Language
* @see getLanguage()
* @see setLanguage()
*/
private ?string $_language = null;

/**
* Returns the site’s name.
*
Expand Down Expand Up @@ -182,6 +185,29 @@ public function setEnabled(bool|string $name): void
$this->_enabled = $name;
}

/**
* Returns the site’s language.
*
* @param bool $parse Whether to parse the language for an environment variable
* @return string
* @since 5.0.0
*/
public function getLanguage(bool $parse = true): string
{
return ($parse ? App::parseEnv($this->_language) : $this->_language) ?? '';
}

/**
* Sets the site’s language.
*
* @param string $language
* @since 5.0.0
*/
public function setLanguage(string $language): void
{
$this->_language = $language;
}

/**
* @inheritdoc
*/
Expand Down Expand Up @@ -299,7 +325,7 @@ public function getConfig(): array
'siteGroup' => $this->getGroup()->uid,
'name' => $this->_name,
'handle' => $this->handle,
'language' => $this->language,
'language' => $this->getLanguage(false),
'hasUrls' => $this->hasUrls,
'baseUrl' => $this->_baseUrl ?: null,
'sortOrder' => $this->sortOrder,
Expand Down
18 changes: 18 additions & 0 deletions src/templates/_includes/forms.twig
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@
{% endmacro %}


{% macro languageMenu(config) %}
{% include "_includes/forms/languageMenu" with config only %}
{% endmacro %}


{% macro fieldLayoutDesigner(config) %}
{% include "_includes/forms/fieldLayoutDesigner" with config only %}
{% endmacro %}
Expand Down Expand Up @@ -434,6 +439,19 @@
{% endmacro %}


{% macro languageMenuField(config) %}
{% set config = config|merge({id: config.id ?? "languagemenu#{random()}"}) %}
{% if (config.includeEnvVars ?? false) and config.tip is not defined and (config.value ?? '')[0:1] != '$' %}
{% set config = config|merge({
tip: 'This can be set to an environment variable with a valid language ID ({examples}).'|t('app', {
examples: '`en`/`en-GB`',
}),
}) %}
{% endif %}
{{ _self.field(config, 'template:_includes/forms/languageMenu') }}
{% endmacro %}


{% macro fieldLayoutDesignerField(config) %}
{{ _self.field({
label: 'Field Layout'|t('app'),
Expand Down
13 changes: 13 additions & 0 deletions src/templates/_includes/forms/languageMenu.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% set id = id ?? "languagemenu#{random()}" %}
{% set value = value ?? null -%}
{% set options = options ?? [] %}
{% set appOnly = appOnly ?? false %}


{% if includeEnvVars ?? false %}
{% set options = options|merge(craft.cp.getLanguageEnvOptions(appOnly)) %}
{% endif %}

{% include '_includes/forms/selectize' with {
includeEnvVars: false,
} %}
7 changes: 4 additions & 3 deletions src/templates/settings/sites/_edit.twig
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,15 @@
required: true
}) }}

{{ forms.selectizeField({
{{ forms.languageMenuField({
label: "Language"|t('app'),
instructions: "The language content in this site will use."|t('app'),
id: 'language',
name: 'language',
value: site.language,
options: languageOptions,
value: site.getLanguage(false),
options: craft.cp.getLanguageOptions(true),
errors: site.getErrors('language'),
includeEnvVars: true,
}) }}

{% if (craft.app.isMultiSite or not site.id) %}
Expand Down
9 changes: 5 additions & 4 deletions src/templates/users/_preferences.twig
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@
<div>
<h2>{{ 'General'|t('app') }}</h2>

{{ forms.selectizeField({
{{ forms.languageMenuField({
id: 'preferredLanguage',
name: 'preferredLanguage',
label: 'Language'|t('app'),
instructions: 'The language that the control panel should use.'|t('app'),
options: languageOptions,
options: craft.cp.getLanguageOptions(false, true, true),
value: userLanguage,
appOnly: true,
}) }}

{{ forms.selectizeField({
{{ forms.languageMenuField({
id: 'preferredLocale',
name: 'preferredLocale',
label: 'Formatting Locale'|t('app'),
instructions: 'The locale that should be used for date and number formatting.'|t('app'),
options: localeOptions,
options: [{'label' : 'Same as language'|t('app'), 'value' : ''}]|merge(craft.cp.getLanguageOptions(false, true)),
value: userLocale,
}) }}

Expand Down
1 change: 1 addition & 0 deletions src/translations/en/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,7 @@
'This can be left blank if you just want an unlabeled separator.' => 'This can be left blank if you just want an unlabeled separator.',
'This can be set to an environment variable matching one of the option values.' => 'This can be set to an environment variable matching one of the option values.',
'This can be set to an environment variable with a boolean value ({examples}).' => 'This can be set to an environment variable with a boolean value ({examples}).',
'This can be set to an environment variable with a valid language ID ({examples}).' => 'This can be set to an environment variable with a valid language ID ({examples}).',
'This can be set to an environment variable with a value of a [supported time zone]({url}).' => 'This can be set to an environment variable with a value of a [supported time zone]({url}).',
'This can be set to an environment variable, or a Twig template that outputs an ID.' => 'This can be set to an environment variable, or a Twig template that outputs an ID.',
'This can be set to an environment variable, or begin with an alias.' => 'This can be set to an environment variable, or begin with an alias.',
Expand Down
Loading