Skip to content

Commit

Permalink
Merge branch 'develop' into 4.1
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/web/assets/cp/dist/cp.js
#	src/web/assets/cp/dist/cp.js.map
  • Loading branch information
brandonkelly committed May 19, 2022
2 parents f45c185 + 1926ca4 commit 6c48a3b
Show file tree
Hide file tree
Showing 35 changed files with 351 additions and 206 deletions.
35 changes: 32 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,39 @@
# Release Notes for Craft CMS 4

## 4.0.2 - 2022-05-11
## Unreleased

### Added
- Added `craft\elements\db\ElementQuery::prepareSubquery()`.

### Changed
- `elements/*` actions no longer include custom field values in the response data, improving performance.
- Element edit pages now disable pointer events on the content container for 300 milliseconds after the “Showing your unsaved changes” notice is displayed. ([#11229](https://github.com/craftcms/cms/issues/11229))
- Users can now create drafts for entries they have permission to view, but not save. ([#11249](https://github.com/craftcms/cms/issues/11249))
- User Group condition rules are no longer available in element conditions when no user groups exist. ([#11252](https://github.com/craftcms/cms/issues/11252))

### Fixed
- Fixed a bug where dynamically-defined image transforms weren’t respecting the `format` param, unless the `generateTransformsBeforePageLoad` config setting was enabled.
- Fixed a bug where Table fields with Min Rows and Max Rows set to `1` were still showing a delete button. ([#11211](https://github.com/craftcms/cms/issues/11211))
- Fixed an error that could occur when saving an Assets field that was restricted to a single location, at the root of a volume. ([#11212](https://github.com/craftcms/cms/issues/11212))
- Fixed an error that could occur after a queue job execution had finished. ([#11213](https://github.com/craftcms/cms/issues/11213))
- Fixed an error that could occur when saving an entry with Matrix blocks. ([#11155](https://github.com/craftcms/cms/issues/11155))
- Fixed an error that occurred when saving a GraphQL schema without a scope. ([#11240](https://github.com/craftcms/cms/issues/11240))
- Fixed an error that could occur when editing the public GraphQL schema, if a public token existed in the project config, but not the database. ([#11218](https://github.com/craftcms/cms/issues/11218))
- Fixed some bugs with inconsistent asset indexing on Windows. ([#11174](https://github.com/craftcms/cms/issues/11174)), ([#11219](https://github.com/craftcms/cms/issues/11219))
- Fixed a bug where custom fields weren’t available to be included as table attributes. ([#11222](https://github.com/craftcms/cms/issues/11222))
- Fixed a bug where Alternative Text wasn’t available to be included as a table attribute. ([#11222](https://github.com/craftcms/cms/issues/11222))`immediately`
- Fixed a JavaScript error that broke Matrix fields with Min Blocks and Max Blocks both set to `1`. ([#11233](https://github.com/craftcms/cms/issues/11233))
- Fixed a bug where request context logs could appear when nothing else was logged. ([#11141](https://github.com/craftcms/cms/issues/11141))
- Fixed a bug where stack traces could be erroneously filtered from logs.
- Fixed a bug where removing an element from a relational field within an element editor could cause the editor to create a provisional draft, even if the element type didn’t support drafts. ([#11242](https://github.com/craftcms/cms/issues/11242))
- Fixed a bug where draft editor pages had two identical “Save and continue editing” alternate form actions.
- Fixed a JavaScript warning that occurred when viewing an element edit page, if the user didn’t have permission to edit it.
- Fixed a bug where asset selector modals weren’t fully initializing for Assets fields, if they were targeting the user’s temp folder. ([#11254](https://github.com/craftcms/cms/issues/11254))
- Fixed an error that occurred when saving an entry via a GraphQL mutation. ([#11258](https://github.com/craftcms/cms/issues/11258))

## 4.0.2 - 2022-05-11

### Changed
- `elements/*` actions no longer include custom field values in the response data, improving performance.
- Garnish menu buttons are now aware of the `disabled` attribute. ([#11128](https://github.com/craftcms/cms/issues/11128))

### Fixed
Expand All @@ -28,7 +56,7 @@
- Fixed a bug where Money field labels’ `for` attributes weren’t referencing the correct input ID. ([#11016](https://github.com/craftcms/cms/pull/11016))
- Fixed a bug where Money field inputs weren’t getting `aria-describedby` attributes. ([#11016](https://github.com/craftcms/cms/pull/11016))
- Fixed an error that occurred when loading an edit screen for an element type that didn’t have a field layout. ([#11110](https://github.com/craftcms/cms/pull/11110))
- Fixed a bug where condition rules they weren’t selectable (per `isSelectable()`) were still visible in the rule dropdown menu. ([#11104](https://github.com/craftcms/cms/pull/11104))
- Fixed a bug where condition rules that weren’t selectable (per `isSelectable()`) were still visible in the rule dropdown menu. ([#11104](https://github.com/craftcms/cms/pull/11104))
- Fixed a bug where element edit pages could reload themselves immediately after saving the element. ([#11084](https://github.com/craftcms/cms/issues/11084))
- Fixed a bug where tabs weren’t interactive after changing an entry’s type, if the new entry type didn’t have a tab of the same name as the previously-selected tab. ([#11093](https://github.com/craftcms/cms/issues/11093))
- Fixed a bug where Twig syntax errors weren’t being handled properly. ([#11108](https://github.com/craftcms/cms/issues/11108))
Expand Down Expand Up @@ -831,6 +859,7 @@
- Removed `craft\services\AssetIndexer::getMissingFiles()`.
- Removed `craft\services\AssetIndexer::prepareIndexList()`.
- Removed `craft\services\AssetIndexer::processIndexForVolume()`.
- Removed `craft\services\Assets::$generatePendingTransformsViaQueue`.
- Removed `craft\services\Assets::EVENT_GET_ASSET_THUMB_URL`.
- Removed `craft\services\Assets::EVENT_GET_THUMB_PATH`.
- Removed `craft\services\Assets::getThumbPath()`.
Expand Down
2 changes: 1 addition & 1 deletion src/Craft.php
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ private static function _generateCustomFieldBehavior(array $fieldHandles, ?strin

foreach ($fieldHandles as $handle => $types) {
$methods[] = <<<EOD
* @method \$this $handle(mixed \$value) Sets the [[$handle]] property
* @method static $handle(mixed \$value) Sets the [[$handle]] property
EOD;

$handles[] = <<<EOD
Expand Down
131 changes: 69 additions & 62 deletions src/controllers/ElementsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,7 @@ public function actionEdit(?ElementInterface $element, ?int $elementId = null):
]
);
}
}

if ($isCurrent) {
if ($canSaveCanonical) {
if ($isUnpublishedDraft) {
$response->addAltAction(Craft::t('app', 'Save {type}', [
Expand Down Expand Up @@ -531,37 +529,28 @@ public function actionEdit(?ElementInterface $element, ?int $elementId = null):
]),
]);
}
} elseif ($isDraft) {
$response->addAltAction(Craft::t('app', 'Save and continue editing'), [
'redirect' => '{cpEditUrl}',
'action' => 'elements/save-draft',
'shortcut' => true,
'retainScroll' => true,
]);

if ($canDeleteDraft) {
if ($canDeleteForSite) {
$response->addAltAction(Craft::t('app', 'Delete {type} for this site', [
'type' => Craft::t('app', 'draft'),
]), [
'destructive' => true,
'action' => 'elements/delete-for-site',
'redirect' => "$redirectUrl#",
'confirm' => Craft::t('app', 'Are you sure you want to delete the {type} for this site?', compact('type')),
]);
}

$response->addAltAction(Craft::t('app', 'Delete {type}', [
} elseif ($isDraft && $canDeleteDraft) {
if ($canDeleteForSite) {
$response->addAltAction(Craft::t('app', 'Delete {type} for this site', [
'type' => Craft::t('app', 'draft'),
]), [
'destructive' => true,
'action' => 'elements/delete-draft',
'redirect' => $canonical->getCpEditUrl(),
'confirm' => Craft::t('app', 'Are you sure you want to delete this {type}?', [
'type' => Craft::t('app', 'draft'),
]),
'action' => 'elements/delete-for-site',
'redirect' => "$redirectUrl#",
'confirm' => Craft::t('app', 'Are you sure you want to delete the {type} for this site?', compact('type')),
]);
}

$response->addAltAction(Craft::t('app', 'Delete {type}', [
'type' => Craft::t('app', 'draft'),
]), [
'destructive' => true,
'action' => 'elements/delete-draft',
'redirect' => $canonical->getCpEditUrl(),
'confirm' => Craft::t('app', 'Are you sure you want to delete this {type}?', [
'type' => Craft::t('app', 'draft'),
]),
]);
}
}

Expand Down Expand Up @@ -647,44 +636,58 @@ private function _additionalButtons(
): string {
$components = [];

if ($canSave) {
if ($previewTargets) {
// Preview (View will be added later by JS)
$components[] =
Html::beginTag('div', [
'class' => ['preview-btn-container', 'btngroup'],
]) .
($enablePreview
? Html::button(Craft::t('app', 'Preview'), [
'class' => ['preview-btn', 'btn'],
])
: '') .
Html::endTag('div');
}
// Preview (View will be added later by JS)
if ($canSave && $previewTargets) {
$components[] =
Html::beginTag('div', [
'class' => ['preview-btn-container', 'btngroup'],
]) .
($enablePreview
? Html::button(Craft::t('app', 'Preview'), [
'class' => ['preview-btn', 'btn'],
])
: '') .
Html::endTag('div');
}

if ($isCurrent) {
if (!$isUnpublishedDraft && $canCreateDrafts) {
// Create a draft
$components[] = Html::button(Craft::t('app', 'Create a draft'), [
'class' => ['btn', 'formsubmit'],
'data' => [
'action' => 'elements/save-draft',
'redirect' => Craft::$app->getSecurity()->hashData('{cpEditUrl}'),
'params' => ['dropProvisional' => 1],
],
]);
}
} elseif ($isDraft && $canSaveCanonical) {
// Apply draft
$components[] = Html::button(Craft::t('app', 'Apply draft'), [
'class' => ['btn', 'secondary', 'formsubmit'],
// Create a draft
if ($isCurrent && !$isUnpublishedDraft && $canCreateDrafts) {
if ($canSave) {
$components[] = Html::button(Craft::t('app', 'Create a draft'), [
'class' => ['btn', 'formsubmit'],
'data' => [
'action' => 'elements/apply-draft',
'action' => 'elements/save-draft',
'redirect' => Craft::$app->getSecurity()->hashData('{cpEditUrl}'),
'params' => ['dropProvisional' => 1],
],
]);
} else {
$components[] = Html::beginForm() .
Html::actionInput('elements/save-draft') .
Html::redirectInput('{cpEditUrl}') .
Html::hiddenInput('elementId', (string)$canonical->id) .
Html::beginTag('div', ['class' => 'secondary-buttons']) .
Html::button(Craft::t('app', 'Create a draft'), [
'class' => ['btn', 'secondary', 'formsubmit'],
]) .
Html::endTag('div') .
Html::endForm();
}
} elseif ($isRevision && $canSaveCanonical) {
}

// Apply draft
if ($isDraft && !$isCurrent && $canSave && $canSaveCanonical) {
$components[] = Html::button(Craft::t('app', 'Apply draft'), [
'class' => ['btn', 'secondary', 'formsubmit'],
'data' => [
'action' => 'elements/apply-draft',
'redirect' => Craft::$app->getSecurity()->hashData('{cpEditUrl}'),
],
]);
}

// Revert content from this revision
if ($isRevision && $canSaveCanonical) {
$components[] = Html::beginForm() .
Html::actionInput('elements/revert') .
Html::redirectInput('{cpEditUrl}') .
Expand Down Expand Up @@ -723,7 +726,7 @@ private function _prepareEditor(
->content($contentFn($form))
->sidebar($sidebarFn($form));

if (!$element->getIsRevision()) {
if ($canSave && !$element->getIsRevision()) {
$this->view->registerJsWithVars(fn($settingsJs) => <<<JS
new Craft.ElementEditor($('#$containerId'), $settingsJs);
JS, [
Expand Down Expand Up @@ -1079,7 +1082,11 @@ public function actionSaveDraft(): ?Response

$user = Craft::$app->getUser()->getIdentity();

if (!$this->_canSave($element, $user)) {
if (!$element->getIsDraft() && !$this->_provisional) {
if (!$element->canCreateDrafts($user)) {
throw new ForbiddenHttpException('User not authorized to create drafts for this element.');
}
} elseif (!$this->_canSave($element, $user)) {
throw new ForbiddenHttpException('User not authorized to save this element.');
}

Expand Down
8 changes: 4 additions & 4 deletions src/controllers/GraphqlController.php
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ public function actionEditPublicSchema(?GqlSchema $schema = null): Response
$schema = $gqlService->getPublicSchema();
}

$token = $gqlService->getTokenByAccessToken(GqlToken::PUBLIC_TOKEN);
$token = $gqlService->getPublicToken();
$title = Craft::t('app', 'Edit the public GraphQL schema');

return $this->renderTemplate('graphql/schemas/_edit', compact(
Expand All @@ -583,7 +583,7 @@ public function actionSavePublicSchema(): ?Response

$gqlService = Craft::$app->getGql();
$schema = $gqlService->getPublicSchema();
$schema->scope = $this->request->getBodyParam('permissions');
$schema->scope = $this->request->getBodyParam('permissions') ?? [];

if (!$gqlService->saveSchema($schema)) {
$this->setFailFlash(Craft::t('app', 'Couldn’t save schema.'));
Expand All @@ -596,7 +596,7 @@ public function actionSavePublicSchema(): ?Response
return null;
}

$token = $gqlService->getTokenByAccessToken(GqlToken::PUBLIC_TOKEN);
$token = $gqlService->getPublicToken();
$token->enabled = (bool)$this->request->getRequiredBodyParam('enabled');

if (($expiryDate = $this->request->getBodyParam('expiryDate')) !== null) {
Expand Down Expand Up @@ -642,7 +642,7 @@ public function actionSaveSchema(): ?Response
}

$schema->name = $this->request->getBodyParam('name') ?? $schema->name;
$schema->scope = $this->request->getBodyParam('permissions');
$schema->scope = $this->request->getBodyParam('permissions') ?? [];

if (!$gqlService->saveSchema($schema)) {
$this->setFailFlash(Craft::t('app', 'Couldn’t save schema.'));
Expand Down
7 changes: 4 additions & 3 deletions src/elements/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,9 @@ protected static function defineFieldLayouts(string $source): array
{
$fieldLayouts = [];
if (
preg_match('/^folder:(.+)$/', $source, $matches) &&
($folder = Craft::$app->getAssets()->getFolderByUid($matches[1])) &&
$fieldLayout = $folder->getVolume()->getFieldLayout()
preg_match('/^volume:(.+)$/', $source, $matches) &&
($volume = Craft::$app->getVolumes()->getVolumeByUid($matches[1])) &&
$fieldLayout = $volume->getFieldLayout()
) {
$fieldLayouts[] = $fieldLayout;
}
Expand Down Expand Up @@ -497,6 +497,7 @@ protected static function defineTableAttributes(): array
'imageSize' => ['label' => Craft::t('app', 'Dimensions')],
'width' => ['label' => Craft::t('app', 'Image Width')],
'height' => ['label' => Craft::t('app', 'Image Height')],
'alt' => ['label' => Craft::t('app', 'Alternative Text')],
'link' => ['label' => Craft::t('app', 'Link'), 'icon' => 'world'],
'id' => ['label' => Craft::t('app', 'ID')],
'uid' => ['label' => Craft::t('app', 'UID')],
Expand Down
8 changes: 8 additions & 0 deletions src/elements/conditions/users/GroupConditionRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ public function getExclusiveQueryParams(): array
return ['group', 'groupId'];
}

/**
* @inheritdoc
*/
public static function isSelectable(): bool
{
return !empty(Craft::$app->getUserGroups()->getAllGroups());
}

/**
* @inheritdoc
*/
Expand Down
18 changes: 18 additions & 0 deletions src/elements/db/ElementQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use yii\db\Connection;
use yii\db\Expression;
use yii\db\ExpressionInterface;
use yii\db\QueryBuilder;

/**
* ElementQuery represents a SELECT SQL statement for elements in a way that is independent of DBMS.
Expand Down Expand Up @@ -1613,6 +1614,23 @@ public function criteriaAttributes(): array
return $names;
}

/**
* Prepares the element query and returns its subquery (which determines what elements will be returned).
*
* @param QueryBuilder|null $builder
* @return Query
* @since 4.0.3
*/
public function prepareSubquery(?QueryBuilder $builder = null): Query
{
if ($builder === null) {
$builder = Craft::$app->getDb()->getQueryBuilder();
}

/** @var Query */
return $this->prepare($builder)->from['subquery'];
}

// Arrayable methods
// -------------------------------------------------------------------------

Expand Down
Loading

0 comments on commit 6c48a3b

Please sign in to comment.