Skip to content

Commit

Permalink
Merge branch '5.6' into feature/cms-660-show-read-only-settings-when-…
Browse files Browse the repository at this point in the history
…allowadminchanges-false
  • Loading branch information
i-just committed Dec 9, 2024
2 parents 8327c21 + 39e90e4 commit 0dedce3
Show file tree
Hide file tree
Showing 39 changed files with 817 additions and 439 deletions.
15 changes: 13 additions & 2 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@
- Improved the accessibility of control panel icons. ([#16128](https://github.com/craftcms/cms/pull/16128))
- Improved the accessibility of Selectize inputs. ([#16110](https://github.com/craftcms/cms/pull/16110))
- Improved the accessibility of the image rotation control within the Image Editor. ([#16218](https://github.com/craftcms/cms/pull/16218))
- Improved the accessibility of action menus on the Plugins index page.
- Improved the accessibility of “More” and “Advanced” toggle triggers. ([#16293]](https://github.com/craftcms/cms/pull/16293))
- Improved the accessibility of the Craft Support widget. ([#16293]](https://github.com/craftcms/cms/pull/16293))

### Administration
- Added the “Show the ‘URL Suffix’ field” setting to Link fields. ([#15813](https://github.com/craftcms/cms/discussions/15813))
- Added the “Affiliated Site” native user field. ([#16174](https://github.com/craftcms/cms/pull/16174))
- Added support for setting site-specific email setting overrides. ([#16187](https://github.com/craftcms/cms/pull/16187))
- Added the “View users” user permission. ([#16206](https://github.com/craftcms/cms/pull/16206))
- Added the “Advanced Fields” setting to Link fields, with “Target”, “URL Suffix”, “Title Text”, “ARIA Label”, “Class Name”, “ID”, and “Relation (rel)” options. ([#15813](https://github.com/craftcms/cms/discussions/15813))
- Added the “GraphQL Mode” Link field setting. ([#16237](https://github.com/craftcms/cms/pull/16237))
- Added the “Field” entry condition rule, which replaces “Matrix field”, includes a “has a value” operator. ([#16270](https://github.com/craftcms/cms/discussions/16270))
- Section condition rules now have a “has a value” operator. ([#16270](https://github.com/craftcms/cms/discussions/16270))
- Added “Copy plugin handle” and “Copy package name” options to plugins’ action menus on the Plugins index page. ([#16281](https://github.com/craftcms/cms/discussions/16281))
- The Updates utility now shows an action menu for each plugin, with “Copy plugin handle” and “Copy package name” options. ([#16281](https://github.com/craftcms/cms/discussions/16281))
- The Queue Manager utility now shows jobs’ class names. ([#16228](https://github.com/craftcms/cms/pull/16228))
- Improved the wording of field instance action labels. ([#16261](https://github.com/craftcms/cms/discussions/16261))
- Improved the error output for nested elements when they can’t be resaved via `resave` commands.
Expand All @@ -42,6 +47,11 @@
- Added `craft\elements\User::$affiliatedSiteId`.
- Added `craft\elements\User::getAffiliatedSite()`.
- Added `craft\elements\conditions\entries\FieldConditionRule`.
- Added `craft\fields\data\LinkData::$ariaLabel`.
- Added `craft\fields\data\LinkData::$class`.
- Added `craft\fields\data\LinkData::$id`.
- Added `craft\fields\data\LinkData::$rel`.
- Added `craft\fields\data\LinkData::$title`.
- Added `craft\fields\data\LinkData::$urlSuffix`.
- Added `craft\fields\data\LinkData::getUrl()`.
- Added `craft\gql\types\LinkData`.
Expand All @@ -53,7 +63,8 @@
- `craft\elements\conditions\entries\MatrixFieldConditionRule` is now an alias of `FieldConditionRule`.
- `craft\helpers\Cp::elementIndexHtml()` now supports passing `defaultSort` in the `$config` array, when `sources` is `null`. ([#16236](https://github.com/craftcms/cms/discussions/16236))
- `craft\models\Site` now implements `craft\base\Chippable`.
- `craft\services\Revisions::createRevision()` no longer creates the revision if an `EVENT_BEFORE_CREATE_REVISION` event handler sets `$event->handled` to `true` and at least one revision already exists for the element. ([#16260](https://github.com/craftcms/cms/discussions/16260))
- `craft\services\Revisions::createRevision()` no longer creates the revision if an `EVENT_BEFORE_CREATE_REVISION` event handler sets `$event->handled` to `true` and at least one revision already exists for the element. ([#16260](https://github.com/craftcms/cms/discussions/16260))
- Deprecated `craft\fields\Link::$showTargetField`.
- Sortable checkbox selects now always display the selected options first on initial render.

### System
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
## Unreleased

- Fixed a bug where some blank user group and entry type values weren’t getting omitted from project config data. ([#16272](https://github.com/craftcms/cms/pull/16272), [#16273](https://github.com/craftcms/cms/pull/16273))
- Fixed a bug where pressing <kbd>Return</kbd> when a color text input within an editable table was focused was submitting the form rather than moving focus to the next row.
- Fixed a JavaScript error that occurred on the Plugins index page, if there were any missing plugins associated with the Craft CMS license and no plugins were Composer-installed yet.
- Fixed a bug where custom fields would stay visible within Field Layout Designer field libraries when they didn’t match the search criteria, if they had previously been dragged. ([#16277](https://github.com/craftcms/cms/issues/16277))

## 5.5.5 - 2024-12-03

Expand Down
192 changes: 142 additions & 50 deletions src/fields/Link.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use craft\helpers\Cp;
use craft\helpers\Html;
use craft\helpers\StringHelper;
use craft\helpers\Template;
use craft\validators\ArrayValidator;
use craft\validators\StringValidator;
use GraphQL\Type\Definition\InputObjectType;
Expand Down Expand Up @@ -103,6 +104,11 @@ public static function dbType(): array
'label' => Schema::TYPE_STRING,
'urlSuffix' => Schema::TYPE_STRING,
'target' => Schema::TYPE_STRING,
'title' => Schema::TYPE_STRING,
'class' => Schema::TYPE_STRING,
'id' => Schema::TYPE_STRING,
'rel' => Schema::TYPE_STRING,
'ariaLabel' => Schema::TYPE_STRING,
];
}

Expand Down Expand Up @@ -149,16 +155,11 @@ private static function types(): array
public bool $showLabelField = false;

/**
* @var bool Whether the “URL Suffix” field should be shown.
* @var string[] Attribute fields to show.
* @phpstan-var array<'urlSuffix'|'target'|'title'|'class'|'id'|'rel'|'ariaLabel'>
* @since 5.6.0
*/
public bool $showUrlSuffixField = false;

/**
* @var bool Whether the “Open in a new tab” field should be shown.
* @since 5.5.0
*/
public bool $showTargetField = false;
public array $advancedFields = [];

/**
* @var array<string,BaseLinkType>
Expand Down Expand Up @@ -208,6 +209,18 @@ public function __construct($config = [])
unset($config['placeholder']);
}

$config['advancedFields'] ??= [];

if (isset($config['showTargetField'])) {
unset($config['showTargetField']);
$config['advancedFields'][] = 'target';
}

if (isset($config['showUrlSuffixField'])) {
unset($config['showUrlSuffixField']);
$config['advancedFields'][] = 'urlSuffix';
}

if (isset($config['graphqlMode'])) {
$config['fullGraphqlData'] = ArrayHelper::remove($config, 'graphqlMode') === 'full';
}
Expand Down Expand Up @@ -242,6 +255,25 @@ protected function defineRules(): array
return $rules;
}

/**
* @deprecated in 5.6.0
* @return bool
*/
public function getShowTargetField(): bool
{
return in_array('target', $this->advancedFields);
}

/**
* @deprecated in 5.6.0
*/
public function setShowTargetField(bool $showTargetField): void
{
if (!$this->getShowTargetField()) {
$this->advancedFields[] = 'target';
}
}

/**
* Returns the link types available to the field.
*
Expand Down Expand Up @@ -384,22 +416,25 @@ public function getSettingsHtml(): ?string
'on' => $this->showLabelField,
'disabled' => $readOnly,
]) .
Cp::lightswitchFieldHtml([
'label' => Craft::t('app', 'Show the “URL Suffix” field'),
'id' => 'show-url-suffix-field',
'name' => 'showUrlSuffixField',
'on' => $this->showUrlSuffixField,
'disabled' => $readOnly,
]) .
Cp::lightswitchFieldHtml([
'label' => Craft::t('app', 'Show the “Open in a new tab” field'),
'id' => 'show-target-field',
'name' => 'showTargetField',
'on' => $this->showTargetField,
Cp::checkboxSelectFieldHtml([
'label' => Craft::t('app', 'Advanced Fields'),
'id' => 'attribute-fields',
'name' => 'advancedFields',
'options' => [
['label' => Craft::t('app', 'URL Suffix'), 'value' => 'urlSuffix'],
['label' => Craft::t('app', 'Target'), 'value' => 'target'],
['label' => Craft::t('app', 'Title Text'), 'value' => 'title'],
['label' => Craft::t('app', 'Class Name'), 'value' => 'class'],
['label' => Craft::t('app', 'ID'), 'value' => 'id'],
['label' => Template::raw(Craft::t('app', 'Relation ({ex})', ['ex' => '<code>rel</code>'])), 'value' => 'rel'],
['label' => Craft::t('app', 'ARIA Label'), 'value' => 'ariaLabel'],
],
'values' => $this->advancedFields,
'sortable' => true,
'disabled' => $readOnly,
]) .
Html::tag('hr') .
Html::a(Craft::t('app', 'Advanced'), options: [
Html::button(Craft::t('app', 'Advanced'), options: [
'class' => 'fieldtoggle',
'data' => ['target' => 'advanced'],
]) .
Expand Down Expand Up @@ -465,9 +500,7 @@ public function normalizeValue(mixed $value, ?ElementInterface $element): mixed
$type = $value->getType();
$value = [
'type' => $type,
$type => [
'value' => sprintf('{%s:%s@%s:url}', $linkedElement::refHandle(), $linkedElement->id, $element->siteId),
],
'value' => sprintf('{%s:%s@%s:url}', $linkedElement::refHandle(), $linkedElement->id, $element->siteId),
];
}
}
Expand All @@ -482,10 +515,20 @@ public function normalizeValue(mixed $value, ?ElementInterface $element): mixed
if (is_array($value)) {
$typeId = $value['type'] ?? UrlType::id();
$config = array_filter([
'label' => $this->showLabelField ? ($value['label'] ?? null) : null,
'urlSuffix' => $this->showUrlSuffixField ? ($value['urlSuffix'] ?? null) : null,
'target' => $this->showTargetField ? ($value['target'] ?? null) : null,
'label' => (!empty($value['label']) && $this->showLabelField) ? $value['label'] : null,
'urlSuffix' => (!empty($value['urlSuffix']) && in_array('urlSuffix', $this->advancedFields)) ? $value['urlSuffix'] : null,
'target' => (!empty($value['target']) && in_array('target', $this->advancedFields)) ? $value['target'] : null,
'title' => (!empty($value['title']) && in_array('title', $this->advancedFields)) ? $value['title'] : null,
'class' => (!empty($value['class']) && in_array('class', $this->advancedFields))
? (implode(' ', array_map(fn(string $class) => Html::id($class), explode(' ', $value['class']))))
: null,
'id' => (!empty($value['id']) && in_array('id', $this->advancedFields)) ? Html::id($value['id']) : null,
'rel' => (!empty($value['rel']) && in_array('rel', $this->advancedFields))
? (implode(' ', array_map(fn(string $rel) => Html::id($rel), explode(' ', $value['rel']))))
: null,
'ariaLabel' => (!empty($value['ariaLabel']) && in_array('ariaLabel', $this->advancedFields)) ? $value['ariaLabel'] : null,
]);

$value = $value['value'] ?? $value[$typeId]['value'] ?? '';

if (is_string($value)) {
Expand Down Expand Up @@ -637,7 +680,7 @@ protected function inputHtml(mixed $value, ?ElementInterface $element, bool $inl
Html::endTag('div');
}

$pane = $this->showLabelField || $this->showUrlSuffixField || $this->showTargetField;
$pane = $this->showLabelField || !empty($this->advancedFields);
$html =
Html::beginTag('div', [
'id' => $id,
Expand Down Expand Up @@ -666,29 +709,78 @@ protected function inputHtml(mixed $value, ?ElementInterface $element, bool $inl
]);
}

if ($this->showUrlSuffixField) {
$html .= Cp::textFieldHtml([
'fieldClass' => ['my-m', 'info-icon-instructions'],
'label' => Craft::t('app', 'URL Suffix'),
'instructions' => Craft::t('app', 'Query params (e.g. {ex1}) or a URI fragment (e.g. {ex2}) that should be appended to the URL.', [
'ex1' => '`?p1=foo&p2=bar`',
'ex2' => '`#anchor`',
]),
'id' => "$id-url-suffix",
'name' => "$this->handle[urlSuffix]",
'value' => $value?->urlSuffix,
]);
}
if (!empty($this->advancedFields)) {
$html .=
Html::button(Craft::t('app', 'Advanced'), options: [
'class' => ['fieldtoggle', 'mb-0'],
'data' => ['target' => "$id-advanced"],
]) .
Html::beginTag('div', [
'id' => "$id-advanced",
'class' => ['hidden', 'meta', 'pane', 'hairline'],
]);

if ($this->showTargetField) {
$html .= Cp::lightswitchFieldHtml([
'fieldClass' => 'my-m',
'label' => Craft::t('app', 'Open in a new tab'),
'id' => "$id-target",
'name' => "$this->handle[target]",
'on' => $value?->target,
'value' => '_blank',
]);
foreach ($this->advancedFields as $field) {
$html .= match ($field) {
'urlSuffix' => Cp::textFieldHtml([
'fieldClass' => 'info-icon-instructions',
'label' => Craft::t('app', 'URL Suffix'),
'instructions' => Craft::t('app', 'Query params (e.g. {ex1}) or a URI fragment (e.g. {ex2}) that should be appended to the URL.', [
'ex1' => '`?p1=foo&p2=bar`',
'ex2' => '`#anchor`',
]),
'id' => "$id-url-suffix",
'name' => "$this->handle[urlSuffix]",
'value' => $value?->urlSuffix,
]),
'target' => Cp::lightswitchFieldHtml([
'label' => Craft::t('app', 'Open in a new tab'),
'id' => "$id-target",
'name' => "$this->handle[target]",
'on' => $value?->target,
'value' => '_blank',
]),
'title' => Cp::textFieldHtml([
'label' => Craft::t('app', 'Title Text'),
'id' => "$id-title",
'name' => "$this->handle[title]",
'value' => $value?->title,
]),
'class' => Cp::textFieldHtml([
'fieldClass' => 'info-icon-instructions',
'class' => 'code',
'label' => Craft::t('app', 'Class Name'),
'instructions' => Craft::t('app', 'Separate multiple values with spaces.'),
'id' => "$id-class",
'name' => "$this->handle[class]",
'value' => $value?->class,
]),
'id' => Cp::textFieldHtml([
'class' => 'code',
'label' => Craft::t('app', 'ID'),
'id' => "$id-id",
'name' => "$this->handle[id]",
'value' => $value?->id,
]),
'rel' => Cp::textfieldHtml([
'fieldClass' => 'info-icon-instructions',
'class' => 'code',
'label' => Craft::t('app', 'Relation ({ex})', ['ex' => '<code>rel</code>']),
'instructions' => Craft::t('app', 'Separate multiple values with spaces.'),
'id' => "$id-rel",
'name' => "$this->handle[rel]",
'value' => $value?->rel,
]),
'ariaLabel' => Cp::textFieldHtml([
'label' => Craft::t('app', 'ARIA Label'),
'id' => "$id-aria-label",
'name' => "$this->handle[ariaLabel]",
'value' => $value?->ariaLabel,
]),
};
}

$html .= Html::endTag('div');
}

$html .= Html::endTag('div');
Expand Down
44 changes: 42 additions & 2 deletions src/fields/data/LinkData.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,36 @@ class LinkData extends BaseObject implements Serializable
*/
public ?string $target = null;

/**
* @var string|null The link’s `title` attribute.
* @since 5.6.0
*/
public ?string $title = null;

/**
* @var string|null The link’s `class` attribute.
* @since 5.6.0
*/
public ?string $class = null;

/**
* @var string|null The link’s `id` attribute.
* @since 5.6.0
*/
public ?string $id = null;

/**
* @var string|null The link’s `rel` attribute.
* @since 5.6.0
*/
public ?string $rel = null;

/**
* @var string|null The link’s `aria-label` attribute.
* @since 5.6.0
*/
public ?string $ariaLabel = null;

private string $renderedValue;
private ?string $label = null;

Expand Down Expand Up @@ -129,6 +159,11 @@ public function getLink(): Markup
$label = $this->getLabel();
$html = Html::a(Html::encode($label !== '' ? $label : $url), $url, [
'target' => $this->target,
'title' => $this->title,
'class' => $this->class,
'id' => $this->id,
'rel' => $this->rel,
'ariaLabel' => $this->ariaLabel,
]);
}

Expand All @@ -150,12 +185,17 @@ public function getElement(): ?ElementInterface

public function serialize(): mixed
{
return [
return array_filter([
'value' => $this->value,
'type' => $this->getType(),
'label' => $this->label,
'urlSuffix' => $this->urlSuffix,
'target' => $this->target,
];
'title' => $this->title,
'class' => $this->class,
'id' => $this->id,
'rel' => $this->rel,
'ariaLabel' => $this->ariaLabel,
]);
}
}
1 change: 1 addition & 0 deletions src/gql/types/LinkData.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ protected function resolve(mixed $source, array $arguments, mixed $context, Reso
'value' => $source->getValue(),
'label' => $source->getLabel(true),
'url' => $source->getUrl(),
'link' => $source->getLink(),
'elementType' => $source->getElement() ? $source->getElement()::class : null,
'elementId' => $source->getElement()?->id,
'elementSiteId' => $source->getElement()?->siteId,
Expand Down
Loading

0 comments on commit 0dedce3

Please sign in to comment.