Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased (4.6.x)

- Added live conditional field support to asset edit pages, as well as asset, user, and tag slideouts. ([#14115](https://github.com/craftcms/cms/pull/14115))
- Added `Craft.FormObserver`. ([#14114](https://github.com/craftcms/cms/pull/14114))

## Unreleased (4.5.x)
Expand Down
158 changes: 105 additions & 53 deletions src/controllers/ElementsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1417,63 +1417,12 @@ public function actionSaveDraft(): ?Response

if ($this->request->getIsCpRequest()) {
[$docTitle, $title] = $this->_editElementTitles($element);

$view = Craft::$app->getView();

$namespace = $this->request->getHeaders()->get('X-Craft-Namespace');
$fieldLayout = $element->getFieldLayout();
$form = $fieldLayout->createForm($element, false, [
'namespace' => $namespace,
'registerDeltas' => false,
'visibleElements' => $this->_visibleLayoutElements,
]);
$missingElements = [];
foreach ($form->tabs as $tab) {
if (!$tab->getUid()) {
continue;
}

$elementInfo = [];

foreach ($tab->elements as [$layoutElement, $isConditional, $elementHtml]) {
/** @var FieldLayoutComponent $layoutElement */
/** @var bool $isConditional */
/** @var string|bool $elementHtml */
if ($isConditional) {
$elementInfo[] = [
'uid' => $layoutElement->uid,
'html' => $elementHtml,
];
}
}

$missingElements[] = [
'uid' => $tab->getUid(),
'id' => $tab->getId(),
'elements' => $elementInfo,
];
}

$tabs = $form->getTabMenu();
if (count($tabs) > 1) {
$selectedTab = isset($tabs[$this->_selectedTab]) ? $this->_selectedTab : null;
$tabHtml = $view->namespaceInputs(fn() => $view->renderTemplate('_includes/tabs.twig', [
'tabs' => $tabs,
'selectedTab' => $selectedTab,
], View::TEMPLATE_MODE_CP), $namespace);
} else {
$tabHtml = null;
}

$data += $this->_fieldLayoutData($element);
$data += [
'docTitle' => $docTitle,
'title' => $title,
'tabs' => $tabHtml,
'previewTargets' => $element->getPreviewTargets(),
'missingElements' => $missingElements,
'initialDeltaValues' => $view->getInitialDeltaValues(),
'headHtml' => $view->getHeadHtml(),
'bodyHtml' => $view->getBodyHtml(),
'initialDeltaValues' => Craft::$app->getView()->getInitialDeltaValues(),
'updatedTimestamp' => $element->dateUpdated->getTimestamp(),
'canonicalUpdatedTimestamp' => $element->getCanonical()->dateUpdated->getTimestamp(),
];
Expand Down Expand Up @@ -1695,6 +1644,109 @@ public function actionRevert(): Response
]), $canonical);
}

/**
* Returns an element’s missing field layout components.
*
* @return Response|null
* @throws BadRequestHttpException
* @throws ForbiddenHttpException
* @throws ServerErrorHttpException
* @since 4.6.0
*/
public function actionUpdateFieldLayout(): ?Response
{
$this->requirePostRequest();
$this->requireCpRequest();

/** @var Element|DraftBehavior|null $element */
$element = $this->_element();

if (!$element || $element->getIsRevision()) {
throw new BadRequestHttpException('No element was identified by the request.');
}

$elementsService = Craft::$app->getElements();
$user = static::currentUser();

if (!$elementsService->canView($element, $user)) {
throw new ForbiddenHttpException('User not authorized to view this element.');
}

$this->element = $element;
$this->_applyParamsToElement($element);

// Make sure nothing just changed that would prevent the user from saving
if (!$elementsService->canView($element, $user)) {
throw new ForbiddenHttpException('User not authorized to view this element.');
}

$data = $this->_fieldLayoutData($this->element);

$data += [
'initialDeltaValues' => Craft::$app->getView()->getInitialDeltaValues(),
];

return $this->_asSuccess(Craft::t('app', '{type} saved.', [
'type' => Craft::t('app', 'Draft'),
]), $element, $data, true);
}

private function _fieldLayoutData(ElementInterface $element): array
{
$view = Craft::$app->getView();
$namespace = $this->request->getHeaders()->get('X-Craft-Namespace');
$fieldLayout = $element->getFieldLayout();
$form = $fieldLayout->createForm($element, false, [
'namespace' => $namespace,
'registerDeltas' => false,
'visibleElements' => $this->_visibleLayoutElements,
]);
$missingElements = [];
foreach ($form->tabs as $tab) {
if (!$tab->getUid()) {
continue;
}

$elementInfo = [];

foreach ($tab->elements as [$layoutElement, $isConditional, $elementHtml]) {
/** @var FieldLayoutComponent $layoutElement */
/** @var bool $isConditional */
/** @var string|bool $elementHtml */
if ($isConditional) {
$elementInfo[] = [
'uid' => $layoutElement->uid,
'html' => $elementHtml,
];
}
}

$missingElements[] = [
'uid' => $tab->getUid(),
'id' => $tab->getId(),
'elements' => $elementInfo,
];
}

$tabs = $form->getTabMenu();
if (count($tabs) > 1) {
$selectedTab = isset($tabs[$this->_selectedTab]) ? $this->_selectedTab : null;
$tabHtml = $view->namespaceInputs(fn() => $view->renderTemplate('_includes/tabs.twig', [
'tabs' => $tabs,
'selectedTab' => $selectedTab,
], View::TEMPLATE_MODE_CP), $namespace);
} else {
$tabHtml = null;
}

return [
'tabs' => $tabHtml,
'missingElements' => $missingElements,
'headHtml' => $view->getHeadHtml(),
'bodyHtml' => $view->getBodyHtml(),
];
}

/**
* Returns the HTML for a single element
*
Expand Down
2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js.map

Large diffs are not rendered by default.

Loading