Skip to content

Commit

Permalink
Merge branch 'release/4.4.6' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonkelly committed Apr 4, 2023
2 parents 4adcbee + 1cb0534 commit 81bc48f
Show file tree
Hide file tree
Showing 104 changed files with 630 additions and 159 deletions.
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
# Release Notes for Craft CMS 4

## 4.4.6 - 2023-04-04

- Content tab menus now reveal when a tab contains validation errors, and invalid tabs’ menu options get the same warning icon treatment as inline tabs do. ([#12971](https://github.com/craftcms/cms/issues/12971))
- Selectize menus now expand upwards when there’s not ample space below them. ([#12976](https://github.com/craftcms/cms/issues/12976))
- Element index bulk action spinners are now centered on the viewport. ([#12972](https://github.com/craftcms/cms/issues/12972))
- All control panel errors are new presented via error notifications rather than browser alerts. ([#13024](https://github.com/craftcms/cms/issues/13024))
- The `up` command now sets its default `--isolated` option value to `true`, and no longer creates a redundant mutex lock.
- Added `craft\base\Element::EVENT_BEFORE_DEFINE_URL`. ([#13018](https://github.com/craftcms/cms/issues/13018))
- Added `craft\utilities\AssetIndexes::volumes()`.
- `craft\controllers\AssetIndexesController::actionStartIndexing()` now cross-references the selected volumes with those allowed by `craft\utilities\AssetIndexes::EVENT_LIST_VOLUMES` event handlers. ([#13039](https://github.com/craftcms/cms/pull/13039), [#12819](https://github.com/craftcms/cms/pull/12819))
- Fixed a bug where Assets fields weren’t respecting their View Mode setting when viewing entry revisions. ([#12948](https://github.com/craftcms/cms/issues/12948))
- Fixed a bug where asset pagination was broken when there was more than 100 subfolders. ([#12969](https://github.com/craftcms/cms/issues/12969))
- Fixed a bug where entry index pages’ “Revision Notes” and “Last Edited By” columns weren’t getting populated for disabled entries. ([#12981](https://github.com/craftcms/cms/issues/12981))
- Fixed a bug where assets were getting relocated to the root volume folder when renamed. ([#12995](https://github.com/craftcms/cms/issues/12995))
- Fixed a bug where it wasn’t possible to preview entries on another domain when the system was offline. ([#12979](https://github.com/craftcms/cms/issues/12979))
- Fixed a bug where users were able to access volumes they didn’t have permission to view via Assets fields. ([#13006](https://github.com/craftcms/cms/issues/13006))
- Fixed a bug where zero-width spaces, invisible plus signs, and byte order marks weren’t getting stripped from sanitized asset filenames. ([#13022](https://github.com/craftcms/cms/issues/13022))
- Fixed a bug where the Plugin Store wasn’t accurately reporting installed plugins’ license statuses. ([#12986](https://github.com/craftcms/cms/issues/12986))
- Fixed a bug where the Plugin Store wasn’t handling 403 API responses for cart operations properly, once a cart had been handed off to Craft Console and assigned to an organization. ([#12916](https://github.com/craftcms/cms/issues/12916))
- Fixed a bug where `craft\helpers\FileHelper::absolutePath()` wasn’t treating Windows file paths beginning drive letters as absolute. ([craftcms/generator#16](https://github.com/craftcms/generator/issues/16))
- Fixed a bug where it wasn’t possible to sort Categories fields with “Maintain hierarchy” disabled. ([#10560](https://github.com/craftcms/cms/discussions/10560))
- Fixed a bug where selectize inputs didn’t have a minimum width. ([#12950](https://github.com/craftcms/cms/issues/12950))
- Fixed a bug where the wrong tab would appear to be initially selected after an autosave, if the selected tab had changed during the autosave. ([#12960](https://github.com/craftcms/cms/issues/12960))
- Fixed a bug where it wasn’t possible to add a Dropdown field without a blank option to a global set. ([#12965](https://github.com/craftcms/cms/issues/12965))
- Fixed a bug where automatically-added Matrix blocks (per the field’s Min Blocks setting) were getting discarded if no changes were made to them. ([#12973](https://github.com/craftcms/cms/issues/12973))
- Fixed an error that could occur when installing Craft with an existing project config, if any image transforms were defined that didn’t specify the `upscale` property.
- Fixed a bug where nested folders in asset search results weren’t showing their relative path.
- Fixed a bug where admin tables’ default delete icon title text wasn’t getting translated. ([#13030](https://github.com/craftcms/cms/issues/13030))
- Fixed a bug where it was possible to save a Local filesystem pointed at a system directory (e.g. the `templates/` or `vendor/` folders), which mitigates a potential RCE vulnerability.
- Fixed XSS vulnerabilities.

## 4.4.5 - 2023-03-21

- Fixed a bug where relation data was getting deleted when running garbage collection on PostgreSQL. ([#9905](https://github.com/craftcms/cms/issues/9905))
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"issues": "https://github.com/craftcms/cms/issues?state=open",
"forum": "https://craftcms.stackexchange.com/",
"source": "https://github.com/craftcms/cms",
"docs": "https://docs.craftcms.com/v3/",
"docs": "https://craftcms.com/docs/4.x/",
"rss": "https://github.com/craftcms/cms/releases.atom"
},
"require": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<a
:title="'Delete' | t('app')"
:title="deleteTitle"
v-on:click.prevent="handleClick"
class="delete icon"
:class="{disabled}"
Expand All @@ -18,6 +18,10 @@
actionUrl: String,
before: Function,
confirmationMessage: String,
deleteTitle: {
type: String,
default: Craft.escapeHtml(Craft.t('app', 'Delete')),
},
disabled: Boolean,
failMessage: String,
id: [Number, String],
Expand Down
1 change: 1 addition & 0 deletions packages/craftcms-webpack/Craft.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ declare var Craft: {
initUiElements($container: JQuery): void;
expandPostArray(arr: object): any;
Preview: any;
cp: any;
};

declare var Garnish: any;
Expand Down
49 changes: 45 additions & 4 deletions src/base/Element.php
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,40 @@ abstract class Element extends Component implements ElementInterface
*/
public const EVENT_DEFINE_KEYWORDS = 'defineKeywords';

/**
* @event DefineUrlEvent The event that is triggered before defining the element’s URL.
*
* It can be used to provide a custom URL, completely bypassing the default URL generation.
*
* ```php
* use craft\base\Element;
* use craft\elements\Entry;
* use craft\events\DefineUrlEvent;
* use craft\helpers\UrlHelper;
* use yii\base\Event;
*
* Event::on(
* Entry::class,
* Element::EVENT_BEFORE_DEFINE_URL,
* function(DefineUrlEvent $e
* ) {
* // @var Entry $entry
* $entry = $e->sender;
*
* $event->url = '...';
* });
* ```
*
* To prevent the element from getting a URL, ensure `$event->url` is set to `null`,
* and set `$event->handled` to `true`.
*
* Note that [[EVENT_DEFINE_URL]] will still be called regardless of what happens with this event.
*
* @since 4.4.6
* @see getUrl()
*/
public const EVENT_BEFORE_DEFINE_URL = 'beforeDefineUrl';

/**
* @event DefineUrlEvent The event that is triggered when defining the element’s URL.
*
Expand Down Expand Up @@ -570,6 +604,9 @@ abstract class Element extends Component implements ElementInterface
* });
* ```
*
* To prevent the element from getting a URL, ensure `$event->url` is set to `null`,
* and set `$event->handled` to `true`.
*
* @since 4.3.0
* @see getUrl()
*/
Expand Down Expand Up @@ -1591,7 +1628,7 @@ private static function _mapCurrentRevisions(array $sourceElements): array
return [
'elementType' => static::class,
'map' => $map,
'criteria' => ['revisions' => true],
'criteria' => ['revisions' => true, 'status' => null],
];
}

Expand Down Expand Up @@ -2904,11 +2941,15 @@ public function getIsHomepage(): bool
*/
public function getUrl(): ?string
{
if (isset($this->uri)) {
// Give plugins/modules a chance to provide a custom URL
$event = new DefineUrlEvent();
$this->trigger(self::EVENT_BEFORE_DEFINE_URL, $event);
$url = $event->url;

// If DefineAssetUrlEvent::$url is set to null, only respect that if $handled is true
if ($url === null && !$event->handled && isset($this->uri)) {
$path = $this->getIsHomepage() ? '' : $this->uri;
$url = UrlHelper::siteUrl($path, null, null, $this->siteId);
} else {
$url = null;
}

// Give plugins/modules a chance to customize it
Expand Down
14 changes: 8 additions & 6 deletions src/config/GeneralConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -429,12 +429,13 @@ class GeneralConfig extends BaseConfig
* When set to `null` (default), Craft will run `mysqldump` or `pg_dump`, provided that those libraries are in the `$PATH` variable
* for the system user running the web server.
*
* You may provide your own command optionally using several tokens Craft will swap out at runtime:
* You may provide your own command, which can include several tokens Craft will substitute at runtime:
*
* - `{path}` - the target backup file path
* - `{file}` - the target backup file path
* - `{port}` - the current database port
* - `{server}` - the current database hostname
* - `{user}` - the user to connect to the database
* - `{user}` - user that was used to connect to the database
* - `{password}` - password for the specified `{user}`
* - `{database}` - the current database name
* - `{schema}` - the current database schema (if any)
*
Expand Down Expand Up @@ -3444,12 +3445,13 @@ public function backupOnUpdate(bool $value = true): self
* When set to `null` (default), Craft will run `mysqldump` or `pg_dump`, provided that those libraries are in the `$PATH` variable
* for the system user running the web server.
*
* You may provide your own command optionally using several tokens Craft will swap out at runtime:
* You may provide your own command, which can include several tokens Craft will substitute at runtime:
*
* - `{path}` - the target backup file path
* - `{file}` - the target backup file path
* - `{port}` - the current database port
* - `{server}` - the current database hostname
* - `{user}` - the user to connect to the database
* - `{user}` - user that was used to connect to the database
* - `{password}` - password for the specified `{user}`
* - `{database}` - the current database name
* - `{schema}` - the current database schema (if any)
*
Expand Down
2 changes: 1 addition & 1 deletion src/config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
return [
'id' => 'CraftCMS',
'name' => 'Craft CMS',
'version' => '4.4.5',
'version' => '4.4.6',
'schemaVersion' => '4.4.0.4',
'minVersionRequired' => '3.7.11',
'basePath' => dirname(__DIR__), // Defines the @app alias
Expand Down
21 changes: 5 additions & 16 deletions src/console/controllers/UpController.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ class UpController extends Controller
*/
public bool $force = false;

/**
* @inheritdoc
*/
public bool $isolated = true;

/**
* @inheritdoc
*/
Expand All @@ -45,15 +50,6 @@ public function options($actionID): array
*/
public function actionIndex(): int
{
$lockName = 'craft-up';
$mutex = Craft::$app->getMutex();
$this->stdout('🔒 Acquiring lock ... ');
if (!$mutex->acquire($lockName) && !$this->force) {
$this->stderr("Couldn’t acquire a mutex lock. Run again with --force to bypass.\n", Console::FG_RED);
return ExitCode::UNAVAILABLE;
}
$this->stdout("done\n\n", Console::FG_GREEN);

try {
$pendingChanges = Craft::$app->getProjectConfig()->areChangesPending();

Expand Down Expand Up @@ -82,13 +78,6 @@ public function actionIndex(): int
throw $e;
}
return ExitCode::UNSPECIFIED_ERROR;
} finally {
$this->stdout("🔓 Releasing lock ... ");
if ($mutex->release($lockName)) {
$this->stdout("done\n", Console::FG_GREEN);
} else {
$this->stderr("Couldn’t release lock.\n");
}
}

return ExitCode::OK;
Expand Down
13 changes: 10 additions & 3 deletions src/controllers/AssetIndexesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
use craft\helpers\Json;
use craft\i18n\Locale;
use craft\models\AssetIndexingSession;
use craft\models\Volume;
use craft\utilities\AssetIndexes;
use craft\web\Controller;
use Throwable;
use yii\web\BadRequestHttpException;
Expand Down Expand Up @@ -50,15 +52,20 @@ public function beforeAction($action): bool
public function actionStartIndexing(): Response
{
$request = Craft::$app->getRequest();
$volumes = (array)$request->getRequiredBodyParam('volumes');
$volumeIds = (array)$request->getRequiredBodyParam('volumes');
$cacheRemoteImages = (bool)$request->getBodyParam('cacheImages', false);
$listEmptyFolders = (bool)$request->getBodyParam('listEmptyFolders', false);

if (empty($volumes)) {
// Typecast volume IDs and filter out any disallowed volumes
$volumeIds = array_map(fn($volumeId) => (int)$volumeId, $volumeIds);
$allowedVolumeIds = array_map(fn(Volume $volume) => $volume->id, AssetIndexes::volumes());
$volumeIds = array_intersect($volumeIds, $allowedVolumeIds);

if (empty($volumeIds)) {
return $this->asFailure(Craft::t('app', 'No volumes specified.'));
}

$indexingSession = Craft::$app->getAssetIndexer()->startIndexingSession($volumes, $cacheRemoteImages, $listEmptyFolders);
$indexingSession = Craft::$app->getAssetIndexer()->startIndexingSession($volumeIds, $cacheRemoteImages, $listEmptyFolders);
$sessionData = $this->prepareSessionData($indexingSession);

$data = ['session' => $sessionData];
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/ElementsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ public function actionEdit(?ElementInterface $element, ?int $elementId = null):
'revisionId' => $element->revisionId,
'siteId' => $element->siteId,
'siteStatuses' => $siteStatuses,
'siteToken' => !$element->getSite()->enabled ? $security->hashData((string)$element->siteId) : null,
'siteToken' => (!Craft::$app->getIsLive() || !$element->getSite()->enabled) ? $security->hashData((string)$element->siteId) : null,
'visibleLayoutElements' => $form ? $form->getVisibleElements() : [],
]
)
Expand Down
23 changes: 19 additions & 4 deletions src/elements/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use craft\errors\VolumeException;
use craft\events\AssetEvent;
use craft\events\DefineAssetUrlEvent;
use craft\events\DefineUrlEvent;
use craft\events\GenerateTransformEvent;
use craft\fieldlayoutelements\assets\AltField;
use craft\fs\Temp;
Expand Down Expand Up @@ -688,11 +689,17 @@ protected static function indexElements(ElementQueryInterface $elementQuery, ?st
}
}

if (!self::isFolderIndex()) {
$assets = array_merge($assets, $elementQuery->all());
// if it's a 'foldersOnly' request, or we have enough folders to hit the query limit,
// return the folders directly
if (
self::isFolderIndex() ||
count($assets) === (int)$elementQuery->limit
) {
return $assets;
}

return $assets;
// otherwise merge in the resulting assets
return array_merge($assets, $elementQuery->all());
}

/**
Expand Down Expand Up @@ -1821,7 +1828,15 @@ public function getUrl(mixed $transform = null, ?bool $immediately = null): ?str
return null;
}

$url = $this->_url($transform, $immediately);
// Give plugins/modules a chance to provide a custom URL
$event = new DefineUrlEvent();
$this->trigger(self::EVENT_BEFORE_DEFINE_URL, $event);
$url = $event->url;

// If DefineAssetUrlEvent::$url is set to null, only respect that if $handled is true
if ($url === null && !$event->handled) {
$url = $this->_url($transform, $immediately);
}

// Give plugins/modules a chance to customize it
if ($this->hasEventHandlers(self::EVENT_DEFINE_URL)) {
Expand Down
19 changes: 19 additions & 0 deletions src/elements/GlobalSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
*/
class GlobalSet extends Element
{
// Validation scenarios
// -------------------------------------------------------------------------

/**
* @since 4.4.6
*/
public const SCENARIO_SAVE_SET = 'saveSet';

/**
* @inheritdoc
*/
Expand Down Expand Up @@ -243,6 +251,17 @@ protected function defineRules(): array
return $rules;
}

/**
* @inheritdoc
*/
public function scenarios(): array
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_SAVE_SET] = $scenarios[self::SCENARIO_DEFAULT];

return $scenarios;
}

/**
* @inheritdoc
*/
Expand Down
8 changes: 7 additions & 1 deletion src/elements/actions/RenameFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,15 @@ public function getTriggerHtml(): ?string
Craft.elementIndex.setIndexBusy();
let currentFolderId = Craft.elementIndex.\$source.data('folder-id');
const currentFolder = Craft.elementIndex.sourcePath[Craft.elementIndex.sourcePath.length - 1];
if (currentFolder && currentFolder.folderId) {
currentFolderId = currentFolder.folderId;
}
const data = {
assetId: assetId,
folderId: Craft.elementIndex.\$source.data('folder-id'),
folderId: currentFolderId,
filename: newName
};
Expand Down

0 comments on commit 81bc48f

Please sign in to comment.