Skip to content

Commit

Permalink
Pass the field layout config to element conditions
Browse files Browse the repository at this point in the history
Fixes #14787
  • Loading branch information
brandonkelly committed Apr 25, 2024
1 parent 30f21f4 commit 886fed2
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 118 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
- Element tooltips now appear after a half-second delay. ([#14836](https://github.com/craftcms/cms/issues/14836))

### Administration
- Element conditions within field layout designers’ component settings now only list custom fields present in the current field layout. ([#14787](https://github.com/craftcms/cms/issues/14787))
- Added the `asyncCsrfInputs` config setting. ([#14625](https://github.com/craftcms/cms/pull/14625))
- Added the `safeMode` config setting. ([#14734](https://github.com/craftcms/cms/pull/14734))
- `resave` commands now support an `--if-invalid` option. ([#14731](https://github.com/craftcms/cms/issues/14731))

### Extensibility
- Added `craft\conditions\ConditionInterface::getBuilderConfig()`.
- Added `craft\controllers\EditUserTrait`. ([#14789](https://github.com/craftcms/cms/pull/14789))
- Added `craft\controllers\UsersController::EVENT_DEFINE_EDIT_SCREENS`. ([#14789](https://github.com/craftcms/cms/pull/14789))
- Added `craft\elements\conditions\ElementConditionInterface::setFieldLayouts()`.
- Added `craft\events\DefineEditUserScreensEvent`. ([#14789](https://github.com/craftcms/cms/pull/14789))
- Added `craft\services\ProjectConfig::getAppliedChanges()`. ([#14851](https://github.com/craftcms/cms/discussions/14851))
- Added `craft\web\Request::getBearerToken()`. ([#14784](https://github.com/craftcms/cms/pull/14784))
Expand All @@ -27,3 +30,4 @@
### System
- Batched queue jobs now set their progress based on the total progress across all batches, rather than just the current batch. ([#14817](https://github.com/craftcms/cms/pull/14817))
- Fixed a bug where ordering by a custom field would only partially work, if the custom field was included in multiple field layouts for the resulting elements. ([#14821](https://github.com/craftcms/cms/issues/14821))
- Fixed a bug where element conditions within field layout designers’ component settings weren’t listing custom fields which were just added to the layout. ([#14787](https://github.com/craftcms/cms/issues/14787))
13 changes: 12 additions & 1 deletion src/base/FieldLayoutComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ public function setUserCondition(mixed $userCondition): void
public function getElementCondition(): ?ElementConditionInterface
{
if (isset($this->_elementCondition) && !$this->_elementCondition instanceof ElementConditionInterface) {
if (is_string($this->_elementCondition)) {
$this->_elementCondition = ['class' => $this->_elementCondition];
}
$this->_elementCondition = array_merge(
['fieldLayouts' => [$this->getLayout()]],
$this->_elementCondition,
);
$this->_elementCondition = $this->_normalizeCondition($this->_elementCondition);
}

Expand Down Expand Up @@ -266,7 +273,11 @@ public function getSettingsHtml(): string
$elementType = $this->elementType ?? $this->getLayout()->type;

if ($elementType && is_subclass_of($elementType, ElementInterface::class)) {
$elementCondition = $this->getElementCondition() ?? self::defaultElementCondition($elementType);
$elementCondition = $this->getElementCondition();
if (!$elementCondition) {
$elementCondition = clone self::defaultElementCondition($elementType);
$elementCondition->setFieldLayouts([$this->getLayout()]);
}
$elementCondition->mainTag = 'div';
$elementCondition->id = 'element-condition';
$elementCondition->name = 'elementCondition';
Expand Down
12 changes: 10 additions & 2 deletions src/base/conditions/BaseCondition.php
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ public function getBuilderInnerHtml(bool $autofocusAddButton = false): string
]);

$html .= Html::hiddenInput('class', get_class($this));
$html .= Html::hiddenInput('config', Json::encode($this->config()));
$html .= Html::hiddenInput('config', Json::encode($this->getBuilderConfig()));

foreach ($this->getConditionRules() as $rule) {
try {
Expand Down Expand Up @@ -587,6 +587,14 @@ protected function defineRules(): array
];
}

/**
* @inheritdoc
*/
public function getBuilderConfig(): array
{
return $this->config();
}

/**
* @inheritdoc
*/
Expand All @@ -610,7 +618,7 @@ public function getConfig(): array
}

/**
* Returns the condition’s portable config.
* Returns the base config that should be maintained by the builder and included in the condition’s portable config.
*
* @return array
*/
Expand Down
8 changes: 8 additions & 0 deletions src/base/conditions/ConditionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ public function getBuilderHtml(): string;
*/
public function getBuilderInnerHtml(bool $autofocusAddButton = false): string;

/**
* Returns configuration that should be maintained for the builder.
*
* @return array
* @since 5.1.0
*/
public function getBuilderConfig(): array;

/**
* Returns the condition’s portable config.
*
Expand Down
102 changes: 74 additions & 28 deletions src/controllers/FieldsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
use craft\base\ElementInterface;
use craft\base\Field;
use craft\base\FieldInterface;
use craft\base\FieldLayoutComponent;
use craft\base\FieldLayoutElement;
use craft\base\FieldLayoutProviderInterface;
use craft\base\Iconic;
use craft\fieldlayoutelements\CustomField;
Expand Down Expand Up @@ -420,40 +422,40 @@ public function actionDeleteField(): ?Response
// -------------------------------------------------------------------------

/**
* Applies a field layout tab’s settings.
* Renders a field layout component’s settings.
*
* @return Response
* @throws BadRequestHttpException
* @since 4.0.0
* @since 5.1.0
*/
public function actionApplyLayoutTabSettings(): Response
public function actionRenderLayoutComponentSettings(): Response
{
$tab = new FieldLayoutTab($this->_fldComponentConfig());
$element = $this->_fldComponent();
$namespace = StringHelper::randomString(10);
$view = Craft::$app->getView();
$html = $view->namespaceInputs(fn() => $element->getSettingsHtml(), $namespace);

return $this->asJson([
'config' => $tab->toArray(),
'hasConditions' => $tab->hasConditions(),
'settingsHtml' => $html,
'namespace' => $namespace,
'headHtml' => $view->getHeadHtml(),
'bodyHtml' => $view->getBodyHtml(),
]);
}

/**
* Renders a field layout element’s settings.
* Applies a field layout tab’s settings.
*
* @since 5.0.0
* @return Response
* @throws BadRequestHttpException
* @since 4.0.0
*/
public function actionRenderLayoutElementSettings(): Response
public function actionApplyLayoutTabSettings(): Response
{
$element = Craft::$app->getFields()->createLayoutElement($this->_fldComponentConfig());
$namespace = StringHelper::randomString(10);

$view = Craft::$app->getView();
$html = $view->namespaceInputs(fn() => $element->getSettingsHtml(), $namespace);
/** @var FieldLayoutTab $tab */
$tab = $this->_fldComponent();

return $this->asJson([
'settingsHtml' => $html,
'namespace' => $namespace,
'headHtml' => $view->getHeadHtml(),
'bodyHtml' => $view->getBodyHtml(),
'config' => $tab->toArray(),
'hasConditions' => $tab->hasConditions(),
]);
}

Expand All @@ -466,7 +468,8 @@ public function actionRenderLayoutElementSettings(): Response
*/
public function actionApplyLayoutElementSettings(): Response
{
$element = Craft::$app->getFields()->createLayoutElement($this->_fldComponentConfig());
/** @var FieldLayoutElement $element */
$element = $this->_fldComponent();

if ($element instanceof CustomField) {
$field = $element->getField();
Expand Down Expand Up @@ -516,22 +519,65 @@ public function actionTableData(): Response
}

/**
* Returns the posted settings.
* Returns the field layout component being edited, populated with the posted config/settings.
*
* @return array
* @return FieldLayoutComponent
*/
private function _fldComponentConfig(): array
private function _fldComponent(): FieldLayoutComponent
{
$config = $this->request->getRequiredBodyParam('config');
$config['elementType'] = $this->request->getRequiredBodyParam('elementType');
$uid = $this->request->getRequiredBodyParam('uid');
$elementType = $this->request->getRequiredBodyParam('elementType');
$layoutConfig = $this->request->getRequiredBodyParam('layoutConfig');

if (!isset($layoutConfig['tabs'])) {
throw new BadRequestHttpException('Layout config doesn’t have any tabs.');
}

$layoutConfig['type'] = $elementType;

$componentConfig = $this->request->getBodyParam('config') ?? [];
$componentConfig['elementType'] = $elementType;
$settingsStr = $this->request->getBodyParam('settings');

if ($settingsStr !== null) {
parse_str($settingsStr, $settings);
$settingsNamespace = $this->request->getRequiredBodyParam('settingsNamespace');
$config = array_merge($config, ArrayHelper::getValue($settings, $settingsNamespace, []));
$componentConfig = array_merge($componentConfig, ArrayHelper::getValue($settings, $settingsNamespace, []));
}

return $config;
$isTab = false;

foreach ($layoutConfig['tabs'] as &$tabConfig) {
if (isset($tabConfig['uid']) && $tabConfig['uid'] === $uid) {
$isTab = true;
$tabConfig = array_merge($tabConfig, $componentConfig);
break;
}

foreach ($tabConfig['elements'] as &$elementConfig) {
if (isset($elementConfig['uid']) && $elementConfig['uid'] === $uid) {
$elementConfig = array_merge($elementConfig, $componentConfig);
break 2;
}
}
}

$layout = Craft::$app->getFields()->createLayout($layoutConfig);

if ($isTab) {
foreach ($layout->getTabs() as $tab) {
if ($tab->uid === $uid) {
return $tab;
}
}

throw new BadRequestHttpException("Invalid layout tab UUID: $uid");
}

$element = $layout->getElementByUid($uid);
if (!$element) {
throw new BadRequestHttpException("Invalid layout element UUID: $uid");
}
return $element;
}
}
44 changes: 43 additions & 1 deletion src/elements/conditions/ElementCondition.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use craft\elements\db\ElementQueryInterface;
use craft\errors\InvalidTypeException;
use craft\fields\conditions\FieldConditionRuleInterface;
use craft\models\FieldLayout;
use yii\base\InvalidConfigException;

/**
Expand Down Expand Up @@ -54,6 +55,13 @@ class ElementCondition extends BaseCondition implements ElementConditionInterfac
*/
public ?ElementInterface $referenceElement = null;

/**
* @var FieldLayout[]
* @see getFieldLayouts()
* @see setFieldLayouts()
*/
private array $_fieldLayouts;

/**
* Constructor.
*
Expand All @@ -77,8 +85,15 @@ public function __construct(?string $elementType = null, array $config = [])
parent::__construct($config);
}

/**
* @inheritdoc
*/
public function getFieldLayouts(): array
{
if (isset($this->_fieldLayouts)) {
return $this->_fieldLayouts;
}

if ($this->elementType === null) {
return [];
}
Expand All @@ -91,6 +106,21 @@ public function getFieldLayouts(): array
return Craft::$app->getFields()->getLayoutsByType($this->elementType);
}

/**
* @inheritdoc
*/
public function setFieldLayouts(array $fieldLayouts): void
{
$fieldsService = Craft::$app->getFields();
$this->_fieldLayouts = array_map(function(FieldLayout|array $fieldLayout) use ($fieldsService) {
if (is_array($fieldLayout)) {
$fieldLayout['type'] = $this->elementType;
return $fieldsService->createLayout($fieldLayout);
}
return $fieldLayout;
}, $fieldLayouts);
}

/**
* @inheritdoc
*/
Expand Down Expand Up @@ -198,10 +228,22 @@ protected function selectableConditionRules(): array
protected function defineRules(): array
{
$rules = parent::defineRules();
$rules[] = [['elementType', 'fieldContext'], 'safe'];
$rules[] = [['elementType', 'fieldLayouts', 'fieldContext'], 'safe'];
return $rules;
}

/**
* @inheritdoc
*/
public function getBuilderConfig(): array
{
$config = parent::getBuilderConfig();
if (isset($this->_fieldLayouts)) {
$config['fieldLayouts'] = array_map(fn(FieldLayout $layout) => $layout->getConfig(), $this->_fieldLayouts);
}
return $config;
}

/**
* @inheritdoc
*/
Expand Down
8 changes: 8 additions & 0 deletions src/elements/conditions/ElementConditionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ interface ElementConditionInterface extends ConditionInterface
*/
public function getFieldLayouts(): array;

/**
* Sets the possible field layouts that the condition could be working with.
*
* @param array<FieldLayout|array> $fieldLayouts
* @since 5.1.0
*/
public function setFieldLayouts(array $fieldLayouts): void;

/**
* Modifies a given query based on the configured condition rules.
*
Expand Down
Loading

0 comments on commit 886fed2

Please sign in to comment.