diff --git a/src/Asset/Elements/Asset.php b/src/Asset/Elements/Asset.php index a9e599c00be..9d2c44ff758 100644 --- a/src/Asset/Elements/Asset.php +++ b/src/Asset/Elements/Asset.php @@ -32,9 +32,6 @@ use craft\errors\FsException; use craft\errors\ImageTransformException; use craft\errors\VolumeException; -use craft\events\AssetEvent; -use craft\events\DefineAssetUrlEvent; -use craft\events\GenerateTransformEvent; use craft\gql\interfaces\elements\Asset as AssetInterface; use craft\helpers\Assets; use craft\helpers\Cp; @@ -56,6 +53,11 @@ use craft\validators\AssetLocationValidator; use craft\web\twig\AllowedInSandbox; use CraftCms\Aliases\Aliases; +use CraftCms\Cms\Asset\Events\AfterGenerateTransform; +use CraftCms\Cms\Asset\Events\BeforeDefineAssetUrl; +use CraftCms\Cms\Asset\Events\BeforeGenerateTransform; +use CraftCms\Cms\Asset\Events\BeforeHandleFile; +use CraftCms\Cms\Asset\Events\DefineAssetUrl; use CraftCms\Cms\Asset\Models\Asset as AssetModel; use CraftCms\Cms\Asset\Validation\AssetRules; use CraftCms\Cms\Cms; @@ -128,40 +130,8 @@ * @property-read string|null $mimeType the file’s MIME type, if it can be determined */ #[Ruleset(AssetRules::class)] -final class Asset extends Element +class Asset extends Element { - // Events - // ------------------------------------------------------------------------- - - /** - * @event AssetEvent The event that is triggered before an asset is uploaded to volume. - */ - public const string EVENT_BEFORE_HANDLE_FILE = 'beforeHandleFile'; - - /** - * @event GenerateTransformEvent The event that is triggered before a transform is generated for an asset. - */ - public const string EVENT_BEFORE_GENERATE_TRANSFORM = 'beforeGenerateTransform'; - - /** - * @event GenerateTransformEvent The event that is triggered after a transform is generated for an asset. - */ - public const string EVENT_AFTER_GENERATE_TRANSFORM = 'afterGenerateTransform'; - - /** - * @event DefineAssetUrlEvent The event that is triggered before defining the asset’s URL. - * - * @see getUrl() - */ - public const string EVENT_BEFORE_DEFINE_URL = 'beforeDefineUrl'; - - /** - * @event DefineAssetUrlEvent The event that is triggered when defining the asset’s URL. - * - * @see getUrl() - */ - public const string EVENT_DEFINE_URL = 'defineUrl'; - // Location error codes // ------------------------------------------------------------------------- @@ -2087,35 +2057,20 @@ public function getUrl(mixed $transform = null, ?bool $immediately = null): ?str $transform ??= $this->_transform; - // Fire a 'beforeDefineUrl' event - if ($this->hasEventHandlers(self::EVENT_BEFORE_DEFINE_URL)) { - $event = new DefineAssetUrlEvent([ - 'transform' => $transform, - 'asset' => $this, - ]); - $this->trigger(self::EVENT_BEFORE_DEFINE_URL, $event); - $url = $event->url; - } else { - $url = null; - } + event($event = new BeforeDefineAssetUrl($this, $transform)); + + $url = $event->url; - // If DefineAssetUrlEvent::$url is set to null, only respect that if $handled is true - if ($url === null && ! ($event->handled ?? false)) { + // If BeforeDefineAssetUrl::$url is set to null, only respect that if $handled is true + if ($event->url === null && ! ($event->handled ?? false)) { $url = $this->_url($transform, $immediately); } - // Fire a 'defineUrl' event - if ($this->hasEventHandlers(self::EVENT_DEFINE_URL)) { - $event = new DefineAssetUrlEvent([ - 'url' => $url, - 'transform' => $transform, - 'asset' => $this, - ]); - $this->trigger(self::EVENT_DEFINE_URL, $event); - // If DefineAssetUrlEvent::$url is set to null, only respect that if $handled is true - if ($event->url !== null || $event->handled) { - $url = $event->url; - } + event($event = new DefineAssetUrl($this, $transform, $url)); + + // If DefineAssetUrl::$url is set to null, only respect that if $handled is true + if ($event->url !== null || $event->handled) { + $url = $event->url; } return $url !== null ? Html::encodeSpaces($url) : $url; @@ -2157,17 +2112,11 @@ private function _url(mixed $transform = null, ?bool $immediately = null): ?stri $immediately = Cms::config()->generateTransformsBeforePageLoad; } - // Fire a 'beforeGenerateTransform' event - if ($this->hasEventHandlers(self::EVENT_BEFORE_GENERATE_TRANSFORM)) { - $event = new GenerateTransformEvent([ - 'asset' => $this, - 'transform' => $transform, - ]); - $this->trigger(self::EVENT_BEFORE_GENERATE_TRANSFORM, $event); - // If a plugin set the url, we'll just use that. - if ($event->url !== null) { - return Html::encodeSpaces($event->url); - } + event($event = new BeforeGenerateTransform($this, $transform)); + + // If a plugin set the url, we'll just use that. + if ($event->url !== null) { + return Html::encodeSpaces($event->url); } $imageTransformer = $transform->getImageTransformer(); @@ -2183,15 +2132,7 @@ private function _url(mixed $transform = null, ?bool $immediately = null): ?stri return null; } - // Fire an 'afterGenerateTransform' event - if ($this->hasEventHandlers(self::EVENT_AFTER_GENERATE_TRANSFORM)) { - $event = new GenerateTransformEvent([ - 'asset' => $this, - 'transform' => $transform, - 'url' => $url, - ]); - $this->trigger(self::EVENT_AFTER_GENERATE_TRANSFORM, $event); - } + event(new AfterGenerateTransform($this, $transform, $url)); return $url; } @@ -3120,14 +3061,8 @@ public function beforeSave(bool $isNew): bool } // Fire a 'beforeHandleFile' event if we're going to be doing any file operations in afterSave() - if ( - (isset($this->newLocation) || isset($this->tempFilePath)) && - $this->hasEventHandlers(self::EVENT_BEFORE_HANDLE_FILE) - ) { - $this->trigger(self::EVENT_BEFORE_HANDLE_FILE, new AssetEvent([ - 'asset' => $this, - 'isNew' => ! $this->id, - ])); + if (isset($this->newLocation) || isset($this->tempFilePath)) { + event(new BeforeHandleFile($this, isNew: ! $this->id)); } // Set the kind based on filename diff --git a/src/Asset/Events/AfterGenerateTransform.php b/src/Asset/Events/AfterGenerateTransform.php new file mode 100644 index 00000000000..d5aaf2f4867 --- /dev/null +++ b/src/Asset/Events/AfterGenerateTransform.php @@ -0,0 +1,20 @@ +navItems[] = [ + * 'label' => 'Item Label', + * 'url' => 'my-module', + * 'icon' => '/path/to/icon.svg', + * ]; + * } + * ); + * ``` + * + * [[RegisterCpNavItems::$navItems]] is an array whose values are sub-arrays that define the nav items. Each sub-array can have the following keys: + * + * - `label` – The item’s label. + * - `url` – The URL or path of the control panel page the item should link to. + * - `icon` – The path to the SVG icon that should be used for the item. + * - `badgeCount` _(optional)_ – The badge count number that should be displayed next to the label. + * - `external` _(optional)_ – Set to `true` if the item links to an external URL. + * - `id` _(optional)_ – The ID of the `
  • ` element. If not specified, it will default to `nav-`. + * - `subnav` _(optional)_ – A nested array of sub-navigation items that should be displayed if the main item is selected. + * + * The keys of the array should define the items’ IDs, and the values should be nested arrays with `label` and `url` keys, and optionally + * `badgeCount` and `external` keys. + * + * If a subnav is defined, subpages can specify which subnav item should be selected by defining a `selectedSubnavItem` variable that is set to + * the selected item’s ID (its key in the `subnav` array). + */ +final class RegisterCpNavItems +{ + public function __construct( + public array $navItems, + ) {} +} diff --git a/src/Cp/Navigation.php b/src/Cp/Navigation.php index daac99d9272..a92be45a7c0 100644 --- a/src/Cp/Navigation.php +++ b/src/Cp/Navigation.php @@ -7,6 +7,7 @@ use Craft; use craft\helpers\UrlHelper; use CraftCms\Cms\Config\GeneralConfig; +use CraftCms\Cms\Cp\Events\RegisterCpNavItems; use CraftCms\Cms\Edition; use CraftCms\Cms\Plugin\Plugins; use CraftCms\Cms\Support\Facades\Sections; @@ -162,13 +163,9 @@ public function getItems(): array ]; } - // Fire a 'registerCpNavItems' event - // @TODO Bring this back - // if ($this->hasEventHandlers(self::EVENT_REGISTER_CP_NAV_ITEMS)) { - // $event = new RegisterCpNavItemsEvent(['navItems' => $navItems]); - // $this->trigger(self::EVENT_REGISTER_CP_NAV_ITEMS, $event); - // $navItems = $event->navItems; - // } + event($event = new RegisterCpNavItems($navItems)); + + $navItems = $event->navItems; // Figure out which item is selected, and normalize the items $path = $this->request->getPathInfo(); diff --git a/src/Element/Concerns/Cacheable.php b/src/Element/Concerns/Cacheable.php index ebe89315b66..2e0e1d2121e 100644 --- a/src/Element/Concerns/Cacheable.php +++ b/src/Element/Concerns/Cacheable.php @@ -4,7 +4,7 @@ namespace CraftCms\Cms\Element\Concerns; -use craft\events\DefineValueEvent; +use CraftCms\Cms\Element\Events\DefineCacheTags; /** * Cacheable provides cache tag management for elements. @@ -16,15 +16,6 @@ */ trait Cacheable { - /** - * @event DefineValueEvent The event that is triggered when defining the cache tags that should be cleared when - * this element is saved. - * - * @see getCacheTags() - * @since 4.1.0 - */ - public const EVENT_DEFINE_CACHE_TAGS = 'defineCacheTags'; - /** * Returns the cache tags that should be cleared when this element is saved. * @@ -34,17 +25,9 @@ trait Cacheable */ public function getCacheTags(): array { - $cacheTags = $this->cacheTags(); - - // Fire a 'defineCacheTags' event - if ($this->hasEventHandlers(self::EVENT_DEFINE_CACHE_TAGS)) { - $event = new DefineValueEvent(['value' => $cacheTags]); - $this->trigger(self::EVENT_DEFINE_CACHE_TAGS, $event); - - return $event->value; - } + event($event = new DefineCacheTags($this, $this->cacheTags())); - return $cacheTags; + return $event->tags; } /** diff --git a/src/Element/Concerns/DisplayedInIndex.php b/src/Element/Concerns/DisplayedInIndex.php index 80232403284..096bf18f47c 100644 --- a/src/Element/Concerns/DisplayedInIndex.php +++ b/src/Element/Concerns/DisplayedInIndex.php @@ -9,18 +9,18 @@ use craft\base\NestedElementInterface; use craft\db\Connection; use craft\db\ExcludeDescendantIdsExpression; -use craft\events\ElementIndexTableAttributeEvent; -use craft\events\RegisterElementCardAttributesEvent; -use craft\events\RegisterElementDefaultCardAttributesEvent; -use craft\events\RegisterElementDefaultTableAttributesEvent; -use craft\events\RegisterElementSearchableAttributesEvent; -use craft\events\RegisterElementSortOptionsEvent; -use craft\events\RegisterElementTableAttributesEvent; use craft\helpers\ElementHelper; use craft\models\FieldLayout; use CraftCms\Cms\Auth\SessionAuth; use CraftCms\Cms\Element\Element; use CraftCms\Cms\Element\ElementSources; +use CraftCms\Cms\Element\Events\PrepQueryForTableAttribute; +use CraftCms\Cms\Element\Events\RegisterCardAttributes; +use CraftCms\Cms\Element\Events\RegisterDefaultCardAttributes; +use CraftCms\Cms\Element\Events\RegisterDefaultTableAttributes; +use CraftCms\Cms\Element\Events\RegisterSearchableAttributes; +use CraftCms\Cms\Element\Events\RegisterSortOptions; +use CraftCms\Cms\Element\Events\RegisterTableAttributes; use CraftCms\Cms\Element\Queries\Contracts\ElementQueryInterface; use CraftCms\Cms\Element\Queries\ElementQuery; use CraftCms\Cms\Support\Arr; @@ -33,7 +33,6 @@ use Illuminate\Support\Facades\DB; use Stringable; use Tpetry\QueryExpressions\Function\Conditional\Coalesce; -use yii\base\Event; use yii\db\Expression; use function CraftCms\Cms\t; @@ -48,94 +47,6 @@ */ trait DisplayedInIndex { - /** - * @event RegisterElementSortOptionsEvent The event that is triggered when registering the sort options for the element type. - */ - public const EVENT_REGISTER_SORT_OPTIONS = 'registerSortOptions'; - - /** - * @event RegisterElementTableAttributesEvent The event that is triggered when registering the table attributes for the element type. - */ - public const EVENT_REGISTER_TABLE_ATTRIBUTES = 'registerTableAttributes'; - - /** - * @event RegisterElementTableAttributesEvent The event that is triggered when registering the table attributes for the element type. - */ - public const EVENT_REGISTER_DEFAULT_TABLE_ATTRIBUTES = 'registerDefaultTableAttributes'; - - /** - * @event ElementIndexTableAttributeEvent The event that is triggered when preparing an element query for an element index, for each - * attribute present in the table. - * - * Paired with [[EVENT_REGISTER_TABLE_ATTRIBUTES]] and [[EVENT_DEFINE_ATTRIBUTE_HTML]], this allows optimization of queries on element indexes. - * - * ```php - * use CraftCms\Cms\Element\Element; - * use CraftCms\Cms\Entry\Elements\Entry; - * use craft\events\DefineAttributeHtmlEvent; - * use craft\events\ElementIndexTableAttributeEvent; - * use craft\events\RegisterElementTableAttributesEvent; - * use craft\helpers\Cp; - * use yii\base\Event; - * - * Event::on( - * Entry::class, - * Element::EVENT_REGISTER_TABLE_ATTRIBUTES, - * function(RegisterElementTableAttributesEvent $e) { - * $e->tableAttributes['authorExpertise'] = ['label' => 'Author Expertise']; - * } - * ); - * - * Event::on( - * Entry::class, - * Element::EVENT_PREP_QUERY_FOR_TABLE_ATTRIBUTE, - * function(ElementIndexTableAttributeEvent $e) { - * $query = $e->query; - * $attr = $e->attribute; - * - * if ($attr === 'authorExpertise') { - * $query->andWith(['author.areasOfExpertiseCategoryField']); - * } - * } - * ); - * - * Event::on( - * Entry::class, - * Element::EVENT_DEFINE_ATTRIBUTE_HTML, - * function(DefineAttributeHtmlEvent $e) { - * $attribute = $e->attribute; - * - * if ($attribute !== 'authorExpertise') { - * return; - * } - * - * // The field data is eager-loaded! - * $author = $e->sender->getAuthor(); - * $categories = $author->areasOfExpertiseCategoryField; - * - * $e->html = Cp::elementPreviewHtml($categories); - * } - * ); - * ``` - * - * @since 3.7.14 - */ - public const EVENT_PREP_QUERY_FOR_TABLE_ATTRIBUTE = 'prepQueryForTableAttribute'; - - /** - * @event RegisterElementCardAttributesEvent The event that is triggered when registering the card attributes for the element type. - * - * @since 5.5.0 - */ - public const EVENT_REGISTER_CARD_ATTRIBUTES = 'registerCardAttributes'; - - /** - * @event RegisterElementCardAttributesEvent The event that is triggered when registering the card attributes for the element type. - * - * @since 5.5.0 - */ - public const EVENT_REGISTER_DEFAULT_CARD_ATTRIBUTES = 'registerDefaultCardAttributes'; - /** * @var string|null The view mode used to show this element (e.g. `structure`, `table`, `thumbs`, `cards`). * @@ -150,17 +61,12 @@ trait DisplayedInIndex */ public static function searchableAttributes(): array { - $attributes = static::defineSearchableAttributes(); + event($event = new RegisterSearchableAttributes( + elementType: static::class, + attributes: static::defineSearchableAttributes(), + )); - // Fire a 'registerSearchableAttributes' event - if (Event::hasHandlers(static::class, Element::EVENT_REGISTER_SEARCHABLE_ATTRIBUTES)) { - $event = new RegisterElementSearchableAttributesEvent(['attributes' => $attributes]); - Event::trigger(static::class, Element::EVENT_REGISTER_SEARCHABLE_ATTRIBUTES, $event); - - return $event->attributes; - } - - return $attributes; + return $event->attributes; } /** @@ -285,18 +191,15 @@ public static function indexHtml( ); // Prepare the element query for each of the table attributes - $hasHandlers = Event::hasHandlers(static::class, Element::EVENT_PREP_QUERY_FOR_TABLE_ATTRIBUTE); foreach ($variables['attributes'] as $attribute) { - if ($hasHandlers) { - // Fire a 'prepQueryForTableAttribute' event - $event = new ElementIndexTableAttributeEvent([ - 'query' => $elementQuery, - 'attribute' => $attribute[0], - ]); - Event::trigger(static::class, Element::EVENT_PREP_QUERY_FOR_TABLE_ATTRIBUTE, $event); - if ($event->handled) { - continue; - } + event($event = new PrepQueryForTableAttribute( + elementType: static::class, + query: $elementQuery, + attribute: $attribute[0], + )); + + if ($event->handled) { + continue; } static::prepElementQueryForTableAttribute($elementQuery, $attribute[0]); @@ -498,15 +401,12 @@ public static function sortOptions(): array ...Arr::except($sortOptions, 'id'), ]; - // Fire a 'registerSortOptions' event - if (Event::hasHandlers(static::class, Element::EVENT_REGISTER_SORT_OPTIONS)) { - $event = new RegisterElementSortOptionsEvent(['sortOptions' => $sortOptions]); - Event::trigger(static::class, Element::EVENT_REGISTER_SORT_OPTIONS, $event); + event($event = new RegisterSortOptions( + elementType: static::class, + sortOptions: $sortOptions, + )); - return $event->sortOptions; - } - - return $sortOptions; + return $event->sortOptions; } /** @@ -536,17 +436,12 @@ protected static function defineSortOptions(): array */ public static function tableAttributes(): array { - $tableAttributes = static::defineTableAttributes(); - - // Fire a 'registerTableAttributes' event - if (Event::hasHandlers(static::class, Element::EVENT_REGISTER_TABLE_ATTRIBUTES)) { - $event = new RegisterElementTableAttributesEvent(['tableAttributes' => $tableAttributes]); - Event::trigger(static::class, Element::EVENT_REGISTER_TABLE_ATTRIBUTES, $event); - - return $event->tableAttributes; - } + event($event = new RegisterTableAttributes( + elementType: static::class, + tableAttributes: static::defineTableAttributes(), + )); - return $tableAttributes; + return $event->tableAttributes; } /** @@ -588,20 +483,13 @@ protected static function defineTableAttributes(): array */ public static function defaultTableAttributes(string $source): array { - $tableAttributes = static::defineDefaultTableAttributes($source); + event($event = new RegisterDefaultTableAttributes( + elementType: static::class, + source: $source, + tableAttributes: static::defineDefaultTableAttributes($source), + )); - // Fire a 'registerDefaultTableAttributes' event - if (Event::hasHandlers(static::class, Element::EVENT_REGISTER_DEFAULT_TABLE_ATTRIBUTES)) { - $event = new RegisterElementDefaultTableAttributesEvent([ - 'source' => $source, - 'tableAttributes' => $tableAttributes, - ]); - Event::trigger(static::class, Element::EVENT_REGISTER_DEFAULT_TABLE_ATTRIBUTES, $event); - - return $event->tableAttributes; - } - - return $tableAttributes; + return $event->tableAttributes; } /** @@ -631,17 +519,13 @@ protected static function defineDefaultTableAttributes(string $source): array */ public static function cardAttributes(?FieldLayout $fieldLayout = null): array { - $cardAttributes = static::defineCardAttributes(); - - // Fire a 'registerCardAttributes' event - if (Event::hasHandlers(static::class, Element::EVENT_REGISTER_CARD_ATTRIBUTES)) { - $event = new RegisterElementCardAttributesEvent(['cardAttributes' => $cardAttributes, 'fieldLayout' => $fieldLayout]); - Event::trigger(static::class, Element::EVENT_REGISTER_CARD_ATTRIBUTES, $event); + event($event = new RegisterCardAttributes( + elementType: static::class, + cardAttributes: static::defineCardAttributes(), + fieldLayout: $fieldLayout, + )); - return $event->cardAttributes; - } - - return $cardAttributes; + return $event->cardAttributes; } /** @@ -722,19 +606,12 @@ public static function attributePreviewHtml(array $attribute): mixed */ public static function defaultCardAttributes(): array { - $cardAttributes = static::defineDefaultCardAttributes(); - - // Fire a 'registerDefaultCardAttributes' event - if (Event::hasHandlers(static::class, Element::EVENT_REGISTER_DEFAULT_CARD_ATTRIBUTES)) { - $event = new RegisterElementDefaultCardAttributesEvent([ - 'cardAttributes' => $cardAttributes, - ]); - Event::trigger(static::class, Element::EVENT_REGISTER_DEFAULT_CARD_ATTRIBUTES, $event); - - return $event->cardAttributes; - } + event($event = new RegisterDefaultCardAttributes( + elementType: static::class, + cardAttributes: static::defineDefaultCardAttributes(), + )); - return $cardAttributes; + return $event->cardAttributes; } /** diff --git a/src/Element/Concerns/Eagerloadable.php b/src/Element/Concerns/Eagerloadable.php index fca0de04184..a5edcc0a2a9 100644 --- a/src/Element/Concerns/Eagerloadable.php +++ b/src/Element/Concerns/Eagerloadable.php @@ -7,19 +7,18 @@ use craft\base\ElementInterface; use craft\elements\db\EagerLoadInfo; use craft\elements\db\EagerLoadPlan; -use craft\events\DefineEagerLoadingMapEvent; -use craft\events\SetEagerLoadedElementsEvent; use craft\helpers\ElementHelper; use CraftCms\Cms\Database\Table; use CraftCms\Cms\Element\Element; use CraftCms\Cms\Element\ElementCollection; +use CraftCms\Cms\Element\Events\DefineEagerLoadingMap; +use CraftCms\Cms\Element\Events\SetEagerLoadedElements; use CraftCms\Cms\Field\Contracts\EagerLoadingFieldInterface; use CraftCms\Cms\Support\Facades\Sites; use CraftCms\Cms\User\Elements\User; use Illuminate\Database\Query\Builder; use Illuminate\Support\Facades\DB; use Tpetry\QueryExpressions\Language\Alias; -use yii\base\Event; use yii\base\InvalidConfigException; /** @@ -33,50 +32,6 @@ */ trait Eagerloadable { - /** - * @event DefineEagerLoadingMapEvent The event that is triggered when defining an eager-loading map. - * - * ```php - * use CraftCms\Cms\Element\Element; - * use craft\base\ElementInterface; - * use craft\db\Query; - * use CraftCms\Cms\Entry\Elements\Entry; - * use craft\events\DefineEagerLoadingMapEvent; - * use yii\base\Event; - * - * // Add support for `with(['bookClub'])` to entries - * Event::on( - * Entry::class, - * Element::EVENT_DEFINE_EAGER_LOADING_MAP, - * function(DefineEagerLoadingMapEvent $event) { - * if ($event->handle === 'bookClub') { - * $bookEntryIds = array_map(fn(ElementInterface $element) => $element->id, $event->elements); - * $event->elementType = \my\plugin\BookClub::class, - * $event->map = (new Query) - * ->select(['source' => 'bookId', 'target' => 'clubId']) - * ->from('{{%bookclub_books}}') - * ->where(['bookId' => $bookEntryIds]) - * ->all(); - * $event->handled = true; - * } - * } - * ); - * ``` - * - * @since 3.1.0 - */ - public const EVENT_DEFINE_EAGER_LOADING_MAP = 'defineEagerLoadingMap'; - - /** - * @event SetEagerLoadedElementsEvent The event that is triggered when setting eager-loaded elements. - * - * Set [[Event::$handled]] to `true` to prevent the elements from getting stored to the private - * `$_eagerLoadedElements` array. - * - * @since 3.5.0 - */ - public const EVENT_SET_EAGER_LOADED_ELEMENTS = 'setEagerLoadedElements'; - /** * @var ElementInterface[]|null All elements that the element was queried with. * @@ -205,19 +160,18 @@ public static function eagerLoadingMap(array $sourceElements, string $handle): a } // Fire a 'defineEagerLoadingMap' event - if (Event::hasHandlers(static::class, Element::EVENT_DEFINE_EAGER_LOADING_MAP)) { - $event = new DefineEagerLoadingMapEvent([ - 'sourceElements' => $sourceElements, - 'handle' => $handle, - ]); - Event::trigger(static::class, Element::EVENT_DEFINE_EAGER_LOADING_MAP, $event); - if ($event->elementType !== null) { - return [ - 'elementType' => $event->elementType, - 'map' => $event->map, - 'criteria' => $event->criteria, - ]; - } + event($event = new DefineEagerLoadingMap( + elementType: static::class, + sourceElements: $sourceElements, + handle: $handle, + )); + + if ($event->targetElementType !== null) { + return [ + 'elementType' => $event->targetElementType, + 'map' => $event->map, + 'criteria' => $event->criteria, + ]; } // return null so eager-loading is ignored for this handle @@ -651,16 +605,15 @@ public function setEagerLoadedElements(string $handle, array $elements, EagerLoa break; default: // Fire a 'setEagerLoadedElements' event - if ($this->hasEventHandlers(Element::EVENT_SET_EAGER_LOADED_ELEMENTS)) { - $event = new SetEagerLoadedElementsEvent([ - 'handle' => $handle, - 'elements' => $elements, - 'plan' => $plan, - ]); - $this->trigger(Element::EVENT_SET_EAGER_LOADED_ELEMENTS, $event); - if ($event->handled) { - break; - } + event($event = new SetEagerLoadedElements( + element: $this, + handle: $handle, + elements: $elements, + plan: $plan, + )); + + if ($event->handled) { + break; } // No takers. Just store it in the internal array then. diff --git a/src/Element/Concerns/Exportable.php b/src/Element/Concerns/Exportable.php index eabb58c7933..06070b3afa6 100644 --- a/src/Element/Concerns/Exportable.php +++ b/src/Element/Concerns/Exportable.php @@ -6,8 +6,7 @@ use craft\elements\exporters\Expanded; use craft\elements\exporters\Raw; -use craft\events\RegisterElementExportersEvent; -use yii\base\Event; +use CraftCms\Cms\Element\Events\RegisterExporters; /** * Exportable provides element export functionality. @@ -19,13 +18,6 @@ */ trait Exportable { - /** - * @event RegisterElementExportersEvent The event that is triggered when registering the available exporters for the element type. - * - * @since 3.4.0 - */ - public const EVENT_REGISTER_EXPORTERS = 'registerExporters'; - /** * Returns the available element exporters for a given source. * @@ -36,20 +28,13 @@ trait Exportable */ public static function exporters(string $source): array { - $exporters = static::defineExporters($source); - - // Fire a 'registerExporters' event - if (Event::hasHandlers(static::class, self::EVENT_REGISTER_EXPORTERS)) { - $event = new RegisterElementExportersEvent([ - 'source' => $source, - 'exporters' => $exporters, - ]); - Event::trigger(static::class, self::EVENT_REGISTER_EXPORTERS, $event); - - return $event->exporters; - } + event($event = new RegisterExporters( + elementType: static::class, + source: $source, + exporters: static::defineExporters($source), + )); - return $exporters; + return $event->exporters; } /** diff --git a/src/Element/Concerns/HasActions.php b/src/Element/Concerns/HasActions.php index 1925d4978e4..a81f7e801e2 100644 --- a/src/Element/Concerns/HasActions.php +++ b/src/Element/Concerns/HasActions.php @@ -10,9 +10,8 @@ use craft\elements\actions\Edit; use craft\elements\actions\SetStatus; use craft\elements\actions\View as ViewAction; -use craft\events\RegisterElementActionsEvent; +use CraftCms\Cms\Element\Events\RegisterActions; use Illuminate\Support\Collection; -use yii\base\Event; use function CraftCms\Cms\t; @@ -26,11 +25,6 @@ */ trait HasActions { - /** - * @event RegisterElementActionsEvent The event that is triggered when registering the available bulk actions for the element type. - */ - public const EVENT_REGISTER_ACTIONS = 'registerActions'; - /** * {@inheritdoc} */ @@ -86,20 +80,13 @@ public static function actions(string $source): array $actions->push(Delete::class); } - $actions = $actions->all(); - - // Fire a 'registerActions' event - if (Event::hasHandlers(static::class, static::EVENT_REGISTER_ACTIONS)) { - $event = new RegisterElementActionsEvent([ - 'source' => $source, - 'actions' => $actions, - ]); - Event::trigger(static::class, static::EVENT_REGISTER_ACTIONS, $event); - - return $event->actions; - } + event($event = new RegisterActions( + elementType: static::class, + source: $source, + actions: $actions->all(), + )); - return $actions; + return $event->actions; } /** diff --git a/src/Element/Concerns/HasControlPanelUI.php b/src/Element/Concerns/HasControlPanelUI.php index a06a6ad08b7..7d73515b71a 100644 --- a/src/Element/Concerns/HasControlPanelUI.php +++ b/src/Element/Concerns/HasControlPanelUI.php @@ -7,15 +7,18 @@ use Craft; use craft\base\NestedElementInterface; use craft\controllers\ElementsController; -use craft\events\DefineAltActionsEvent; -use craft\events\DefineAttributeHtmlEvent; -use craft\events\DefineHtmlEvent; -use craft\events\DefineMenuItemsEvent; -use craft\events\DefineMetadataEvent; -use craft\events\RegisterElementHtmlAttributesEvent; use craft\helpers\Cp; use craft\helpers\ElementHelper; use CraftCms\Cms\Element\ElementAttributeRenderer; +use CraftCms\Cms\Element\Events\DefineActionMenuItems; +use CraftCms\Cms\Element\Events\DefineAdditionalButtons; +use CraftCms\Cms\Element\Events\DefineAltActions; +use CraftCms\Cms\Element\Events\DefineAttributeHtml; +use CraftCms\Cms\Element\Events\DefineInlineAttributeInputHtml; +use CraftCms\Cms\Element\Events\DefineMetadata; +use CraftCms\Cms\Element\Events\DefineMetaFieldsHtml; +use CraftCms\Cms\Element\Events\DefineSidebarHtml; +use CraftCms\Cms\Element\Events\RegisterHtmlAttributes; use CraftCms\Cms\Http\Responses\CpScreenResponse; use CraftCms\Cms\Shared\Enums\Color; use CraftCms\Cms\Support\Arr; @@ -36,81 +39,12 @@ * including edit URLs, sidebar HTML, metadata, action menus, and attribute rendering. * * @property string|null $ref The reference string to this element - * @property array $htmlAttributes Any attributes that should be included in the element’s DOM representation in the control panel + * @property array $htmlAttributes Any attributes that should be included in the element's DOM representation in the control panel * * @internal */ trait HasControlPanelUI { - /** - * @event DefineHtmlEvent The event that is triggered when defining additional buttons that should be shown at the top of the element's edit page. - * - * @see getAdditionalButtons() - * @since 4.0.0 - */ - public const EVENT_DEFINE_ADDITIONAL_BUTTONS = 'defineAdditionalButtons'; - - /** - * @event DefineAltActionsEvent The event that is triggered when defining alternative form actions for the element. - * - * @see getAltActions() - * @since 5.6.0 - */ - public const EVENT_DEFINE_ALT_ACTIONS = 'defineAltActions'; - - /** - * @event DefineMenuItemsEvent The event that is triggered when defining action menu items.. - * - * @see getActionMenuItems() - * @since 5.0.0 - */ - public const EVENT_DEFINE_ACTION_MENU_ITEMS = 'defineActionMenuItems'; - - /** - * @event DefineHtmlEvent The event that is triggered when defining the HTML for the editor sidebar. - * - * @see getSidebarHtml() - * @since 3.7.0 - */ - public const EVENT_DEFINE_SIDEBAR_HTML = 'defineSidebarHtml'; - - /** - * @event DefineHtmlEvent The event that is triggered when defining the HTML for meta fields within the editor sidebar. - * - * @see metaFieldsHtml() - * @since 3.7.0 - */ - public const EVENT_DEFINE_META_FIELDS_HTML = 'defineMetaFieldsHtml'; - - /** - * @event DefineMetadataEvent The event that is triggered when defining the element's metadata info. - * - * @see getMetadata() - * @since 3.7.0 - */ - public const EVENT_DEFINE_METADATA = 'defineMetadata'; - - /** - * @event RegisterElementHtmlAttributesEvent The event that is triggered when registering the HTML attributes that should be included in the element's DOM representation in the control panel. - */ - public const EVENT_REGISTER_HTML_ATTRIBUTES = 'registerHtmlAttributes'; - - /** - * @event DefineAttributeHtmlEvent The event that is triggered when defining an attribute's HTML for table and card views. - * - * @see getAttributeHtml() - * @since 5.0.0 - */ - public const EVENT_DEFINE_ATTRIBUTE_HTML = 'defineAttributeHtml'; - - /** - * @event DefineAttributeHtmlEvent The event that is triggered when defining an attribute's inline input HTML. - * - * @see getInlineAttributeInputHtml() - * @since 5.0.0 - */ - public const EVENT_DEFINE_INLINE_ATTRIBUTE_INPUT_HTML = 'defineInlineAttributeInputHtml'; - /** * @see getUiLabel() * @see setUiLabel() @@ -135,12 +69,7 @@ public function prepareEditScreen(Response|CpScreenResponse $response, string $c */ public function getAdditionalButtons(): string|Stringable { - if (! $this->hasEventHandlers(self::EVENT_DEFINE_ADDITIONAL_BUTTONS)) { - return ''; - } - - $event = new DefineHtmlEvent; - $this->trigger(self::EVENT_DEFINE_ADDITIONAL_BUTTONS, $event); + event($event = new DefineAdditionalButtons($this)); return $event->html; } @@ -206,14 +135,7 @@ public function getAltActions(): array } } - if (! $this->hasEventHandlers(self::EVENT_DEFINE_ALT_ACTIONS)) { - return $altActions; - } - - $event = new DefineAltActionsEvent([ - 'altActions' => $altActions, - ]); - $this->trigger(self::EVENT_DEFINE_ALT_ACTIONS, $event); + event($event = new DefineAltActions($this, $altActions)); return $event->altActions; } @@ -228,12 +150,7 @@ public function getActionMenuItems(): array ...array_map(fn (array $item) => $item + ['destructive' => true], $this->destructiveActionMenuItems()), ]; - if (! $this->hasEventHandlers(self::EVENT_DEFINE_ACTION_MENU_ITEMS)) { - return $items; - } - - $event = new DefineMenuItemsEvent(['items' => $items]); - $this->trigger(self::EVENT_DEFINE_ACTION_MENU_ITEMS, $event); + event($event = new DefineActionMenuItems($this, $items)); return $event->items; } @@ -510,15 +427,9 @@ public function getHtmlAttributes(string $context): array ], ]); - // Fire a 'registerHtmlAttributes' event - if ($this->hasEventHandlers(self::EVENT_REGISTER_HTML_ATTRIBUTES)) { - $event = new RegisterElementHtmlAttributesEvent(['htmlAttributes' => $htmlAttributes]); - $this->trigger(self::EVENT_REGISTER_HTML_ATTRIBUTES, $event); + event($event = new RegisterHtmlAttributes($this, $context, $htmlAttributes)); - return $event->htmlAttributes; - } - - return $htmlAttributes; + return $event->htmlAttributes; } /** @@ -538,18 +449,9 @@ protected function htmlAttributes(string $context): array */ public function getAttributeHtml(string $attribute): string|Stringable { - // Fire a 'defineAttributeHtml' event - if ($this->hasEventHandlers(self::EVENT_DEFINE_ATTRIBUTE_HTML)) { - $event = new DefineAttributeHtmlEvent([ - 'attribute' => $attribute, - ]); - $this->trigger(self::EVENT_DEFINE_ATTRIBUTE_HTML, $event); - if (isset($event->html)) { - return $event->html; - } - } + event($event = new DefineAttributeHtml($this, $attribute)); - return $this->attributeHtml($attribute); + return $event->html ?? $this->attributeHtml($attribute); } /** @@ -557,16 +459,9 @@ public function getAttributeHtml(string $attribute): string|Stringable */ public function getInlineAttributeInputHtml(string $attribute): string|Stringable { - // Fire a 'defineInlineAttributeInputHtml' event - if ($this->hasEventHandlers(self::EVENT_DEFINE_INLINE_ATTRIBUTE_INPUT_HTML)) { - $event = new DefineAttributeHtmlEvent(['attribute' => $attribute]); - $this->trigger(self::EVENT_DEFINE_INLINE_ATTRIBUTE_INPUT_HTML, $event); - if (isset($event->html)) { - return $event->html; - } - } + event($event = new DefineInlineAttributeInputHtml($this, $attribute)); - return $this->inlineAttributeInputHtml($attribute); + return $event->html ?? $this->inlineAttributeInputHtml($attribute); } /** @@ -641,12 +536,7 @@ public function getSidebarHtml(bool $static): string|Stringable $html = implode("\n", $components); - if (! $this->hasEventHandlers(self::EVENT_DEFINE_SIDEBAR_HTML)) { - return $html; - } - - $event = new DefineHtmlEvent(['html' => $html]); - $this->trigger(self::EVENT_DEFINE_SIDEBAR_HTML, $event); + event($event = new DefineSidebarHtml($this, $static, $html)); return $event->html; } @@ -660,12 +550,7 @@ public function getSidebarHtml(bool $static): string|Stringable */ protected function metaFieldsHtml(bool $static): string|Stringable { - if (! $this->hasEventHandlers(self::EVENT_DEFINE_META_FIELDS_HTML)) { - return ''; - } - - $event = new DefineHtmlEvent(['static' => $static]); - $this->trigger(self::EVENT_DEFINE_META_FIELDS_HTML, $event); + event($event = new DefineMetaFieldsHtml($this, $static)); return $event->html; } @@ -808,12 +693,8 @@ public function getMetadata(): array { $metadata = $this->metadata(); - // Fire a 'defineMetadata' event - if ($this->hasEventHandlers(self::EVENT_DEFINE_METADATA)) { - $event = new DefineMetadataEvent(['metadata' => $metadata]); - $this->trigger(self::EVENT_DEFINE_METADATA, $event); - $metadata = $event->metadata; - } + event($event = new DefineMetadata($this, $metadata)); + $metadata = $event->metadata; $formatter = I18N::getFormatter(); diff --git a/src/Element/Concerns/HasCustomFields.php b/src/Element/Concerns/HasCustomFields.php index 68be33ff8b0..8ea7a8b7985 100644 --- a/src/Element/Concerns/HasCustomFields.php +++ b/src/Element/Concerns/HasCustomFields.php @@ -23,9 +23,9 @@ * This trait contains all logic related to getting, setting, normalizing, * and tracking changes to custom field values on elements. * - * @property array $serializedFieldValues Array of the element’s serialized custom field values, indexed by their handles - * @property array $fieldValues The element’s normalized custom field values, indexed by their handles - * @property string $fieldContext The field context this element’s content uses + * @property array $serializedFieldValues Array of the element's serialized custom field values, indexed by their handles + * @property array $fieldValues The element's normalized custom field values, indexed by their handles + * @property string $fieldContext The field context this element's content uses * @property FieldLayout|null $fieldLayout The field layout used by this element * @property array $fieldParamNamespace The namespace used by custom field params on the request * @@ -34,16 +34,7 @@ trait HasCustomFields { /** - * @event RegisterElementFieldLayoutsEvent The event that is triggered when registering all of the field layouts - * associated with elements from a given source. - * - * @see fieldLayouts() - * @since 3.5.0 - */ - public const EVENT_REGISTER_FIELD_LAYOUTS = 'registerFieldLayouts'; - - /** - * @var int|null The element’s field layout ID + * @var int|null The element's field layout ID */ public ?int $fieldLayoutId = null; diff --git a/src/Element/Concerns/HasLifecycleHooks.php b/src/Element/Concerns/HasLifecycleHooks.php index 29beb4b6c94..6dd3920ec0b 100644 --- a/src/Element/Concerns/HasLifecycleHooks.php +++ b/src/Element/Concerns/HasLifecycleHooks.php @@ -4,9 +4,14 @@ namespace CraftCms\Cms\Element\Concerns; -use craft\events\ModelEvent; -use CraftCms\Cms\Element\Element; use CraftCms\Cms\Element\ElementRelations; +use CraftCms\Cms\Element\Events\AfterDelete; +use CraftCms\Cms\Element\Events\AfterPropagate; +use CraftCms\Cms\Element\Events\AfterRestore; +use CraftCms\Cms\Element\Events\AfterSave; +use CraftCms\Cms\Element\Events\BeforeDelete; +use CraftCms\Cms\Element\Events\BeforeRestore; +use CraftCms\Cms\Element\Events\BeforeSave; /** * HasLifecycleHooks provides the lifecycle hooks for the element. @@ -17,119 +22,6 @@ */ trait HasLifecycleHooks { - /** - * @event ModelEvent The event that is triggered before the element is saved. - * - * You may set [[\yii\base\ModelEvent::$isValid]] to `false` to prevent the element from getting saved. - * - * If you want to ignore events for drafts or revisions, call [[\craft\helpers\ElementHelper::isDraftOrRevision()]] - * from your event handler: - * - * ```php - * use CraftCms\Cms\Element\Element; - * use CraftCms\Cms\Entry\Elements\Entry; - * use craft\events\ModelEvent; - * use craft\helpers\ElementHelper; - * use yii\base\Event; - * - * Event::on(Entry::class, Element::EVENT_BEFORE_SAVE, function(ModelEvent $e) { - * // @var Entry $entry - * $entry = $e->sender; - * - * if (ElementHelper::isDraftOrRevision($entry)) { - * return; - * } - * - * // ... - * }); - * ``` - */ - public const EVENT_BEFORE_SAVE = 'beforeSave'; - - /** - * @event ModelEvent The event that is triggered after the element is saved. - * - * If you want to ignore events for drafts or revisions, call [[\craft\helpers\ElementHelper::isDraftOrRevision()]] - * from your event handler: - * - * ```php - * use CraftCms\Cms\Element\Element; - * use CraftCms\Cms\Entry\Elements\Entry; - * use craft\events\ModelEvent; - * use craft\helpers\ElementHelper; - * use yii\base\Event; - * - * Event::on(Entry::class, Element::EVENT_AFTER_SAVE, function(ModelEvent $e) { - * // @var Entry $entry - * $entry = $e->sender; - * - * if (ElementHelper::isDraftOrRevision($entry)) { - * return; - * } - * - * // ... - * }); - * ``` - */ - public const EVENT_AFTER_SAVE = 'afterSave'; - - /** - * @event ModelEvent The event that is triggered after the element is fully saved and propagated to other sites. - * - * If you want to ignore events for drafts or revisions, call [[\craft\helpers\ElementHelper::isDraftOrRevision()]] - * from your event handler: - * - * ```php - * use CraftCms\Cms\Element\Element; - * use CraftCms\Cms\Entry\Elements\Entry; - * use craft\events\ModelEvent; - * use craft\helpers\ElementHelper; - * use yii\base\Event; - * - * Event::on(Entry::class, Element::EVENT_AFTER_PROPAGATE, function(ModelEvent $e) { - * // @var Entry $entry - * $entry = $e->sender; - * - * if (ElementHelper::isDraftOrRevision($entry) { - * return; - * } - * - * // ... - * }); - * ``` - * - * @since 3.2.0 - */ - public const EVENT_AFTER_PROPAGATE = 'afterPropagate'; - - /** - * @event ModelEvent The event that is triggered before the element is deleted. - * - * You may set [[\yii\base\ModelEvent::$isValid]] to `false` to prevent the element from getting deleted. - */ - public const EVENT_BEFORE_DELETE = 'beforeDelete'; - - /** - * @event \yii\base\Event The event that is triggered after the element is deleted. - */ - public const EVENT_AFTER_DELETE = 'afterDelete'; - - /** - * @event ModelEvent The event that is triggered before the element is restored. - * - * You may set [[\yii\base\ModelEvent::$isValid]] to `false` to prevent the element from getting restored. - * - * @since 3.1.0 - */ - public const EVENT_BEFORE_RESTORE = 'beforeRestore'; - - /** - * @event \yii\base\Event The event that is triggered after the element is restored. - * - * @since 3.1.0 - */ - public const EVENT_AFTER_RESTORE = 'afterRestore'; - /** * {@inheritdoc} * @@ -142,15 +34,9 @@ public function beforeSave(bool $isNew): bool return false; } - // Fire a 'beforeSave' event - if ($this->hasEventHandlers(Element::EVENT_BEFORE_SAVE)) { - $event = new ModelEvent(['isNew' => $isNew]); - $this->trigger(Element::EVENT_BEFORE_SAVE, $event); + event($event = new BeforeSave($this, $isNew)); - return $event->isValid; - } - - return true; + return $event->isValid; } /** @@ -160,7 +46,7 @@ public function beforeSave(bool $isNew): bool */ public function afterSave(bool $isNew): void { - // Update the element’s relation data + // Update the element's relation data app(ElementRelations::class)->updateRelations($this, $isNew); // Tell the fields about it @@ -168,12 +54,7 @@ public function afterSave(bool $isNew): void $field->afterElementSave($this, $isNew); } - // Fire an 'afterSave' event - if ($this->hasEventHandlers(Element::EVENT_AFTER_SAVE)) { - $this->trigger(Element::EVENT_AFTER_SAVE, new ModelEvent([ - 'isNew' => $isNew, - ])); - } + event(new AfterSave($this, $isNew)); } /** @@ -188,12 +69,7 @@ public function afterPropagate(bool $isNew): void $field->afterElementPropagate($this, $isNew); } - // Fire an 'afterPropagate' event - if ($this->hasEventHandlers(Element::EVENT_AFTER_PROPAGATE)) { - $this->trigger(Element::EVENT_AFTER_PROPAGATE, new ModelEvent([ - 'isNew' => $isNew, - ])); - } + event(new AfterPropagate($this, $isNew)); $this->handleDraftSave(); } @@ -210,15 +86,9 @@ public function beforeDelete(): bool return false; } - // Fire a 'beforeDelete' event - if ($this->hasEventHandlers(Element::EVENT_BEFORE_DELETE)) { - $event = new ModelEvent; - $this->trigger(Element::EVENT_BEFORE_DELETE, $event); + event($event = new BeforeDelete($this)); - return $event->isValid; - } - - return true; + return $event->isValid; } /** @@ -233,10 +103,7 @@ public function afterDelete(): void $field->afterElementDelete($this); } - // Fire an 'afterDelete' event - if ($this->hasEventHandlers(Element::EVENT_AFTER_DELETE)) { - $this->trigger(Element::EVENT_AFTER_DELETE); - } + event(new AfterDelete($this)); $this->handleRevisionDelete(); $this->handleDraftDelete(); @@ -280,15 +147,9 @@ public function beforeRestore(): bool return false; } - // Fire a 'beforeRestore' event - if ($this->hasEventHandlers(Element::EVENT_BEFORE_RESTORE)) { - $event = new ModelEvent; - $this->trigger(Element::EVENT_BEFORE_RESTORE, $event); + event($event = new BeforeRestore($this)); - return $event->isValid; - } - - return true; + return $event->isValid; } /** @@ -303,9 +164,6 @@ public function afterRestore(): void $field->afterElementRestore($this); } - // Fire an 'afterRestore' event - if ($this->hasEventHandlers(Element::EVENT_AFTER_RESTORE)) { - $this->trigger(Element::EVENT_AFTER_RESTORE); - } + event(new AfterRestore($this)); } } diff --git a/src/Element/Concerns/HasPreviewTargets.php b/src/Element/Concerns/HasPreviewTargets.php index 5719308eb64..f5702d20e92 100644 --- a/src/Element/Concerns/HasPreviewTargets.php +++ b/src/Element/Concerns/HasPreviewTargets.php @@ -5,8 +5,8 @@ namespace CraftCms\Cms\Element\Concerns; use Craft; -use craft\events\RegisterPreviewTargetsEvent; use craft\helpers\UrlHelper; +use CraftCms\Cms\Element\Events\RegisterPreviewTargets; use CraftCms\Cms\Support\Env; use Illuminate\Support\Collection; @@ -22,13 +22,6 @@ */ trait HasPreviewTargets { - /** - * @event RegisterPreviewTargetsEvent The event that is triggered when registering the element's preview targets. - * - * @since 3.2.0 - */ - public const EVENT_REGISTER_PREVIEW_TARGETS = 'registerPreviewTargets'; - /** * @var bool Whether the element is currently being previewed. * @@ -43,17 +36,10 @@ trait HasPreviewTargets */ public function getPreviewTargets(): array { - $previewTargets = $this->previewTargets(); - - // Fire a 'registerPreviewTargets' event - if ($this->hasEventHandlers(self::EVENT_REGISTER_PREVIEW_TARGETS)) { - $event = new RegisterPreviewTargetsEvent(['previewTargets' => $previewTargets]); - $this->trigger(self::EVENT_REGISTER_PREVIEW_TARGETS, $event); - $previewTargets = $event->previewTargets; - } + event($event = new RegisterPreviewTargets($this, $this->previewTargets())); // Normalize the targets - return new Collection($previewTargets) + return new Collection($event->previewTargets) ->map(function (array $previewTarget) { if (isset($previewTarget['urlFormat'])) { $url = trim(Craft::$app->getView()->renderObjectTemplate(Env::parse($previewTarget['urlFormat']), $this)); diff --git a/src/Element/Concerns/HasRoutesAndUrls.php b/src/Element/Concerns/HasRoutesAndUrls.php index b1cf6fecc63..d9c964b92bf 100644 --- a/src/Element/Concerns/HasRoutesAndUrls.php +++ b/src/Element/Concerns/HasRoutesAndUrls.php @@ -5,13 +5,14 @@ namespace CraftCms\Cms\Element\Concerns; use craft\base\NestedElementInterface; -use craft\events\DefineUrlEvent; -use craft\events\SetElementRouteEvent; use craft\helpers\ElementHelper; use craft\helpers\Template; use craft\helpers\UrlHelper; use craft\web\twig\AllowedInSandbox; use CraftCms\Cms\Element\Element; +use CraftCms\Cms\Element\Events\BeforeDefineUrl; +use CraftCms\Cms\Element\Events\DefineUrl; +use CraftCms\Cms\Element\Events\SetRoute; use CraftCms\Cms\Support\Html; use Twig\Markup; @@ -33,99 +34,7 @@ trait HasRoutesAndUrls { /** - * @event SetElementRouteEvent The event that is triggered when defining the route that should be used when this element’s URL is requested. - * - * Set [[Event::$handled]] to `true` to explicitly tell the element that a route has been set (even if you’re - * setting it to `null`). - * - * ```php - * Event::on(craft\elements\Entry::class, craft\base\Element::EVENT_SET_ROUTE, function(craft\events\SetElementRouteEvent $e) { - * // @var craft\elements\Entry $entry - * $entry = $e->sender; - * - * if ($entry->uri === 'pricing') { - * $e->route = 'module/pricing/index'; - * - * // Explicitly tell the element that a route has been set, - * // and prevent other event handlers from running, and tell - * $e->handled = true; - * } - * }); - * ``` - */ - public const string EVENT_SET_ROUTE = 'setRoute'; - - /** - * @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 CraftCms\Cms\Element\Element; - * use CraftCms\Cms\Entry\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. - * - * @see getUrl() - * @since 4.4.6 - */ - public const string EVENT_BEFORE_DEFINE_URL = 'beforeDefineUrl'; - - /** - * @event DefineUrlEvent The event that is triggered when defining the element’s URL. - * - * ```php - * use CraftCms\Cms\Element\Element; - * use CraftCms\Cms\Entry\Elements\Entry; - * use craft\events\DefineUrlEvent; - * use craft\helpers\UrlHelper; - * use yii\base\Event; - * - * Event::on( - * Entry::class, - * Element::EVENT_DEFINE_URL, - * function(DefineUrlEvent $e - * ) { - * // @var Entry $entry - * $entry = $e->sender; - * - * // Add a custom query string param to the URL - * if ($event->value !== null) { - * $event->url = UrlHelper::urlWithParams($event->url, [ - * 'foo' => 'bar', - * ]); - * } - * }); - * ``` - * - * To prevent the element from getting a URL, ensure `$event->url` is set to `null`, - * and set `$event->handled` to `true`. - * - * @see getUrl() - * @since 4.3.0 - */ - public const string EVENT_DEFINE_URL = 'defineUrl'; - - /** - * @var string|null The element’s URI + * @var string|null The element's URI */ #[AllowedInSandbox] public ?string $uri = null; @@ -152,12 +61,9 @@ public function getUriFormat(): ?string public function getRoute(): mixed { // Fire a 'setRoute' event - if ($this->hasEventHandlers(self::EVENT_SET_ROUTE)) { - $event = new SetElementRouteEvent; - $this->trigger(self::EVENT_SET_ROUTE, $event); - if ($event->handled || $event->route !== null) { - return $event->route ?: null; - } + event($event = new SetRoute($this)); + if ($event->handled || $event->route !== null) { + return $event->route ?: null; } if ($this instanceof NestedElementInterface) { @@ -195,31 +101,22 @@ public function getIsHomepage(): bool */ public function getUrl(): ?string { - $url = null; - $handled = false; - // Fire a 'beforeDefineUrl' event - if ($this->hasEventHandlers(self::EVENT_BEFORE_DEFINE_URL)) { - $event = new DefineUrlEvent; - $this->trigger(self::EVENT_BEFORE_DEFINE_URL, $event); - $url = $event->url; - $handled = $event->handled; - } + event($beforeEvent = new BeforeDefineUrl($this)); + $url = $beforeEvent->url; + $handled = $beforeEvent->handled; - // If DefineAssetUrlEvent::$url is set to null, only respect that if $handled is true + // If BeforeDefineUrl::$url is set to null, only respect that if $handled is true if ($url === null && ! $handled && isset($this->uri)) { $path = $this->getIsHomepage() ? '' : $this->uri; $url = UrlHelper::siteUrl($path, null, null, $this->siteId); } // Fire a 'defineUrl' event - if ($this->hasEventHandlers(self::EVENT_DEFINE_URL)) { - $event = new DefineUrlEvent(['url' => $url]); - $this->trigger(self::EVENT_DEFINE_URL, $event); - // If DefineAssetUrlEvent::$url is set to null, only respect that if $handled is true - if ($event->url !== null || $event->handled) { - $url = $event->url; - } + event($event = new DefineUrl($this, $url)); + // If DefineUrl::$url is set to null, only respect that if $handled is true + if ($event->url !== null || $event->handled) { + $url = $event->url; } if ($url === null) { diff --git a/src/Element/Concerns/HasSources.php b/src/Element/Concerns/HasSources.php index bdbb95a5a5b..a6eb8e2c51d 100644 --- a/src/Element/Concerns/HasSources.php +++ b/src/Element/Concerns/HasSources.php @@ -4,11 +4,10 @@ namespace CraftCms\Cms\Element\Concerns; -use craft\events\RegisterElementFieldLayoutsEvent; -use craft\events\RegisterElementSourcesEvent; use craft\models\FieldLayout; +use CraftCms\Cms\Element\Events\RegisterFieldLayouts; +use CraftCms\Cms\Element\Events\RegisterSources; use CraftCms\Cms\Support\Facades\Fields; -use yii\base\Event; /** * HasSources provides element source management functionality. @@ -20,11 +19,6 @@ */ trait HasSources { - /** - * @event RegisterElementSourcesEvent The event that is triggered when registering the available sources for the element type. - */ - public const EVENT_REGISTER_SOURCES = 'registerSources'; - /** * @see sources() */ @@ -47,15 +41,8 @@ public static function sources(string $context): array // Memoize the results immediately, in case sources() gets called again via the event self::$sources[static::class][$context] = static::defineSources($context); - // Fire a 'registerSources' event - if (Event::hasHandlers(static::class, self::EVENT_REGISTER_SOURCES)) { - $event = new RegisterElementSourcesEvent([ - 'context' => $context, - 'sources' => self::$sources[static::class][$context], - ]); - Event::trigger(static::class, self::EVENT_REGISTER_SOURCES, $event); - self::$sources[static::class][$context] = $event->sources; - } + event($event = new RegisterSources(static::class, $context, self::$sources[static::class][$context])); + self::$sources[static::class][$context] = $event->sources; } return self::$sources[static::class][$context]; @@ -105,15 +92,7 @@ public static function fieldLayouts(?string $source): array { $fieldLayouts = static::defineFieldLayouts($source); - if (! Event::hasHandlers(static::class, self::EVENT_REGISTER_FIELD_LAYOUTS)) { - return $fieldLayouts; - } - - $event = new RegisterElementFieldLayoutsEvent([ - 'source' => $source, - 'fieldLayouts' => $fieldLayouts, - ]); - Event::trigger(static::class, self::EVENT_REGISTER_FIELD_LAYOUTS, $event); + event($event = new RegisterFieldLayouts(static::class, $source, $fieldLayouts)); return $event->fieldLayouts; } diff --git a/src/Element/Concerns/Renderable.php b/src/Element/Concerns/Renderable.php index 5c79cf0e56e..2cc777c7f67 100644 --- a/src/Element/Concerns/Renderable.php +++ b/src/Element/Concerns/Renderable.php @@ -5,9 +5,9 @@ namespace CraftCms\Cms\Element\Concerns; use Craft; -use craft\events\RenderElementEvent; use craft\web\View; use CraftCms\Cms\Cms; +use CraftCms\Cms\Element\Events\Render; use CraftCms\Cms\Support\Arr; use CraftCms\Cms\Support\Html; use Twig\Markup; @@ -22,27 +22,6 @@ */ trait Renderable { - /** - * @event RenderElementEvent The event that is triggered before an element is rendered. - * - * @since 5.7.5 - * - * ```php - * use CraftCms\Cms\Element\Element; - * use craft\events\RenderElementEvent; - * use yii\base\Event; - * - * Event::on( - * Element::class, - * Element::EVENT_RENDER, - * function(RenderElementEvent $event) { - * $event->output = '…'; - * } - * ); - * ``` - */ - public const EVENT_RENDER = 'render'; - /** * {@inheritdoc} */ @@ -54,22 +33,19 @@ public function render(array $variables = []): Markup $variables[$refHandle] = $this; } - if ($this->hasEventHandlers(self::EVENT_RENDER)) { - $event = new RenderElementEvent([ - 'templates' => $templates, - 'variables' => $variables, - ]); - - $this->trigger(self::EVENT_RENDER, $event); + event($event = new Render( + element: $this, + templates: $templates, + variables: $variables, + )); - if (isset($event->output)) { - return new Markup($event->output, 'UTF-8'); - } - - $templates = $event->templates; - $variables = $event->variables; + if ($event->output !== null) { + return new Markup($event->output, 'UTF-8'); } + $templates = $event->templates; + $variables = $event->variables; + if (! empty($templates)) { $view = Craft::$app->getView(); foreach (Arr::sort($templates, 'priority') as $template) { diff --git a/src/Element/Concerns/Searchable.php b/src/Element/Concerns/Searchable.php index ccc6980d970..4fe5a80399e 100644 --- a/src/Element/Concerns/Searchable.php +++ b/src/Element/Concerns/Searchable.php @@ -4,7 +4,7 @@ namespace CraftCms\Cms\Element\Concerns; -use craft\events\DefineAttributeKeywordsEvent; +use CraftCms\Cms\Element\Events\DefineKeywords; use CraftCms\Cms\Support\Str; /** @@ -20,45 +20,12 @@ trait Searchable { /** - * @event DefineAttributeKeywordsEvent The event that is triggered when defining the search keywords for an - * element attribute. - * - * Note that you _must_ set [[Event::$handled]] to `true` if you want the element to accept your custom - * [[DefineAttributeKeywordsEvent::$keywords|$keywords]] value. - * - * ```php - * Event::on( - * craft\elements\Entry::class, - * craft\base\Element::EVENT_DEFINE_KEYWORDS, - * function(craft\events\DefineAttributeKeywordsEvent $e - * ) { - * // @var craft\elements\Entry $entry - * $entry = $e->sender; - * - * // Prevent entry titles in the Parts section from getting search keywords - * if ($entry->section->handle === 'parts' && $e->attribute === 'title') { - * $e->keywords = ''; - * $e->handled = true; - * } - * }); - * ``` - * - * @since 3.5.0 - */ - public const EVENT_DEFINE_KEYWORDS = 'defineKeywords'; - - /** - * @event RegisterElementSearchableAttributesEvent The event that is triggered when registering the searchable attributes for the element type. - */ - public const EVENT_REGISTER_SEARCHABLE_ATTRIBUTES = 'registerSearchableAttributes'; - - /** - * @var int|null The element’s search score, if the [[\craft\elements\db\ElementQuery::search]] parameter was used when querying for the element + * @var int|null The element's search score, if the [[\craft\elements\db\ElementQuery::search]] parameter was used when querying for the element */ public ?int $searchScore = null; /** - * @var bool Whether the element’s search keywords should be indexed immediately. + * @var bool Whether the element's search keywords should be indexed immediately. * * If `null`, the search index will only be updated immediately for console requests. * @@ -71,13 +38,13 @@ trait Searchable */ public function getSearchKeywords(string $attribute): string { - if ($this->hasEventHandlers(self::EVENT_DEFINE_KEYWORDS)) { - $event = new DefineAttributeKeywordsEvent(['attribute' => $attribute]); - $this->trigger(self::EVENT_DEFINE_KEYWORDS, $event); + event($event = new DefineKeywords( + element: $this, + attribute: $attribute, + )); - if ($event->handled) { - return $event->keywords ?? ''; - } + if ($event->handled) { + return $event->keywords; } return $this->searchKeywords($attribute); diff --git a/src/Element/Concerns/Structurable.php b/src/Element/Concerns/Structurable.php index 8bb196636f6..120559e8fea 100644 --- a/src/Element/Concerns/Structurable.php +++ b/src/Element/Concerns/Structurable.php @@ -6,9 +6,10 @@ use Craft; use craft\base\ElementInterface; -use craft\events\ElementStructureEvent; use CraftCms\Cms\Element\Element; use CraftCms\Cms\Element\ElementCollection; +use CraftCms\Cms\Element\Events\AfterMoveInStructure; +use CraftCms\Cms\Element\Events\BeforeMoveInStructure; use CraftCms\Cms\Element\Queries\Contracts\ElementQueryInterface; use CraftCms\Cms\Element\Queries\ElementQuery; @@ -35,18 +36,6 @@ */ trait Structurable { - /** - * @event ElementStructureEvent The event that is triggered before the element is moved in a structure. - * - * You may set [[\yii\base\ModelEvent::$isValid]] to `false` to prevent the element from getting moved. - */ - public const string EVENT_BEFORE_MOVE_IN_STRUCTURE = 'beforeMoveInStructure'; - - /** - * @event ElementStructureEvent The event that is triggered after the element is moved in a structure. - */ - public const string EVENT_AFTER_MOVE_IN_STRUCTURE = 'afterMoveInStructure'; - public ?int $structureId = null; public ?int $root = null; @@ -372,14 +361,9 @@ public function isNextSiblingOf(ElementInterface $element): bool public function beforeMoveInStructure(int $structureId): bool { // Fire a 'beforeMoveInStructure' event - if ($this->hasEventHandlers(self::EVENT_BEFORE_MOVE_IN_STRUCTURE)) { - $event = new ElementStructureEvent(['structureId' => $structureId]); - $this->trigger(self::EVENT_BEFORE_MOVE_IN_STRUCTURE, $event); - - return $event->isValid; - } + event($event = new BeforeMoveInStructure($this, $structureId)); - return true; + return $event->isValid; } /** @@ -388,11 +372,7 @@ public function beforeMoveInStructure(int $structureId): bool public function afterMoveInStructure(int $structureId): void { // Fire an 'afterMoveInStructure' event - if ($this->hasEventHandlers(self::EVENT_AFTER_MOVE_IN_STRUCTURE)) { - $this->trigger(self::EVENT_AFTER_MOVE_IN_STRUCTURE, new ElementStructureEvent([ - 'structureId' => $structureId, - ])); - } + event(new AfterMoveInStructure($this, $structureId)); // Invalidate caches for this element Craft::$app->getElements()->invalidateCachesForElement($this); diff --git a/src/Element/Drafts.php b/src/Element/Drafts.php index fd100e86216..2779b6bdc7e 100644 --- a/src/Element/Drafts.php +++ b/src/Element/Drafts.php @@ -6,11 +6,10 @@ use Craft; use craft\base\ElementInterface; -use craft\behaviors\EventBehavior; -use craft\events\ModelEvent; use craft\helpers\ElementHelper; use CraftCms\Cms\Cms; use CraftCms\Cms\Database\Table; +use CraftCms\Cms\Element\Events\AfterPropagate; use CraftCms\Cms\Element\Events\ApplyingDraft; use CraftCms\Cms\Element\Events\CreatingDraft; use CraftCms\Cms\Element\Events\DraftApplied; @@ -19,9 +18,11 @@ use CraftCms\Cms\Support\Arr; use CraftCms\Cms\Support\Facades\Structures; use Illuminate\Container\Attributes\Singleton; +use Illuminate\Database\Query\Builder; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Log; use Throwable; use Tpetry\QueryExpressions\Language\Alias; @@ -121,21 +122,28 @@ public function createDraft( $newAttributes['trackDraftChanges'] = $canonical::trackChanges(); $newAttributes['markDraftAsSaved'] = $markAsSaved; - /** @TODO: Remove behavior */ - $newAttributes['behaviors']['duplicateOwnershipAfterPropagate'] = new EventBehavior([ - Element::EVENT_AFTER_PROPAGATE => function (ModelEvent $event) use ($canonical) { - /** @var ElementInterface $draft */ - $draft = $event->sender; - - // Duplicate nested element ownership - DB::table(Table::ELEMENTS_OWNERS) - ->insertUsing(['elementId', 'ownerId', 'sortOrder'], - DB::table(Table::ELEMENTS_OWNERS, 'o') - ->select('o.elementId', DB::raw($draft->id), 'o.sortOrder') - ->where('o.ownerId', $canonical->id) - ); - }, - ], true); + Event::listen(function (AfterPropagate $event) use ($draftId, $canonical) { + $draft = $event->element; + + // Make sure we're dealing with the same element + if ($draft->getCanonicalId() !== $canonical->id || $draft->draftId !== $draftId) { + return; + } + + // Duplicate nested element ownership + DB::table(Table::ELEMENTS_OWNERS)->insertUsing( + columns: ['elementId', 'ownerId', 'sortOrder'], + query: DB::table(Table::ELEMENTS_OWNERS, 'o') + ->select('o.elementId', DB::raw($draft->id), 'o.sortOrder') + ->where('o.ownerId', $canonical->id) + ->whereNotExists(function (Builder $q) use ($draft) { + $q->selectRaw('1') + ->from(Table::ELEMENTS_OWNERS) + ->whereColumn('elementId', 'o.elementId') + ->where('ownerId', $draft->id); + }), + ); + }); $draft = Craft::$app->getElements()->duplicateElement($canonical, $newAttributes); diff --git a/src/Element/Element.php b/src/Element/Element.php index df036cf73ea..5712c128f57 100644 --- a/src/Element/Element.php +++ b/src/Element/Element.php @@ -19,7 +19,6 @@ use CraftCms\Cms\Validation\Attributes\Ruleset; use CraftCms\Cms\Validation\Concerns\ValidatesWithRuleset; use DateTime; -use Deprecated; use Illuminate\Support\Facades\Validator as ValidatorFacade; use Illuminate\Support\Traits\Macroable; use Illuminate\Validation\Validator as LaravelValidator; @@ -27,7 +26,6 @@ use Throwable; use Traversable; use yii\base\ArrayableTrait; -use yii\base\Event; use yii\base\InvalidCallException; use yii\base\UnknownPropertyException; @@ -87,137 +85,8 @@ abstract class Element extends Component implements ElementInterface public const string SCENARIO_LIVE = 'live'; - // Events - // ------------------------------------------------------------------------- - - /** - * @event AuthorizationCheckEvent The event that is triggered when determining whether a user is authorized to view the element’s edit page. - * - * To authorize the user, set [[AuthorizationCheckEvent::$authorized]] to `true`. - * - * ```php - * Event::on( - * Entry::class, - * Element::EVENT_AUTHORIZE_VIEW, - * function(AuthorizationCheckEvent $event) { - * $event->authorized = true; - * } - * ); - * ``` - * - * @see canView() - * @since 4.0.0 - */ - #[Deprecated(message: 'in 4.3.0. [[\craft\services\Elements::EVENT_AUTHORIZE_VIEW]] should be used instead.')] - public const EVENT_AUTHORIZE_VIEW = 'authorizeView'; - - /** - * @event AuthorizationCheckEvent The event that is triggered when determining whether a user is authorized to save the element in its current state. - * - * To authorize the user, set [[AuthorizationCheckEvent::$authorized]] to `true`. - * - * ```php - * Event::on( - * Entry::class, - * Element::EVENT_AUTHORIZE_SAVE, - * function(AuthorizationCheckEvent $event) { - * $event->authorized = true; - * } - * ); - * ``` - * - * @see canSave() - * @since 4.0.0 - */ - #[Deprecated(message: 'in 4.3.0. [[\craft\services\Elements::EVENT_AUTHORIZE_SAVE]] should be used instead.')] - public const EVENT_AUTHORIZE_SAVE = 'authorizeSave'; - - /** - * @event AuthorizationCheckEvent The event that is triggered when determining whether a user is authorized to create drafts for the element. - * - * To authorize the user, set [[AuthorizationCheckEvent::$authorized]] to `true`. - * - * ```php - * Event::on( - * Entry::class, - * Element::EVENT_AUTHORIZE_CREATE_DRAFTS, - * function(AuthorizationCheckEvent $event) { - * $event->authorized = true; - * } - * ); - * ``` - * - * @see canCreateDrafts() - * @since 4.0.0 - */ - #[Deprecated(message: 'in 4.3.0. [[\craft\services\Elements::EVENT_AUTHORIZE_CREATE_DRAFTS]] should be used instead.')] - public const EVENT_AUTHORIZE_CREATE_DRAFTS = 'authorizeCreateDrafts'; - - /** - * @event AuthorizationCheckEvent The event that is triggered when determining whether a user is authorized to duplicate the element. - * - * To authorize the user, set [[AuthorizationCheckEvent::$authorized]] to `true`. - * - * ```php - * Event::on( - * Entry::class, - * Element::EVENT_AUTHORIZE_DUPLICATE, - * function(AuthorizationCheckEvent $event) { - * $event->authorized = true; - * } - * ); - * ``` - * - * @see canDuplicate() - * @since 4.0.0 - */ - #[Deprecated(message: 'in 4.3.0. [[\craft\services\Elements::EVENT_AUTHORIZE_DUPLICATE]] should be used instead.')] - public const EVENT_AUTHORIZE_DUPLICATE = 'authorizeDuplicate'; - - /** - * @event AuthorizationCheckEvent The event that is triggered when determining whether a user is authorized to delete the element. - * - * To authorize the user, set [[AuthorizationCheckEvent::$authorized]] to `true`. - * - * ```php - * Event::on( - * Entry::class, - * Element::EVENT_AUTHORIZE_DELETE, - * function(AuthorizationCheckEvent $event) { - * $event->authorized = true; - * } - * ); - * ``` - * - * @see canDelete() - * @since 4.0.0 - */ - #[Deprecated(message: 'in 4.3.0. [[\craft\services\Elements::EVENT_AUTHORIZE_DELETE]] should be used instead.')] - public const EVENT_AUTHORIZE_DELETE = 'authorizeDelete'; - - /** - * @event AuthorizationCheckEvent The event that is triggered when determining whether a user is authorized to delete the element for its current site. - * - * To authorize the user, set [[AuthorizationCheckEvent::$authorized]] to `true`. - * - * ```php - * Event::on( - * Entry::class, - * Element::EVENT_AUTHORIZE_DELETE_FOR_SITE, - * function(AuthorizationCheckEvent $event) { - * $event->authorized = true; - * } - * ); - * ``` - * - * @see canDeleteForSite() - * @since 4.0.0 - */ - #[Deprecated(message: 'in 4.3.0. [[\craft\services\Elements::EVENT_AUTHORIZE_DELETE_FOR_SITE]] should be used instead.')] - public const EVENT_AUTHORIZE_DELETE_FOR_SITE = 'authorizeDeleteForSite'; - /** - * @var int|null The element’s ID + * @var int|null The element's ID */ #[AllowedInSandbox] public ?int $id = null; diff --git a/src/Element/Events/AfterDelete.php b/src/Element/Events/AfterDelete.php new file mode 100644 index 00000000000..6e0f8f15b5d --- /dev/null +++ b/src/Element/Events/AfterDelete.php @@ -0,0 +1,19 @@ + $elementType The element type class being queried + * @param ElementInterface[] $sourceElements An array of the source elements + * @param string $handle The property handle used to identify which target elements should be included in the map + * @param class-string|null $targetElementType The element type class to eager-load + * @param array|null $map An array of element ID mappings, where each element is a sub-array with `source` and `target` keys + * @param array|null $criteria Any criteria parameters that should be applied to the element query when fetching the eager-loaded elements + */ + public function __construct( + public string $elementType, + public array $sourceElements, + public string $handle, + public ?string $targetElementType = null, + public ?array $map = null, + public ?array $criteria = null, + ) {} +} diff --git a/src/Element/Events/DefineInlineAttributeInputHtml.php b/src/Element/Events/DefineInlineAttributeInputHtml.php new file mode 100644 index 00000000000..7d730324027 --- /dev/null +++ b/src/Element/Events/DefineInlineAttributeInputHtml.php @@ -0,0 +1,30 @@ + $elementType The element type class + * @param ElementQueryInterface $query The element query + * @param string $attribute The attribute name + * @param bool $handled Whether the event has been handled + */ + public function __construct( + public string $elementType, + public ElementQueryInterface $query, + public string $attribute, + public bool $handled = false, + ) {} +} diff --git a/src/Element/Events/RegisterActions.php b/src/Element/Events/RegisterActions.php new file mode 100644 index 00000000000..e58d9295693 --- /dev/null +++ b/src/Element/Events/RegisterActions.php @@ -0,0 +1,24 @@ + $elementType The element type class + * @param string $source The selected source's key + * @param array $actions List of registered bulk actions for the element type + */ + public function __construct( + public string $elementType, + public string $source, + public array $actions = [], + ) {} +} diff --git a/src/Element/Events/RegisterCardAttributes.php b/src/Element/Events/RegisterCardAttributes.php new file mode 100644 index 00000000000..c1014924636 --- /dev/null +++ b/src/Element/Events/RegisterCardAttributes.php @@ -0,0 +1,25 @@ + $elementType The element type class + * @param array $cardAttributes The card attributes + * @param FieldLayout|null $fieldLayout The field layout + */ + public function __construct( + public string $elementType, + public array $cardAttributes = [], + public ?FieldLayout $fieldLayout = null, + ) {} +} diff --git a/src/Element/Events/RegisterDefaultCardAttributes.php b/src/Element/Events/RegisterDefaultCardAttributes.php new file mode 100644 index 00000000000..b3d4fe529d6 --- /dev/null +++ b/src/Element/Events/RegisterDefaultCardAttributes.php @@ -0,0 +1,22 @@ + $elementType The element type class + * @param array $cardAttributes The default card attribute keys + */ + public function __construct( + public string $elementType, + public array $cardAttributes = [], + ) {} +} diff --git a/src/Element/Events/RegisterDefaultTableAttributes.php b/src/Element/Events/RegisterDefaultTableAttributes.php new file mode 100644 index 00000000000..83f03d41076 --- /dev/null +++ b/src/Element/Events/RegisterDefaultTableAttributes.php @@ -0,0 +1,24 @@ + $elementType The element type class + * @param string $source The source key + * @param array $tableAttributes The default table attribute keys + */ + public function __construct( + public string $elementType, + public string $source, + public array $tableAttributes = [], + ) {} +} diff --git a/src/Element/Events/RegisterExporters.php b/src/Element/Events/RegisterExporters.php new file mode 100644 index 00000000000..22b8c339409 --- /dev/null +++ b/src/Element/Events/RegisterExporters.php @@ -0,0 +1,24 @@ + $elementType The element type class + * @param string $source The selected source's key + * @param array $exporters List of registered exporters for the element type + */ + public function __construct( + public string $elementType, + public string $source, + public array $exporters = [], + ) {} +} diff --git a/src/Element/Events/RegisterFieldLayouts.php b/src/Element/Events/RegisterFieldLayouts.php new file mode 100644 index 00000000000..ba11f57d81f --- /dev/null +++ b/src/Element/Events/RegisterFieldLayouts.php @@ -0,0 +1,28 @@ + $elementType The element type class + * @param string|null $source The selected source's key, or null if all known field layouts should be returned + * @param FieldLayout[] $fieldLayouts The registered field layouts + */ + public function __construct( + public string $elementType, + public ?string $source, + public array $fieldLayouts = [], + ) {} +} diff --git a/src/Element/Events/RegisterHtmlAttributes.php b/src/Element/Events/RegisterHtmlAttributes.php new file mode 100644 index 00000000000..822bafadc16 --- /dev/null +++ b/src/Element/Events/RegisterHtmlAttributes.php @@ -0,0 +1,27 @@ + $elementType The element type class + * @param array $attributes The searchable attributes + */ + public function __construct( + public string $elementType, + public array $attributes = [], + ) {} +} diff --git a/src/Element/Events/RegisterSortOptions.php b/src/Element/Events/RegisterSortOptions.php new file mode 100644 index 00000000000..2d939aa8454 --- /dev/null +++ b/src/Element/Events/RegisterSortOptions.php @@ -0,0 +1,22 @@ + $elementType The element type class + * @param array $sortOptions The sort options + */ + public function __construct( + public string $elementType, + public array $sortOptions = [], + ) {} +} diff --git a/src/Element/Events/RegisterSources.php b/src/Element/Events/RegisterSources.php new file mode 100644 index 00000000000..c03a9345954 --- /dev/null +++ b/src/Element/Events/RegisterSources.php @@ -0,0 +1,26 @@ + $elementType The element type class + * @param string $context The context ('index', 'modal', 'field', or 'settings') + * @param array $sources The registered sources + */ + public function __construct( + public string $elementType, + public string $context, + public array $sources = [], + ) {} +} diff --git a/src/Element/Events/RegisterTableAttributes.php b/src/Element/Events/RegisterTableAttributes.php new file mode 100644 index 00000000000..7a55baaa788 --- /dev/null +++ b/src/Element/Events/RegisterTableAttributes.php @@ -0,0 +1,22 @@ + $elementType The element type class + * @param array $tableAttributes The table attributes + */ + public function __construct( + public string $elementType, + public array $tableAttributes = [], + ) {} +} diff --git a/src/Element/Events/Render.php b/src/Element/Events/Render.php new file mode 100644 index 00000000000..7df6d5ba27d --- /dev/null +++ b/src/Element/Events/Render.php @@ -0,0 +1,28 @@ +hasEventHandlers(self::EVENT_DEFINE_ENTRY_TYPES)) { - $event = new DefineEntryTypesEvent(['entryTypes' => $entryTypes]); - $this->trigger(self::EVENT_DEFINE_ENTRY_TYPES, $event); - $entryTypes = $event->entryTypes; + if ($triggerEvent) { + event($event = new DefineEntryTypes($this, $entryTypes)); + + return $event->entryTypes; } return $entryTypes; @@ -2443,18 +2417,9 @@ public function metaFieldsHtml(bool $static): string $fields[] = parent::metaFieldsHtml($static); - // Fire a 'defineEntryMetaFields' event - if ($this->hasEventHandlers(self::EVENT_DEFINE_META_FIELDS)) { - $event = new DefineMetaFields([ - 'element' => $this, - 'static' => $static, - 'fields' => $fields, - ]); - $this->trigger(self::EVENT_DEFINE_META_FIELDS, $event); - $fields = $event->fields; - } + event($event = new \CraftCms\Cms\Entry\Events\DefineMetaFields($this, $static, $fields)); - return implode("\n", $fields); + return implode("\n", $event->fields); } /** @@ -2569,15 +2534,9 @@ private function _parentOptionCriteria(Section $section): array $parentOptionCriteria['level'] = sprintf('<=%s', $section->maxLevels - $depth); } - // Fire a 'defineParentSelectionCriteria' event - if ($this->hasEventHandlers(self::EVENT_DEFINE_PARENT_SELECTION_CRITERIA)) { - $event = new ElementCriteriaEvent(['criteria' => $parentOptionCriteria]); - $this->trigger(self::EVENT_DEFINE_PARENT_SELECTION_CRITERIA, $event); - - return $event->criteria; - } + event($event = new DefineParentSelectionCriteria($this, $parentOptionCriteria)); - return $parentOptionCriteria; + return $event->criteria; } /** diff --git a/src/Entry/Events/DefineEntryTypes.php b/src/Entry/Events/DefineEntryTypes.php new file mode 100644 index 00000000000..24a9809b253 --- /dev/null +++ b/src/Entry/Events/DefineEntryTypes.php @@ -0,0 +1,21 @@ +hasEventHandlers(self::EVENT_DEFINE_NAME)) { - $event = new DefineValueEvent; - $this->trigger(self::EVENT_DEFINE_NAME, $event); - if ($event->value !== null) { - return $event->value; - } - } + event($event = new DefineName($this)); - return $this->fullName ?? (string) $this->username; + return $event->name ?? $this->fullName ?? (string) $this->username; } /** @@ -1353,16 +1325,9 @@ public function getFriendlyName(): ?string private function _defineFriendlyName(): ?string { - // Fire a 'defineFriendlyName' event - if ($this->hasEventHandlers(self::EVENT_DEFINE_FRIENDLY_NAME)) { - $event = new DefineValueEvent; - $this->trigger(self::EVENT_DEFINE_FRIENDLY_NAME, $event); - if ($event->handled || $event->value !== null) { - return $event->value; - } - } + event($event = new DefineFriendlyName($this)); - return $this->firstName ?? $this->username; + return $event->name ?? $this->firstName ?? $this->username; } /** diff --git a/src/User/Events/DefineFriendlyName.php b/src/User/Events/DefineFriendlyName.php new file mode 100644 index 00000000000..c66dca32018 --- /dev/null +++ b/src/User/Events/DefineFriendlyName.php @@ -0,0 +1,18 @@ +setCustomCacheTags(['original']); - Event::on( - TestCacheableElement::class, - Element::EVENT_DEFINE_CACHE_TAGS, - function (DefineValueEvent $event) { - $event->value = array_merge($event->value, ['added-by-event']); - } - ); + \Illuminate\Support\Facades\Event::listen(function (DefineCacheTags $event) { + $event->tags = array_merge($event->tags, ['added-by-event']); + }); $tags = $element->getCacheTags(); expect($tags)->toContain('original'); expect($tags)->toContain('added-by-event'); - - Event::off(TestCacheableElement::class, Element::EVENT_DEFINE_CACHE_TAGS); }); test('works with real Entry element', function () { diff --git a/tests/Element/Concerns/DisplayedInIndexTest.php b/tests/Element/Concerns/DisplayedInIndexTest.php index c7a7921cd01..2d4fda09fba 100644 --- a/tests/Element/Concerns/DisplayedInIndexTest.php +++ b/tests/Element/Concerns/DisplayedInIndexTest.php @@ -2,15 +2,14 @@ declare(strict_types=1); -use craft\events\RegisterElementDefaultCardAttributesEvent; -use craft\events\RegisterElementDefaultTableAttributesEvent; -use craft\events\RegisterElementSortOptionsEvent; -use craft\events\RegisterElementTableAttributesEvent; -use CraftCms\Cms\Element\Element; +use CraftCms\Cms\Element\Events\RegisterDefaultCardAttributes; +use CraftCms\Cms\Element\Events\RegisterDefaultTableAttributes; +use CraftCms\Cms\Element\Events\RegisterSortOptions; +use CraftCms\Cms\Element\Events\RegisterTableAttributes; use CraftCms\Cms\Element\Queries\Contracts\ElementQueryInterface; use CraftCms\Cms\Entry\Elements\Entry; use CraftCms\Cms\Entry\Models\Entry as EntryModel; -use yii\base\Event; +use Illuminate\Support\Facades\Event; /** * Test Entry class that exposes protected methods from DisplayedInIndex trait @@ -559,82 +558,66 @@ public static function hasStatuses(): bool $eventTriggered = false; $capturedAttributes = null; - Event::on( - Entry::class, - Element::EVENT_REGISTER_TABLE_ATTRIBUTES, - function (RegisterElementTableAttributesEvent $event) use (&$eventTriggered, &$capturedAttributes) { - $eventTriggered = true; - $capturedAttributes = $event->tableAttributes; + Event::listen(function (RegisterTableAttributes $event) use (&$eventTriggered, &$capturedAttributes) { + if ($event->elementType !== Entry::class) { + return; } - ); + $eventTriggered = true; + $capturedAttributes = $event->tableAttributes; + }); $attributes = Entry::tableAttributes(); expect($eventTriggered)->toBeTrue(); expect($capturedAttributes)->toBeArray(); expect($capturedAttributes)->toEqual($attributes); - - // Clean up - Event::off(Entry::class, Element::EVENT_REGISTER_TABLE_ATTRIBUTES); }); test('registerSortOptions event is triggered', function () { $eventTriggered = false; - Event::on( - Entry::class, - Element::EVENT_REGISTER_SORT_OPTIONS, - function (RegisterElementSortOptionsEvent $event) use (&$eventTriggered) { - $eventTriggered = true; + Event::listen(function (RegisterSortOptions $event) use (&$eventTriggered) { + if ($event->elementType !== Entry::class) { + return; } - ); + $eventTriggered = true; + }); Entry::sortOptions(); expect($eventTriggered)->toBeTrue(); - - // Clean up - Event::off(Entry::class, Element::EVENT_REGISTER_SORT_OPTIONS); }); test('registerDefaultTableAttributes event is triggered', function () { $eventTriggered = false; $capturedSource = null; - Event::on( - Entry::class, - Element::EVENT_REGISTER_DEFAULT_TABLE_ATTRIBUTES, - function (RegisterElementDefaultTableAttributesEvent $event) use (&$eventTriggered, &$capturedSource) { - $eventTriggered = true; - $capturedSource = $event->source; + Event::listen(function (RegisterDefaultTableAttributes $event) use (&$eventTriggered, &$capturedSource) { + if ($event->elementType !== Entry::class) { + return; } - ); + $eventTriggered = true; + $capturedSource = $event->source; + }); Entry::defaultTableAttributes('*'); expect($eventTriggered)->toBeTrue(); expect($capturedSource)->toBe('*'); - - // Clean up - Event::off(Entry::class, Element::EVENT_REGISTER_DEFAULT_TABLE_ATTRIBUTES); }); test('registerDefaultCardAttributes event is triggered', function () { $eventTriggered = false; - Event::on( - Entry::class, - Element::EVENT_REGISTER_DEFAULT_CARD_ATTRIBUTES, - function (RegisterElementDefaultCardAttributesEvent $event) use (&$eventTriggered) { - $eventTriggered = true; + Event::listen(function (RegisterDefaultCardAttributes $event) use (&$eventTriggered) { + if ($event->elementType !== Entry::class) { + return; } - ); + $eventTriggered = true; + }); Entry::defaultCardAttributes(); expect($eventTriggered)->toBeTrue(); - - // Clean up - Event::off(Entry::class, Element::EVENT_REGISTER_DEFAULT_CARD_ATTRIBUTES); }); }); diff --git a/tests/Element/Concerns/ExportableTest.php b/tests/Element/Concerns/ExportableTest.php index 4f950e85e99..63d6fb48a9d 100644 --- a/tests/Element/Concerns/ExportableTest.php +++ b/tests/Element/Concerns/ExportableTest.php @@ -4,9 +4,9 @@ use craft\elements\exporters\Expanded; use craft\elements\exporters\Raw; -use craft\events\RegisterElementExportersEvent; +use CraftCms\Cms\Element\Events\RegisterExporters; use CraftCms\Cms\Entry\Elements\Entry; -use yii\base\Event; +use Illuminate\Support\Facades\Event; describe('exporters', function () { test('returns default exporters', function () { @@ -27,8 +27,7 @@ ]); }); - test('triggers registerExporters event', function () { - $eventTriggered = false; + test('RegisterExporters event allows adding custom exporters', function () { $customExporter = new class { public static function displayName(): string @@ -37,72 +36,52 @@ public static function displayName(): string } }; - Event::on( - Entry::class, - Entry::EVENT_REGISTER_EXPORTERS, - function (RegisterElementExportersEvent $event) use (&$eventTriggered, $customExporter) { - $eventTriggered = true; + Event::listen(function (RegisterExporters $event) use ($customExporter) { + if ($event->elementType === Entry::class) { $event->exporters[] = $customExporter::class; } - ); + }); $exporters = Entry::exporters('*'); - expect($eventTriggered)->toBeTrue(); expect($exporters)->toContain(Raw::class); expect($exporters)->toContain(Expanded::class); expect($exporters)->toContain($customExporter::class); - - Event::off(Entry::class, Entry::EVENT_REGISTER_EXPORTERS); }); test('event provides source key', function () { $capturedSource = null; - Event::on( - Entry::class, - Entry::EVENT_REGISTER_EXPORTERS, - function (RegisterElementExportersEvent $event) use (&$capturedSource) { - $capturedSource = $event->source; - } - ); + Event::listen(function (RegisterExporters $event) use (&$capturedSource) { + $capturedSource = $event->source; + }); Entry::exporters('section:my-section'); expect($capturedSource)->toBe('section:my-section'); - - Event::off(Entry::class, Entry::EVENT_REGISTER_EXPORTERS); }); test('event can modify exporters', function () { - Event::on( - Entry::class, - Entry::EVENT_REGISTER_EXPORTERS, - function (RegisterElementExportersEvent $event) { + Event::listen(function (RegisterExporters $event) { + if ($event->elementType === Entry::class) { $event->exporters = [Raw::class]; } - ); + }); $exporters = Entry::exporters('*'); expect($exporters)->toBe([Raw::class]); - - Event::off(Entry::class, Entry::EVENT_REGISTER_EXPORTERS); }); test('event can remove all exporters', function () { - Event::on( - Entry::class, - Entry::EVENT_REGISTER_EXPORTERS, - function (RegisterElementExportersEvent $event) { + Event::listen(function (RegisterExporters $event) { + if ($event->elementType === Entry::class) { $event->exporters = []; } - ); + }); $exporters = Entry::exporters('*'); expect($exporters)->toBe([]); - - Event::off(Entry::class, Entry::EVENT_REGISTER_EXPORTERS); }); }); diff --git a/tests/Element/Concerns/HasActionsTest.php b/tests/Element/Concerns/HasActionsTest.php index cae242fab95..948f55bc38c 100644 --- a/tests/Element/Concerns/HasActionsTest.php +++ b/tests/Element/Concerns/HasActionsTest.php @@ -7,7 +7,9 @@ use craft\elements\actions\Edit; use craft\elements\actions\SetStatus; use craft\elements\actions\View; +use CraftCms\Cms\Element\Events\RegisterActions; use CraftCms\Cms\Entry\Elements\Entry; +use Illuminate\Support\Facades\Event; function extractActionTypes(array $actions): array { @@ -43,3 +45,19 @@ protected static function defineActions(string $source): array expect($class::actions('all'))->toContain(Foo::class); }); + +test('RegisterActions event allows adding custom actions', function () { + Event::listen(function (RegisterActions $event) { + if ($event->elementType === Entry::class) { + $event->actions[] = CustomAction::class; + } + }); + + $actions = Entry::actions('all'); + $actionTypes = extractActionTypes($actions); + + expect($actionTypes)->toContain(CustomAction::class); +}); + +class CustomAction {} +class Foo {} diff --git a/tests/Element/Concerns/HasControlPanelUITest.php b/tests/Element/Concerns/HasControlPanelUITest.php index d296fa636c7..58e417c503d 100644 --- a/tests/Element/Concerns/HasControlPanelUITest.php +++ b/tests/Element/Concerns/HasControlPanelUITest.php @@ -2,18 +2,20 @@ declare(strict_types=1); -use craft\events\DefineAltActionsEvent; -use craft\events\DefineAttributeHtmlEvent; -use craft\events\DefineHtmlEvent; -use craft\events\DefineMenuItemsEvent; -use craft\events\DefineMetadataEvent; -use craft\events\RegisterElementHtmlAttributesEvent; use CraftCms\Cms\Cms; +use CraftCms\Cms\Element\Events\DefineActionMenuItems; +use CraftCms\Cms\Element\Events\DefineAdditionalButtons; +use CraftCms\Cms\Element\Events\DefineAltActions; +use CraftCms\Cms\Element\Events\DefineAttributeHtml; +use CraftCms\Cms\Element\Events\DefineInlineAttributeInputHtml; +use CraftCms\Cms\Element\Events\DefineMetadata; +use CraftCms\Cms\Element\Events\DefineSidebarHtml; +use CraftCms\Cms\Element\Events\RegisterHtmlAttributes; use CraftCms\Cms\Entry\Elements\Entry; use CraftCms\Cms\Entry\Models\Entry as EntryModel; use CraftCms\Cms\Http\Responses\CpScreenResponse; use CraftCms\Cms\User\Elements\User; -use yii\base\Event; +use Illuminate\Support\Facades\Event; use function Pest\Laravel\actingAs; @@ -54,25 +56,19 @@ expect((string) $this->entry->getAdditionalButtons())->toBe(''); }); - test('triggers defineAdditionalButtons event', function () { + test('triggers DefineAdditionalButtons event', function () { $eventTriggered = false; $customHtml = ''; - Event::on( - Entry::class, - Entry::EVENT_DEFINE_ADDITIONAL_BUTTONS, - function (DefineHtmlEvent $event) use (&$eventTriggered, $customHtml) { - $eventTriggered = true; - $event->html = $customHtml; - } - ); + Event::listen(function (DefineAdditionalButtons $event) use (&$eventTriggered, $customHtml) { + $eventTriggered = true; + $event->html = $customHtml; + }); $buttons = $this->entry->getAdditionalButtons(); expect($eventTriggered)->toBeTrue(); expect((string) $buttons)->toBe($customHtml); - - Event::off(Entry::class, Entry::EVENT_DEFINE_ADDITIONAL_BUTTONS); }); }); @@ -97,50 +93,38 @@ function (DefineHtmlEvent $event) use (&$eventTriggered, $customHtml) { expect($continueEditingAction)->toHaveKey('redirect'); }); - test('triggers defineAltActions event', function () { + test('triggers DefineAltActions event', function () { $eventTriggered = false; $customAction = [ 'label' => 'Custom Action', 'action' => 'custom/action', ]; - Event::on( - Entry::class, - Entry::EVENT_DEFINE_ALT_ACTIONS, - function (DefineAltActionsEvent $event) use (&$eventTriggered, $customAction) { - $eventTriggered = true; - $event->altActions[] = $customAction; - } - ); + Event::listen(function (DefineAltActions $event) use (&$eventTriggered, $customAction) { + $eventTriggered = true; + $event->altActions[] = $customAction; + }); $altActions = $this->entry->getAltActions(); expect($eventTriggered)->toBeTrue(); expect($altActions)->toContain($customAction); - - Event::off(Entry::class, Entry::EVENT_DEFINE_ALT_ACTIONS); }); test('event can modify alt actions', function () { - Event::on( - Entry::class, - Entry::EVENT_DEFINE_ALT_ACTIONS, - function (DefineAltActionsEvent $event) { - $event->altActions = [ - [ - 'label' => 'Only Action', - 'action' => 'test/action', - ], - ]; - } - ); + Event::listen(function (DefineAltActions $event) { + $event->altActions = [ + [ + 'label' => 'Only Action', + 'action' => 'test/action', + ], + ]; + }); $altActions = $this->entry->getAltActions(); expect($altActions)->toHaveCount(1); expect($altActions[0]['label'])->toBe('Only Action'); - - Event::off(Entry::class, Entry::EVENT_DEFINE_ALT_ACTIONS); }); }); @@ -161,7 +145,7 @@ function (DefineAltActionsEvent $event) { expect($hasDestructiveItems)->toBeTrue(); }); - test('triggers defineActionMenuItems event', function () { + test('triggers DefineActionMenuItems event', function () { $eventTriggered = false; $customItem = [ 'id' => 'custom-action', @@ -169,43 +153,31 @@ function (DefineAltActionsEvent $event) { 'icon' => 'wand', ]; - Event::on( - Entry::class, - Entry::EVENT_DEFINE_ACTION_MENU_ITEMS, - function (DefineMenuItemsEvent $event) use (&$eventTriggered, $customItem) { - $eventTriggered = true; - $event->items[] = $customItem; - } - ); + Event::listen(function (DefineActionMenuItems $event) use (&$eventTriggered, $customItem) { + $eventTriggered = true; + $event->items[] = $customItem; + }); $items = $this->entry->getActionMenuItems(); expect($eventTriggered)->toBeTrue(); expect($items)->toContain($customItem); - - Event::off(Entry::class, Entry::EVENT_DEFINE_ACTION_MENU_ITEMS); }); test('event can modify menu items', function () { - Event::on( - Entry::class, - Entry::EVENT_DEFINE_ACTION_MENU_ITEMS, - function (DefineMenuItemsEvent $event) { - $event->items = [ - [ - 'id' => 'only-item', - 'label' => 'Only Item', - ], - ]; - } - ); + Event::listen(function (DefineActionMenuItems $event) { + $event->items = [ + [ + 'id' => 'only-item', + 'label' => 'Only Item', + ], + ]; + }); $items = $this->entry->getActionMenuItems(); expect($items)->toHaveCount(1); expect($items[0]['label'])->toBe('Only Item'); - - Event::off(Entry::class, Entry::EVENT_DEFINE_ACTION_MENU_ITEMS); }); }); @@ -222,47 +194,35 @@ function (DefineMenuItemsEvent $event) { expect($attributes['data'])->toHaveKey('disallow-status'); }); - test('triggers registerHtmlAttributes event', function () { + test('triggers RegisterHtmlAttributes event', function () { $eventTriggered = false; $customAttribute = 'custom-value'; - Event::on( - Entry::class, - Entry::EVENT_REGISTER_HTML_ATTRIBUTES, - function (RegisterElementHtmlAttributesEvent $event) use (&$eventTriggered, $customAttribute) { - $eventTriggered = true; - $event->htmlAttributes['data']['custom'] = $customAttribute; - } - ); + Event::listen(function (RegisterHtmlAttributes $event) use (&$eventTriggered, $customAttribute) { + $eventTriggered = true; + $event->htmlAttributes['data']['custom'] = $customAttribute; + }); $attributes = $this->entry->getHtmlAttributes('index'); expect($eventTriggered)->toBeTrue(); expect($attributes['data']['custom'])->toBe($customAttribute); - - Event::off(Entry::class, Entry::EVENT_REGISTER_HTML_ATTRIBUTES); }); test('event can modify html attributes', function () { - Event::on( - Entry::class, - Entry::EVENT_REGISTER_HTML_ATTRIBUTES, - function (RegisterElementHtmlAttributesEvent $event) { - $event->htmlAttributes = [ - 'class' => 'custom-class', - 'data' => [ - 'test' => 'value', - ], - ]; - } - ); + Event::listen(function (RegisterHtmlAttributes $event) { + $event->htmlAttributes = [ + 'class' => 'custom-class', + 'data' => [ + 'test' => 'value', + ], + ]; + }); $attributes = $this->entry->getHtmlAttributes('index'); expect($attributes)->toHaveKey('class', 'custom-class'); expect($attributes['data'])->toHaveKey('test', 'value'); - - Event::off(Entry::class, Entry::EVENT_REGISTER_HTML_ATTRIBUTES); }); }); @@ -273,45 +233,33 @@ function (RegisterElementHtmlAttributesEvent $event) { expect($html)->toBeString(); }); - test('triggers defineAttributeHtml event', function () { + test('triggers DefineAttributeHtml event', function () { $eventTriggered = false; $customHtml = 'Custom HTML'; - Event::on( - Entry::class, - Entry::EVENT_DEFINE_ATTRIBUTE_HTML, - function (DefineAttributeHtmlEvent $event) use (&$eventTriggered, $customHtml) { - $eventTriggered = true; - if ($event->attribute === 'title') { - $event->html = $customHtml; - } + Event::listen(function (DefineAttributeHtml $event) use (&$eventTriggered, $customHtml) { + $eventTriggered = true; + if ($event->attribute === 'title') { + $event->html = $customHtml; } - ); + }); $html = $this->entry->getAttributeHtml('title'); expect($eventTriggered)->toBeTrue(); expect((string) $html)->toBe($customHtml); - - Event::off(Entry::class, Entry::EVENT_DEFINE_ATTRIBUTE_HTML); }); test('event receives correct attribute name', function () { $capturedAttribute = null; - Event::on( - Entry::class, - Entry::EVENT_DEFINE_ATTRIBUTE_HTML, - function (DefineAttributeHtmlEvent $event) use (&$capturedAttribute) { - $capturedAttribute = $event->attribute; - } - ); + Event::listen(function (DefineAttributeHtml $event) use (&$capturedAttribute) { + $capturedAttribute = $event->attribute; + }); $this->entry->getAttributeHtml('slug'); expect($capturedAttribute)->toBe('slug'); - - Event::off(Entry::class, Entry::EVENT_DEFINE_ATTRIBUTE_HTML); }); }); @@ -322,45 +270,33 @@ function (DefineAttributeHtmlEvent $event) use (&$capturedAttribute) { expect($html)->toBeString(); }); - test('triggers defineInlineAttributeInputHtml event', function () { + test('triggers DefineInlineAttributeInputHtml event', function () { $eventTriggered = false; $customHtml = ''; - Event::on( - Entry::class, - Entry::EVENT_DEFINE_INLINE_ATTRIBUTE_INPUT_HTML, - function (DefineAttributeHtmlEvent $event) use (&$eventTriggered, $customHtml) { - $eventTriggered = true; - if ($event->attribute === 'title') { - $event->html = $customHtml; - } + Event::listen(function (DefineInlineAttributeInputHtml $event) use (&$eventTriggered, $customHtml) { + $eventTriggered = true; + if ($event->attribute === 'title') { + $event->html = $customHtml; } - ); + }); $html = $this->entry->getInlineAttributeInputHtml('title'); expect($eventTriggered)->toBeTrue(); expect((string) $html)->toBe($customHtml); - - Event::off(Entry::class, Entry::EVENT_DEFINE_INLINE_ATTRIBUTE_INPUT_HTML); }); test('event receives correct attribute name', function () { $capturedAttribute = null; - Event::on( - Entry::class, - Entry::EVENT_DEFINE_INLINE_ATTRIBUTE_INPUT_HTML, - function (DefineAttributeHtmlEvent $event) use (&$capturedAttribute) { - $capturedAttribute = $event->attribute; - } - ); + Event::listen(function (DefineInlineAttributeInputHtml $event) use (&$capturedAttribute) { + $capturedAttribute = $event->attribute; + }); $this->entry->getInlineAttributeInputHtml('slug'); expect($capturedAttribute)->toBe('slug'); - - Event::off(Entry::class, Entry::EVENT_DEFINE_INLINE_ATTRIBUTE_INPUT_HTML); }); }); @@ -377,41 +313,29 @@ function (DefineAttributeHtmlEvent $event) use (&$capturedAttribute) { expect($html)->toBeString(); }); - test('triggers defineSidebarHtml event', function () { + test('triggers DefineSidebarHtml event', function () { $eventTriggered = false; $customHtml = '
    Custom
    '; - Event::on( - Entry::class, - Entry::EVENT_DEFINE_SIDEBAR_HTML, - function (DefineHtmlEvent $event) use (&$eventTriggered, $customHtml) { - $eventTriggered = true; - $event->html = $customHtml; - } - ); + Event::listen(function (DefineSidebarHtml $event) use (&$eventTriggered, $customHtml) { + $eventTriggered = true; + $event->html = $customHtml; + }); $html = $this->entry->getSidebarHtml(false); expect($eventTriggered)->toBeTrue(); expect((string) $html)->toBe($customHtml); - - Event::off(Entry::class, Entry::EVENT_DEFINE_SIDEBAR_HTML); }); test('event can append to existing html', function () { - Event::on( - Entry::class, - Entry::EVENT_DEFINE_SIDEBAR_HTML, - function (DefineHtmlEvent $event) { - $event->html .= '
    Appended
    '; - } - ); + Event::listen(function (DefineSidebarHtml $event) { + $event->html .= '
    Appended
    '; + }); $html = $this->entry->getSidebarHtml(false); expect((string) $html)->toContain('Appended'); - - Event::off(Entry::class, Entry::EVENT_DEFINE_SIDEBAR_HTML); }); }); @@ -446,45 +370,33 @@ function (DefineHtmlEvent $event) { expect($metadata)->toHaveKey('Updated at'); }); - test('triggers defineMetadata event', function () { + test('triggers DefineMetadata event', function () { $eventTriggered = false; - Event::on( - Entry::class, - Entry::EVENT_DEFINE_METADATA, - function (DefineMetadataEvent $event) use (&$eventTriggered) { - $eventTriggered = true; - $event->metadata['Custom'] = 'Custom Value'; - } - ); + Event::listen(function (DefineMetadata $event) use (&$eventTriggered) { + $eventTriggered = true; + $event->metadata['Custom'] = 'Custom Value'; + }); $metadata = $this->entry->getMetadata(); expect($eventTriggered)->toBeTrue(); expect($metadata)->toHaveKey('Custom'); expect($metadata['Custom'])->toBe('Custom Value'); - - Event::off(Entry::class, Entry::EVENT_DEFINE_METADATA); }); test('event can modify existing metadata', function () { - Event::on( - Entry::class, - Entry::EVENT_DEFINE_METADATA, - function (DefineMetadataEvent $event) { - $event->metadata = [ - 'Only Key' => 'Only Value', - ]; - } - ); + Event::listen(function (DefineMetadata $event) { + $event->metadata = [ + 'Only Key' => 'Only Value', + ]; + }); $metadata = $this->entry->getMetadata(); expect($metadata)->toHaveKey('Only Key'); expect($metadata)->toHaveKey('ID'); // Should still have merged defaults expect($metadata)->toHaveKey('Status'); - - Event::off(Entry::class, Entry::EVENT_DEFINE_METADATA); }); test('metadata values can be callables', function () { @@ -499,13 +411,9 @@ function (DefineMetadataEvent $event) { }); test('callable metadata can return false to omit', function () { - Event::on( - Entry::class, - Entry::EVENT_DEFINE_METADATA, - function (DefineMetadataEvent $event) { - $event->metadata['Hidden'] = fn () => false; - } - ); + Event::listen(function (DefineMetadata $event) { + $event->metadata['Hidden'] = fn () => false; + }); $metadata = $this->entry->getMetadata(); @@ -514,8 +422,6 @@ function (DefineMetadataEvent $event) { // The callable returns false, which indicates it should be omitted when rendered $hiddenValue = call_user_func($metadata['Hidden']); expect($hiddenValue)->toBeFalse(); - - Event::off(Entry::class, Entry::EVENT_DEFINE_METADATA); }); }); diff --git a/tests/Element/Concerns/HasLifecycleHooksTest.php b/tests/Element/Concerns/HasLifecycleHooksTest.php index c35321a6d9f..032fb5e5e6d 100644 --- a/tests/Element/Concerns/HasLifecycleHooksTest.php +++ b/tests/Element/Concerns/HasLifecycleHooksTest.php @@ -2,11 +2,17 @@ declare(strict_types=1); -use CraftCms\Cms\Element\Element; +use CraftCms\Cms\Element\Events\AfterDelete; +use CraftCms\Cms\Element\Events\AfterPropagate; +use CraftCms\Cms\Element\Events\AfterRestore; +use CraftCms\Cms\Element\Events\AfterSave; +use CraftCms\Cms\Element\Events\BeforeDelete; +use CraftCms\Cms\Element\Events\BeforeRestore; +use CraftCms\Cms\Element\Events\BeforeSave; use CraftCms\Cms\Entry\Elements\Entry; use CraftCms\Cms\Entry\Models\Entry as EntryModel; use CraftCms\Cms\User\Elements\User; -use yii\base\Event; +use Illuminate\Support\Facades\Event; use function Pest\Laravel\actingAs; @@ -17,41 +23,144 @@ }); test('beforeSave triggers event', function () { - expectEvent(Entry::class, Element::EVENT_BEFORE_SAVE, fn () => $this->entry->beforeSave(false)); + $triggered = false; + Event::listen(function (BeforeSave $event) use (&$triggered) { + $triggered = true; + }); + + $this->entry->beforeSave(false); + + expect($triggered)->toBeTrue(); +}); + +test('beforeSave event can prevent save', function () { + Event::listen(function (BeforeSave $event) { + $event->isValid = false; + }); + + // Prevent revision + $this->entry->id = null; + + $result = $this->entry->beforeSave(false); + + expect($result)->toBeFalse(); +}); + +test('beforeSave event receives isNew parameter', function () { + $receivedIsNew = []; + Event::listen(function (BeforeSave $event) use (&$receivedIsNew) { + $receivedIsNew[] = $event->isNew; + }); + + $this->entry->beforeSave(true); + + expect(count($receivedIsNew))->toBeGreaterThan(0); }); test('afterSave triggers event', function () { - expectEvent(Entry::class, Element::EVENT_AFTER_SAVE, fn () => $this->entry->afterSave(false)); + $triggered = false; + Event::listen(function (AfterSave $event) use (&$triggered) { + $triggered = true; + }); + + $this->entry->afterSave(false); + + expect($triggered)->toBeTrue(); +}); + +test('afterSave event receives isNew parameter', function () { + $receivedIsNew = null; + Event::listen(function (AfterSave $event) use (&$receivedIsNew) { + $receivedIsNew = $event->isNew; + }); + + $this->entry->afterSave(false); + + expect($receivedIsNew)->toBeFalse(); }); test('afterPropagate triggers event', function () { - expectEvent(Entry::class, Element::EVENT_AFTER_PROPAGATE, fn () => $this->entry->afterPropagate(false)); + $triggered = false; + Event::listen(function (AfterPropagate $event) use (&$triggered) { + $triggered = true; + }); + + $this->entry->afterPropagate(false); + + expect($triggered)->toBeTrue(); +}); + +test('afterPropagate event receives isNew parameter', function () { + $receivedIsNew = []; + Event::listen(function (AfterPropagate $event) use (&$receivedIsNew) { + $receivedIsNew[] = $event->isNew; + }); + + $this->entry->afterPropagate(false); + + expect(count($receivedIsNew))->toBeGreaterThan(0); }); test('beforeDelete triggers event', function () { - expectEvent(Entry::class, Element::EVENT_BEFORE_DELETE, fn () => $this->entry->beforeDelete()); + $triggered = false; + Event::listen(function (BeforeDelete $event) use (&$triggered) { + $triggered = true; + }); + + $this->entry->beforeDelete(); + + expect($triggered)->toBeTrue(); +}); + +test('beforeDelete event can prevent delete', function () { + Event::listen(function (BeforeDelete $event) { + $event->isValid = false; + }); + + $result = $this->entry->beforeDelete(); + + expect($result)->toBeFalse(); }); test('afterDelete triggers event', function () { - expectEvent(Entry::class, Element::EVENT_AFTER_DELETE, fn () => $this->entry->afterDelete()); + $triggered = false; + Event::listen(function (AfterDelete $event) use (&$triggered) { + $triggered = true; + }); + + $this->entry->afterDelete(); + + expect($triggered)->toBeTrue(); }); test('beforeRestore triggers event', function () { - expectEvent(Entry::class, Element::EVENT_BEFORE_RESTORE, fn () => $this->entry->beforeRestore()); + $triggered = false; + Event::listen(function (BeforeRestore $event) use (&$triggered) { + $triggered = true; + }); + + $this->entry->beforeRestore(); + + expect($triggered)->toBeTrue(); }); -test('afterRestore triggers event', function () { - expectEvent(Entry::class, Element::EVENT_AFTER_RESTORE, fn () => $this->entry->afterRestore()); +test('beforeRestore event can prevent restore', function () { + Event::listen(function (BeforeRestore $event) { + $event->isValid = false; + }); + + $result = $this->entry->beforeRestore(); + + expect($result)->toBeFalse(); }); -function expectEvent(string $class, string $eventName, callable $action): void -{ +test('afterRestore triggers event', function () { $triggered = false; - Event::on($class, $eventName, function () use (&$triggered) { + Event::listen(function (AfterRestore $event) use (&$triggered) { $triggered = true; }); - $action(); + $this->entry->afterRestore(); expect($triggered)->toBeTrue(); -} +}); diff --git a/tests/Element/Concerns/HasPreviewTargetsTest.php b/tests/Element/Concerns/HasPreviewTargetsTest.php index b696504e4b2..00003794ad4 100644 --- a/tests/Element/Concerns/HasPreviewTargetsTest.php +++ b/tests/Element/Concerns/HasPreviewTargetsTest.php @@ -2,10 +2,9 @@ declare(strict_types=1); -use craft\events\RegisterPreviewTargetsEvent; use CraftCms\Cms\Element\Element; +use CraftCms\Cms\Element\Events\RegisterPreviewTargets; use CraftCms\Cms\Support\Facades\Sites; -use yii\base\Event; class TestPreviewTargetsElement extends Element { @@ -50,10 +49,6 @@ protected function previewTargets(): array $this->primarySiteId = Sites::getPrimarySite()->id; }); -afterEach(function () { - Event::off(TestPreviewTargetsElement::class, Element::EVENT_REGISTER_PREVIEW_TARGETS); -}); - describe('getPreviewTargets', function () { test('returns empty array when element has no URL', function () { $element = new TestPreviewTargetsElement; @@ -164,21 +159,19 @@ protected function previewTargets(): array expect($targets[0]['refresh'])->toBeFalse(); }); - test('EVENT_REGISTER_PREVIEW_TARGETS can add targets', function () { + test('RegisterPreviewTargets event can add targets', function () { $element = new TestPreviewTargetsElement; $element->siteId = $this->primarySiteId; $element->setElementUrl('https://example.com/original'); - Event::on( - TestPreviewTargetsElement::class, - Element::EVENT_REGISTER_PREVIEW_TARGETS, - function (RegisterPreviewTargetsEvent $event) { + \Illuminate\Support\Facades\Event::listen(function (RegisterPreviewTargets $event) { + if ($event->element instanceof TestPreviewTargetsElement) { $event->previewTargets[] = [ 'label' => 'Added by Event', 'url' => 'https://example.com/event-added', ]; } - ); + }); $targets = $element->getPreviewTargets(); @@ -186,7 +179,7 @@ function (RegisterPreviewTargetsEvent $event) { expect($targets[1]['label'])->toBe('Added by Event'); }); - test('EVENT_REGISTER_PREVIEW_TARGETS can modify existing targets', function () { + test('RegisterPreviewTargets event can modify existing targets', function () { $element = new TestPreviewTargetsElement; $element->siteId = $this->primarySiteId; $element->setCustomPreviewTargets([ @@ -196,13 +189,11 @@ function (RegisterPreviewTargetsEvent $event) { ], ]); - Event::on( - TestPreviewTargetsElement::class, - Element::EVENT_REGISTER_PREVIEW_TARGETS, - function (RegisterPreviewTargetsEvent $event) { + \Illuminate\Support\Facades\Event::listen(function (RegisterPreviewTargets $event) { + if ($event->element instanceof TestPreviewTargetsElement) { $event->previewTargets[0]['label'] = 'Modified by Event'; } - ); + }); $targets = $element->getPreviewTargets(); diff --git a/tests/Element/Concerns/HasRoutesAndUrlsTest.php b/tests/Element/Concerns/HasRoutesAndUrlsTest.php index 100c032d2a4..be743d7b992 100644 --- a/tests/Element/Concerns/HasRoutesAndUrlsTest.php +++ b/tests/Element/Concerns/HasRoutesAndUrlsTest.php @@ -2,15 +2,16 @@ declare(strict_types=1); -use craft\events\DefineUrlEvent; -use craft\events\SetElementRouteEvent; use CraftCms\Cms\Element\Element; +use CraftCms\Cms\Element\Events\BeforeDefineUrl; +use CraftCms\Cms\Element\Events\DefineUrl; +use CraftCms\Cms\Element\Events\SetRoute; use CraftCms\Cms\Entry\Elements\Entry; use CraftCms\Cms\Entry\Models\Entry as EntryModel; use CraftCms\Cms\Support\Facades\Sites; use CraftCms\Cms\User\Elements\User; +use Illuminate\Support\Facades\Event; use Twig\Markup; -use yii\base\Event; use function Pest\Laravel\actingAs; @@ -53,12 +54,6 @@ protected function route(): array|string|null $this->primarySiteId = Sites::getPrimarySite()->id; }); -afterEach(function () { - Event::off(TestRoutableElement::class, Element::EVENT_SET_ROUTE); - Event::off(TestRoutableElement::class, Element::EVENT_BEFORE_DEFINE_URL); - Event::off(TestRoutableElement::class, Element::EVENT_DEFINE_URL); -}); - describe('getUriFormat', function () { test('returns null by default', function () { $element = new TestRoutableElement; @@ -86,33 +81,25 @@ protected function route(): array|string|null expect($element->getRoute())->toBe('my/custom/route'); }); - test('EVENT_SET_ROUTE event can override route', function () { + test('SetRoute event can override route', function () { $element = new TestRoutableElement; $element->setCustomRoute('original-route'); - Event::on( - TestRoutableElement::class, - Element::EVENT_SET_ROUTE, - function (SetElementRouteEvent $event) { - $event->route = 'event-override-route'; - } - ); + Event::listen(function (SetRoute $event) { + $event->route = 'event-override-route'; + }); expect($element->getRoute())->toBe('event-override-route'); }); - test('EVENT_SET_ROUTE event can return null with handled flag', function () { + test('SetRoute event can return null with handled flag', function () { $element = new TestRoutableElement; $element->setCustomRoute('original-route'); - Event::on( - TestRoutableElement::class, - Element::EVENT_SET_ROUTE, - function (SetElementRouteEvent $event) { - $event->route = null; - $event->handled = true; - } - ); + Event::listen(function (SetRoute $event) { + $event->route = null; + $event->handled = true; + }); expect($element->getRoute())->toBeNull(); }); @@ -160,34 +147,26 @@ function (SetElementRouteEvent $event) { expect($url)->not->toContain(Element::HOMEPAGE_URI); }); - test('EVENT_BEFORE_DEFINE_URL can set custom URL', function () { + test('BeforeDefineUrl event can set custom URL', function () { $element = new TestRoutableElement; $element->siteId = $this->primarySiteId; $element->uri = 'test-path'; - Event::on( - TestRoutableElement::class, - Element::EVENT_BEFORE_DEFINE_URL, - function (DefineUrlEvent $event) { - $event->url = 'https://custom-url.com/path'; - } - ); + Event::listen(function (BeforeDefineUrl $event) { + $event->url = 'https://custom-url.com/path'; + }); expect($element->getUrl())->toBe('https://custom-url.com/path'); }); - test('EVENT_DEFINE_URL can modify URL', function () { + test('DefineUrl event can modify URL', function () { $element = new TestRoutableElement; $element->siteId = $this->primarySiteId; $element->uri = 'test-path'; - Event::on( - TestRoutableElement::class, - Element::EVENT_DEFINE_URL, - function (DefineUrlEvent $event) { - $event->url = $event->url.'?modified=true'; - } - ); + Event::listen(function (DefineUrl $event) { + $event->url = $event->url.'?modified=true'; + }); $url = $element->getUrl(); diff --git a/tests/Element/Concerns/HasSourcesTest.php b/tests/Element/Concerns/HasSourcesTest.php index c0b4fd53017..eb43fd34845 100644 --- a/tests/Element/Concerns/HasSourcesTest.php +++ b/tests/Element/Concerns/HasSourcesTest.php @@ -2,11 +2,10 @@ declare(strict_types=1); -use craft\events\RegisterElementFieldLayoutsEvent; -use craft\events\RegisterElementSourcesEvent; use CraftCms\Cms\Element\Element; +use CraftCms\Cms\Element\Events\RegisterFieldLayouts; +use CraftCms\Cms\Element\Events\RegisterSources; use CraftCms\Cms\Entry\Elements\Entry; -use yii\base\Event; class TestHasSourcesElement extends Element { @@ -35,16 +34,17 @@ public static function displayName(): string expect($sources1)->toBe($sources2); }); - test('triggers registerSources event', function () { + test('triggers RegisterSources event', function () { $eventTriggered = false; - $handler = function (RegisterElementSourcesEvent $event) use (&$eventTriggered) { - $eventTriggered = true; - $event->sources = []; - }; - Event::on(TestHasSourcesElement::class, TestHasSourcesElement::EVENT_REGISTER_SOURCES, $handler); - TestHasSourcesElement::sources('index'); - Event::off(TestHasSourcesElement::class, TestHasSourcesElement::EVENT_REGISTER_SOURCES, $handler); + \Illuminate\Support\Facades\Event::listen(function (RegisterSources $event) use (&$eventTriggered) { + if ($event->elementType === TestHasSourcesElement::class) { + $eventTriggered = true; + $event->sources = []; + } + }); + + TestHasSourcesElement::sources('modal'); expect($eventTriggered)->toBeTrue(); }); @@ -82,16 +82,17 @@ public static function displayName(): string expect($layouts)->toBeArray(); }); - test('triggers registerFieldLayouts event', function () { + test('triggers RegisterFieldLayouts event', function () { $eventTriggered = false; - $handler = function (RegisterElementFieldLayoutsEvent $event) use (&$eventTriggered) { - $eventTriggered = true; - $event->fieldLayouts = []; - }; - Event::on(Entry::class, Entry::EVENT_REGISTER_FIELD_LAYOUTS, $handler); + \Illuminate\Support\Facades\Event::listen(function (RegisterFieldLayouts $event) use (&$eventTriggered) { + if ($event->elementType === Entry::class) { + $eventTriggered = true; + $event->fieldLayouts = []; + } + }); + Entry::fieldLayouts(null); - Event::off(Entry::class, Entry::EVENT_REGISTER_FIELD_LAYOUTS, $handler); expect($eventTriggered)->toBeTrue(); }); diff --git a/tests/Element/Concerns/LocalizableTest.php b/tests/Element/Concerns/LocalizableTest.php index 4185a75457e..9d54213fe54 100644 --- a/tests/Element/Concerns/LocalizableTest.php +++ b/tests/Element/Concerns/LocalizableTest.php @@ -9,7 +9,6 @@ use CraftCms\Cms\Element\Queries\ElementQuery; use CraftCms\Cms\Field\Contracts\ElementContainerFieldInterface; use CraftCms\Cms\Support\Facades\Sites; -use ReflectionClass; use yii\base\InvalidConfigException; class TestLocalizableElement extends Element diff --git a/tests/Element/Concerns/RenderableTest.php b/tests/Element/Concerns/RenderableTest.php index c195e0ea501..4eaf7f4f643 100644 --- a/tests/Element/Concerns/RenderableTest.php +++ b/tests/Element/Concerns/RenderableTest.php @@ -2,13 +2,12 @@ declare(strict_types=1); -use craft\events\RenderElementEvent; use CraftCms\Cms\Cms; -use CraftCms\Cms\Element\Element; +use CraftCms\Cms\Element\Events\Render; use CraftCms\Cms\Entry\Models\Entry as EntryModel; use CraftCms\Cms\User\Elements\User; +use Illuminate\Support\Facades\Event; use Twig\Markup; -use yii\base\Event; use function Pest\Laravel\actingAs; @@ -29,58 +28,35 @@ expect($markup)->toBeInstanceOf(Markup::class); }); - test('triggers render event', function () { - $eventTriggered = false; + test('Render event allows setting custom output', function () { $customOutput = 'Custom Output'; - Event::on( - Element::class, - Element::EVENT_RENDER, - function (RenderElementEvent $event) use (&$eventTriggered, $customOutput) { - $eventTriggered = true; - $event->output = $customOutput; - } - ); + Event::listen(function (Render $event) use ($customOutput) { + $event->output = $customOutput; + }); $markup = $this->entry->render(); - expect($eventTriggered)->toBeTrue(); expect((string) $markup)->toBe($customOutput); - - Event::off(Element::class, Element::EVENT_RENDER); }); - test('event can modify variables and templates', function () { - $eventTriggered = false; - $customVariables = ['foo' => 'bar']; - - Event::on( - Element::class, - Element::EVENT_RENDER, - function (RenderElementEvent $event) use (&$eventTriggered, $customVariables) { - $eventTriggered = true; - $event->variables = array_merge($event->variables, $customVariables); - } - ); - - // We can't easily verify templates logic without setting up view paths, - // but we can verify the event was triggered and properties were accessible. - $this->entry->render(); + test('Render event can modify variables and templates', function () { + $capturedVariables = null; - expect($eventTriggered)->toBeTrue(); + Event::listen(function (Render $event) use (&$capturedVariables) { + $event->variables = array_merge($event->variables, ['foo' => 'bar']); + $capturedVariables = $event->variables; + }); - Event::off(Element::class, Element::EVENT_RENDER); + $this->entry->render(); + + expect($capturedVariables)->toHaveKey('foo'); + expect($capturedVariables['foo'])->toBe('bar'); }); }); describe('partialTemplatePathCandidates', function () { test('returns correct candidates', function () { - // partialTemplatePathCandidates is protected, so we access it via reflection or if we can infer it from render behavior. - // Or we can assume it works if render works. - // However, we can use reflection to test protected methods if needed, or check if public API exposes it. - // Element::render uses it. - - // We'll test it via reflection to be sure. $reflection = new ReflectionClass($this->entry); $method = $reflection->getMethod('partialTemplatePathCandidates'); diff --git a/tests/Element/Concerns/SearchableTest.php b/tests/Element/Concerns/SearchableTest.php index f19f34ade6f..2705cfad1c6 100644 --- a/tests/Element/Concerns/SearchableTest.php +++ b/tests/Element/Concerns/SearchableTest.php @@ -2,12 +2,12 @@ declare(strict_types=1); -use craft\events\DefineAttributeKeywordsEvent; use CraftCms\Cms\Element\Element; +use CraftCms\Cms\Element\Events\DefineKeywords; use CraftCms\Cms\Entry\Elements\Entry; use CraftCms\Cms\Entry\Models\Entry as EntryModel; use CraftCms\Cms\User\Elements\User; -use yii\base\Event; +use Illuminate\Support\Facades\Event; use function Pest\Laravel\actingAs; @@ -54,66 +54,48 @@ protected function searchKeywords(string $attribute): string expect($element->getSearchKeywords('customField'))->toBe('custom-keywords-for-value'); }); - test('EVENT_DEFINE_KEYWORDS event can override keywords when handled', function () { + test('DefineKeywords event can override keywords when handled', function () { $element = new TestSearchableElement; $element->title = 'Original Title'; - Event::on( - TestSearchableElement::class, - Element::EVENT_DEFINE_KEYWORDS, - function (DefineAttributeKeywordsEvent $event) { - if ($event->attribute === 'title') { - $event->keywords = 'overridden-keywords'; - $event->handled = true; - } + Event::listen(function (DefineKeywords $event) { + if ($event->attribute === 'title') { + $event->keywords = 'overridden-keywords'; + $event->handled = true; } - ); + }); $keywords = $element->getSearchKeywords('title'); expect($keywords)->toBe('overridden-keywords'); - - Event::off(TestSearchableElement::class, Element::EVENT_DEFINE_KEYWORDS); }); - test('EVENT_DEFINE_KEYWORDS returns empty string when keywords is null and handled', function () { + test('DefineKeywords returns empty string when keywords is empty and handled', function () { $element = new TestSearchableElement; $element->title = 'Original Title'; - Event::on( - TestSearchableElement::class, - Element::EVENT_DEFINE_KEYWORDS, - function (DefineAttributeKeywordsEvent $event) { - unset($event->keywords); - $event->handled = true; - } - ); + Event::listen(function (DefineKeywords $event) { + $event->keywords = ''; + $event->handled = true; + }); $keywords = $element->getSearchKeywords('title'); expect($keywords)->toBe(''); - - Event::off(TestSearchableElement::class, Element::EVENT_DEFINE_KEYWORDS); }); - test('EVENT_DEFINE_KEYWORDS is ignored when not handled', function () { + test('DefineKeywords is ignored when not handled', function () { $element = new TestSearchableElement; $element->title = 'Original Title'; - Event::on( - TestSearchableElement::class, - Element::EVENT_DEFINE_KEYWORDS, - function (DefineAttributeKeywordsEvent $event) { - $event->keywords = 'this-should-be-ignored'; - // Note: not setting $event->handled = true - } - ); + Event::listen(function (DefineKeywords $event) { + $event->keywords = 'this-should-be-ignored'; + // Note: not setting $event->handled = true + }); $keywords = $element->getSearchKeywords('title'); expect($keywords)->toBe('Original Title'); - - Event::off(TestSearchableElement::class, Element::EVENT_DEFINE_KEYWORDS); }); test('works with real Entry element', function () { diff --git a/yii2-adapter/legacy/base/Element.php b/yii2-adapter/legacy/base/Element.php index a5fa228935b..be2e91cb9f1 100644 --- a/yii2-adapter/legacy/base/Element.php +++ b/yii2-adapter/legacy/base/Element.php @@ -1,21 +1,1181 @@ $event->element, + 'value' => $event->tags, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_CACHE_TAGS, $yiiEvent); + + $event->tags = $yiiEvent->value; + } + }); + + Event::listen(function(RegisterSources $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_SOURCES)) { + continue; + } + + $yiiEvent = new RegisterElementSourcesEvent([ + 'context' => $event->context, + 'sources' => $event->sources, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_SOURCES, $yiiEvent); + + $event->sources = $yiiEvent->sources; + } + }); + + Event::listen(function(RegisterFieldLayouts $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_FIELD_LAYOUTS)) { + continue; + } + + $yiiEvent = new RegisterElementFieldLayoutsEvent([ + 'source' => $event->source, + 'fieldLayouts' => $event->fieldLayouts, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_FIELD_LAYOUTS, $yiiEvent); + + $event->fieldLayouts = $yiiEvent->fieldLayouts; + } + }); + + Event::listen(function(RegisterPreviewTargets $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_PREVIEW_TARGETS)) { + continue; + } + + $yiiEvent = new RegisterPreviewTargetsEvent([ + 'sender' => $event->element, + 'previewTargets' => $event->previewTargets, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_PREVIEW_TARGETS, $yiiEvent); + + $event->previewTargets = $yiiEvent->previewTargets; + } + }); + + Event::listen(function(RegisterActions $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_ACTIONS)) { + continue; + } + + $yiiEvent = new RegisterElementActionsEvent([ + 'source' => $event->source, + 'actions' => $event->actions, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_ACTIONS, $yiiEvent); + + $event->actions = $yiiEvent->actions; + } + }); + + Event::listen(function(RegisterExporters $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_EXPORTERS)) { + continue; + } + + $yiiEvent = new RegisterElementExportersEvent([ + 'source' => $event->source, + 'exporters' => $event->exporters, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_EXPORTERS, $yiiEvent); + + $event->exporters = $yiiEvent->exporters; + } + }); + + Event::listen(function(Render $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_RENDER)) { + continue; + } + + $yiiEvent = new RenderElementEvent([ + 'sender' => $event->element, + 'templates' => $event->templates, + 'variables' => $event->variables, + ]); + + YiiEvent::trigger($class, self::EVENT_RENDER, $yiiEvent); + + if (isset($yiiEvent->output)) { + $event->output = $yiiEvent->output; + } + $event->templates = $yiiEvent->templates; + $event->variables = $yiiEvent->variables; + } + }); + + Event::listen(function(DefineKeywords $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_KEYWORDS)) { + continue; + } + + $yiiEvent = new DefineAttributeKeywordsEvent([ + 'sender' => $event->element, + 'attribute' => $event->attribute, + 'keywords' => $event->keywords, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_KEYWORDS, $yiiEvent); + + if ($yiiEvent->handled) { + $event->keywords = $yiiEvent->keywords; + $event->handled = true; + } + } + }); + + Event::listen(function(RegisterSortOptions $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_SORT_OPTIONS)) { + continue; + } + + $yiiEvent = new RegisterElementSortOptionsEvent([ + 'sortOptions' => $event->sortOptions, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_SORT_OPTIONS, $yiiEvent); + + $event->sortOptions = $yiiEvent->sortOptions; + } + }); + + Event::listen(function(RegisterTableAttributes $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_TABLE_ATTRIBUTES)) { + continue; + } + + $yiiEvent = new RegisterElementTableAttributesEvent([ + 'tableAttributes' => $event->tableAttributes, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_TABLE_ATTRIBUTES, $yiiEvent); + + $event->tableAttributes = $yiiEvent->tableAttributes; + } + }); + + Event::listen(function(RegisterDefaultTableAttributes $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_DEFAULT_TABLE_ATTRIBUTES)) { + continue; + } + + $yiiEvent = new RegisterElementDefaultTableAttributesEvent([ + 'source' => $event->source, + 'tableAttributes' => $event->tableAttributes, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_DEFAULT_TABLE_ATTRIBUTES, $yiiEvent); + + $event->tableAttributes = $yiiEvent->tableAttributes; + } + }); + + Event::listen(function(RegisterCardAttributes $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_CARD_ATTRIBUTES)) { + continue; + } + + $yiiEvent = new RegisterElementCardAttributesEvent([ + 'cardAttributes' => $event->cardAttributes, + 'fieldLayout' => $event->fieldLayout, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_CARD_ATTRIBUTES, $yiiEvent); + + $event->cardAttributes = $yiiEvent->cardAttributes; + } + }); + + Event::listen(function(RegisterDefaultCardAttributes $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_DEFAULT_CARD_ATTRIBUTES)) { + continue; + } + + $yiiEvent = new RegisterElementDefaultCardAttributesEvent([ + 'cardAttributes' => $event->cardAttributes, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_DEFAULT_CARD_ATTRIBUTES, $yiiEvent); + + $event->cardAttributes = $yiiEvent->cardAttributes; + } + }); + + Event::listen(function(RegisterSearchableAttributes $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_SEARCHABLE_ATTRIBUTES)) { + continue; + } + + $yiiEvent = new RegisterElementSearchableAttributesEvent([ + 'attributes' => $event->attributes, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_SEARCHABLE_ATTRIBUTES, $yiiEvent); + + $event->attributes = $yiiEvent->attributes; + } + }); + + Event::listen(function(PrepQueryForTableAttribute $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_PREP_QUERY_FOR_TABLE_ATTRIBUTE)) { + continue; + } + + $yiiEvent = new ElementIndexTableAttributeEvent([ + 'query' => $event->query, + 'attribute' => $event->attribute, + ]); + + YiiEvent::trigger($class, self::EVENT_PREP_QUERY_FOR_TABLE_ATTRIBUTE, $yiiEvent); + + if ($yiiEvent->handled) { + $event->handled = true; + } + } + }); + + Event::listen(function(DefineEagerLoadingMap $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_EAGER_LOADING_MAP)) { + continue; + } + + $yiiEvent = new DefineEagerLoadingMapEvent([ + 'sourceElements' => $event->sourceElements, + 'handle' => $event->handle, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_EAGER_LOADING_MAP, $yiiEvent); + + if ($yiiEvent->elementType !== null) { + $event->targetElementType = $yiiEvent->elementType; + $event->map = $yiiEvent->map; + $event->criteria = $yiiEvent->criteria; + } + } + }); + + Event::listen(function(SetEagerLoadedElements $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_SET_EAGER_LOADED_ELEMENTS)) { + continue; + } + + $yiiEvent = new SetEagerLoadedElementsEvent([ + 'sender' => $event->element, + 'handle' => $event->handle, + 'elements' => $event->elements, + 'plan' => $event->plan, + ]); + + YiiEvent::trigger($class, self::EVENT_SET_EAGER_LOADED_ELEMENTS, $yiiEvent); + + if ($yiiEvent->handled) { + $event->handled = true; + } + } + }); + + Event::listen(function(BeforeSave $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_BEFORE_SAVE)) { + continue; + } + + $yiiEvent = new ModelEvent([ + 'sender' => $event->element, + 'isNew' => $event->isNew, + ]); + + YiiEvent::trigger($class, self::EVENT_BEFORE_SAVE, $yiiEvent); + + if (!$yiiEvent->isValid) { + $event->isValid = false; + } + } + }); + + Event::listen(function(AfterSave $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_AFTER_SAVE)) { + continue; + } + + $yiiEvent = new ModelEvent([ + 'sender' => $event->element, + 'isNew' => $event->isNew, + ]); + + YiiEvent::trigger($class, self::EVENT_AFTER_SAVE, $yiiEvent); + } + }); + + Event::listen(function(AfterPropagate $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_AFTER_PROPAGATE)) { + continue; + } + + $yiiEvent = new ModelEvent([ + 'sender' => $event->element, + 'isNew' => $event->isNew, + ]); + + YiiEvent::trigger($class, self::EVENT_AFTER_PROPAGATE, $yiiEvent); + } + }); + + Event::listen(function(BeforeDelete $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_BEFORE_DELETE)) { + continue; + } + + $yiiEvent = new ModelEvent([ + 'sender' => $event->element, + ]); + + YiiEvent::trigger($class, self::EVENT_BEFORE_DELETE, $yiiEvent); + + if (!$yiiEvent->isValid) { + $event->isValid = false; + } + } + }); + + Event::listen(function(AfterDelete $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_AFTER_DELETE)) { + continue; + } + + $yiiEvent = new ModelEvent([ + 'sender' => $event->element, + ]); + + YiiEvent::trigger($class, self::EVENT_AFTER_DELETE, $yiiEvent); + } + }); + + Event::listen(function(BeforeRestore $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_BEFORE_RESTORE)) { + continue; + } + + $yiiEvent = new ModelEvent([ + 'sender' => $event->element, + ]); + + YiiEvent::trigger($class, self::EVENT_BEFORE_RESTORE, $yiiEvent); + + if (!$yiiEvent->isValid) { + $event->isValid = false; + } + } + }); + + Event::listen(function(AfterRestore $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_AFTER_RESTORE)) { + continue; + } + + $yiiEvent = new ModelEvent([ + 'sender' => $event->element, + ]); + + YiiEvent::trigger($class, self::EVENT_AFTER_RESTORE, $yiiEvent); + } + }); + + Event::listen(function(DefineAdditionalButtons $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_ADDITIONAL_BUTTONS)) { + continue; + } + + $yiiEvent = new DefineHtmlEvent([ + 'sender' => $event->element, + 'html' => $event->html, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_ADDITIONAL_BUTTONS, $yiiEvent); + + $event->html = $yiiEvent->html; + } + }); + + Event::listen(function(DefineAltActions $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_ALT_ACTIONS)) { + continue; + } + + $yiiEvent = new DefineAltActionsEvent([ + 'sender' => $event->element, + 'altActions' => $event->altActions, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_ALT_ACTIONS, $yiiEvent); + + $event->altActions = $yiiEvent->altActions; + } + }); + + Event::listen(function(DefineActionMenuItems $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_ACTION_MENU_ITEMS)) { + continue; + } + + $yiiEvent = new DefineMenuItemsEvent([ + 'sender' => $event->element, + 'items' => $event->items, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_ACTION_MENU_ITEMS, $yiiEvent); + + $event->items = $yiiEvent->items; + } + }); + + Event::listen(function(DefineSidebarHtml $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_SIDEBAR_HTML)) { + continue; + } + + $yiiEvent = new DefineHtmlEvent([ + 'sender' => $event->element, + 'html' => $event->html, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_SIDEBAR_HTML, $yiiEvent); + + $event->html = $yiiEvent->html; + } + }); + + Event::listen(function(DefineMetaFieldsHtml $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_META_FIELDS_HTML)) { + continue; + } + + $yiiEvent = new DefineHtmlEvent([ + 'sender' => $event->element, + 'static' => $event->static, + 'html' => $event->html, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_META_FIELDS_HTML, $yiiEvent); + + $event->html = $yiiEvent->html; + } + }); + + Event::listen(function(DefineMetadata $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_METADATA)) { + continue; + } + + $yiiEvent = new DefineMetadataEvent([ + 'sender' => $event->element, + 'metadata' => $event->metadata, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_METADATA, $yiiEvent); + + $event->metadata = $yiiEvent->metadata; + } + }); + + Event::listen(function(RegisterHtmlAttributes $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_REGISTER_HTML_ATTRIBUTES)) { + continue; + } + + $yiiEvent = new RegisterElementHtmlAttributesEvent([ + 'sender' => $event->element, + 'htmlAttributes' => $event->htmlAttributes, + ]); + + YiiEvent::trigger($class, self::EVENT_REGISTER_HTML_ATTRIBUTES, $yiiEvent); + + $event->htmlAttributes = $yiiEvent->htmlAttributes; + } + }); + + Event::listen(function(DefineAttributeHtml $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_ATTRIBUTE_HTML)) { + continue; + } + + $yiiEvent = new DefineAttributeHtmlEvent([ + 'sender' => $event->element, + 'attribute' => $event->attribute, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_ATTRIBUTE_HTML, $yiiEvent); + + if (isset($yiiEvent->html)) { + $event->html = $yiiEvent->html; + } + } + }); + + Event::listen(function(DefineInlineAttributeInputHtml $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_INLINE_ATTRIBUTE_INPUT_HTML)) { + continue; + } + + $yiiEvent = new DefineAttributeHtmlEvent([ + 'sender' => $event->element, + 'attribute' => $event->attribute, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_INLINE_ATTRIBUTE_INPUT_HTML, $yiiEvent); + + if (isset($yiiEvent->html)) { + $event->html = $yiiEvent->html; + } + } + }); + + Event::listen(function(SetRoute $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_SET_ROUTE)) { + continue; + } + + $yiiEvent = new SetElementRouteEvent([ + 'sender' => $event->element, + 'route' => $event->route, + ]); + + YiiEvent::trigger($class, self::EVENT_SET_ROUTE, $yiiEvent); + + $event->route = $yiiEvent->route; + if ($yiiEvent->handled) { + $event->handled = true; + } + } + }); + + Event::listen(function(BeforeDefineUrl $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_BEFORE_DEFINE_URL)) { + continue; + } + + $yiiEvent = new DefineUrlEvent([ + 'sender' => $event->element, + 'url' => $event->url, + ]); + + YiiEvent::trigger($class, self::EVENT_BEFORE_DEFINE_URL, $yiiEvent); + + $event->url = $yiiEvent->url; + if ($yiiEvent->handled) { + $event->handled = true; + } + } + }); + + Event::listen(function(DefineUrl $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_DEFINE_URL)) { + continue; + } + + $yiiEvent = new DefineUrlEvent([ + 'sender' => $event->element, + 'url' => $event->url, + ]); + + YiiEvent::trigger($class, self::EVENT_DEFINE_URL, $yiiEvent); + + $event->url = $yiiEvent->url; + if ($yiiEvent->handled) { + $event->handled = true; + } + } + }); + + Event::listen(function(BeforeMoveInStructure $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_BEFORE_MOVE_IN_STRUCTURE)) { + continue; + } + + $yiiEvent = new ElementStructureEvent([ + 'sender' => $event->element, + 'structureId' => $event->structureId, + ]); + + YiiEvent::trigger($class, self::EVENT_BEFORE_MOVE_IN_STRUCTURE, $yiiEvent); + + if (!$yiiEvent->isValid) { + $event->isValid = false; + } + } + }); + + Event::listen(function(AfterMoveInStructure $event) use ($elementClasses) { + foreach ($elementClasses as $class) { + if (!YiiEvent::hasHandlers($class, self::EVENT_AFTER_MOVE_IN_STRUCTURE)) { + continue; + } + + $yiiEvent = new ElementStructureEvent([ + 'sender' => $event->element, + 'structureId' => $event->structureId, + ]); + + YiiEvent::trigger($class, self::EVENT_AFTER_MOVE_IN_STRUCTURE, $yiiEvent); + } + }); } } - -class_alias(\CraftCms\Cms\Element\Element::class, Element::class); diff --git a/yii2-adapter/legacy/elements/Asset.php b/yii2-adapter/legacy/elements/Asset.php index a470bc01a87..0a593d447c3 100644 --- a/yii2-adapter/legacy/elements/Asset.php +++ b/yii2-adapter/legacy/elements/Asset.php @@ -7,15 +7,124 @@ namespace craft\elements; -/** @phpstan-ignore-next-line */ -if (false) { +use craft\base\Event as YiiEvent; +use craft\events\AssetEvent; +use craft\events\DefineAssetUrlEvent; +use craft\events\GenerateTransformEvent; +use CraftCms\Cms\Asset\Events\AfterGenerateTransform; +use CraftCms\Cms\Asset\Events\BeforeDefineAssetUrl; +use CraftCms\Cms\Asset\Events\BeforeGenerateTransform; +use CraftCms\Cms\Asset\Events\BeforeHandleFile; +use CraftCms\Cms\Asset\Events\DefineAssetUrl; +use Illuminate\Support\Facades\Event; + +/** + * @since 3.0.0 + * @deprecated 6.0.0 use {@see \CraftCms\Cms\Asset\Elements\Asset} instead. + */ +class Asset extends \CraftCms\Cms\Asset\Elements\Asset +{ + // Events + // ------------------------------------------------------------------------- + + /** + * @event AssetEvent The event that is triggered before an asset is uploaded to volume. + */ + public const string EVENT_BEFORE_HANDLE_FILE = 'beforeHandleFile'; + + /** + * @event GenerateTransformEvent The event that is triggered before a transform is generated for an asset. + */ + public const string EVENT_BEFORE_GENERATE_TRANSFORM = 'beforeGenerateTransform'; + + /** + * @event GenerateTransformEvent The event that is triggered after a transform is generated for an asset. + */ + public const string EVENT_AFTER_GENERATE_TRANSFORM = 'afterGenerateTransform'; + + /** + * @event DefineAssetUrlEvent The event that is triggered before defining the asset’s URL. + * + * @see getUrl() + */ + public const string EVENT_BEFORE_DEFINE_URL = 'beforeDefineUrl'; + /** - * @since 3.0.0 - * @deprecated 6.0.0 use {@see \CraftCms\Cms\Asset\Elements\Asset} instead. + * @event DefineAssetUrlEvent The event that is triggered when defining the asset’s URL. + * + * @see getUrl() */ - class Asset + public const string EVENT_DEFINE_URL = 'defineUrl'; + + public static function registerEvents(): void { + Event::listen(function(BeforeDefineAssetUrl $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_BEFORE_DEFINE_URL)) { + $yiiEvent = new DefineAssetUrlEvent([ + 'transform' => $event->transform, + 'asset' => $event->asset, + 'sender' => $event->asset, + ]); + + YiiEvent::trigger(self::class, self::EVENT_BEFORE_DEFINE_URL, $yiiEvent); + + $event->url = $yiiEvent->url; + $event->handled = $yiiEvent->handled; + } + }); + + Event::listen(function(DefineAssetUrl $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_DEFINE_URL)) { + $yiiEvent = new DefineAssetUrlEvent([ + 'transform' => $event->transform, + 'asset' => $event->asset, + 'sender' => $event->asset, + ]); + + YiiEvent::trigger(self::class, self::EVENT_DEFINE_URL, $yiiEvent); + + $event->url = $yiiEvent->url; + $event->handled = $yiiEvent->handled; + } + }); + + Event::listen(function(BeforeGenerateTransform $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_BEFORE_GENERATE_TRANSFORM)) { + $yiiEvent = new GenerateTransformEvent([ + 'transform' => $event->transform, + 'asset' => $event->asset, + 'url' => $event->url, + 'sender' => $event->asset, + ]); + + YiiEvent::trigger(self::class, self::EVENT_BEFORE_GENERATE_TRANSFORM, $yiiEvent); + + $event->url = $yiiEvent->url; + } + }); + + Event::listen(function(AfterGenerateTransform $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_AFTER_GENERATE_TRANSFORM)) { + $yiiEvent = new GenerateTransformEvent([ + 'transform' => $event->transform, + 'asset' => $event->asset, + 'url' => $event->url, + 'sender' => $event->asset, + ]); + + YiiEvent::trigger(self::class, self::EVENT_AFTER_GENERATE_TRANSFORM, $yiiEvent); + } + }); + + Event::listen(function(BeforeHandleFile $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_BEFORE_HANDLE_FILE)) { + $yiiEvent = new AssetEvent([ + 'asset' => $event->asset, + 'isNew' => $event->isNew, + ]); + + YiiEvent::trigger(self::class, self::EVENT_BEFORE_HANDLE_FILE, $yiiEvent); + } + }); } } - -class_alias(\CraftCms\Cms\Asset\Elements\Asset::class, Asset::class); diff --git a/yii2-adapter/legacy/elements/Entry.php b/yii2-adapter/legacy/elements/Entry.php index accaf5e9569..10b525e173f 100644 --- a/yii2-adapter/legacy/elements/Entry.php +++ b/yii2-adapter/legacy/elements/Entry.php @@ -7,15 +7,85 @@ namespace craft\elements; -/** @phpstan-ignore-next-line */ -if (false) { +use craft\base\Event as YiiEvent; +use craft\events\DefineEntryTypesEvent; +use craft\events\ElementCriteriaEvent; +use CraftCms\Cms\Entry\Events\DefineEntryTypes; +use CraftCms\Cms\Entry\Events\DefineMetaFields; +use CraftCms\Cms\Entry\Events\DefineParentSelectionCriteria; +use Illuminate\Support\Facades\Event; + +/** + * @since 3.0.0 + * @deprecated 6.0.0 use {@see \CraftCms\Cms\Entry\Elements\Entry} instead. + */ +class Entry extends \CraftCms\Cms\Entry\Elements\Entry +{ + /** + * @event DefineEntryTypesEvent The event that is triggered when defining the available entry types for the entry + * + * @see getAvailableEntryTypes() + * @since 3.6.0 + */ + public const string EVENT_DEFINE_ENTRY_TYPES = 'defineEntryTypes'; + /** - * @since 3.0.0 - * @deprecated 6.0.0 use {@see \CraftCms\Cms\Entry\Elements\Entry} instead. + * @event ElementCriteriaEvent The event that is triggered when defining the parent selection criteria. + * + * @see _parentOptionCriteria() + * @since 4.4.0 */ - class Entry + public const string EVENT_DEFINE_PARENT_SELECTION_CRITERIA = 'defineParentSelectionCriteria'; + + /** + * @event DefineMetaFields The event that is triggered when defining the meta fields. + * + * @see metaFieldsHtml() + * @since 5.9.0 + */ + public const string EVENT_DEFINE_META_FIELDS = 'defineEntryMetaFields'; + + public static function registerEvents(): void { + Event::listen(function(DefineEntryTypes $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_DEFINE_ENTRY_TYPES)) { + $yiiEvent = new DefineEntryTypesEvent([ + 'entryTypes' => $event->entryTypes, + 'sender' => $event->entry, + ]); + + YiiEvent::trigger(self::class, self::EVENT_DEFINE_ENTRY_TYPES, $yiiEvent); + + $event->entryTypes = $yiiEvent->entryTypes; + } + }); + + Event::listen(function(DefineMetaFields $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_DEFINE_META_FIELDS)) { + $yiiEvent = new \craft\events\DefineMetaFields([ + 'element' => $event->entry, + 'sender' => $event->entry, + 'static' => $event->static, + 'fields' => $event->fields, + ]); + + YiiEvent::trigger(self::class, self::EVENT_DEFINE_META_FIELDS, $yiiEvent); + + $event->fields = $yiiEvent->fields; + } + }); + + Event::listen(function(DefineParentSelectionCriteria $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_DEFINE_PARENT_SELECTION_CRITERIA)) { + $yiiEvent = new ElementCriteriaEvent([ + 'sender' => $event->entry, + 'criteria' => $event->criteria, + ]); + + YiiEvent::trigger(self::class, self::EVENT_DEFINE_PARENT_SELECTION_CRITERIA, $yiiEvent); + + $event->criteria = $yiiEvent->criteria; + } + }); } } - -class_alias(\CraftCms\Cms\Entry\Elements\Entry::class, Entry::class); diff --git a/yii2-adapter/legacy/elements/User.php b/yii2-adapter/legacy/elements/User.php index a619182a7d0..261e78c16b0 100644 --- a/yii2-adapter/legacy/elements/User.php +++ b/yii2-adapter/legacy/elements/User.php @@ -9,17 +9,78 @@ namespace craft\elements; -use CraftCms\Cms\Element\Element; +use craft\base\Event as YiiEvent; +use craft\events\AuthenticateUserEvent; +use craft\events\DefineValueEvent; +use CraftCms\Cms\Auth\Events\Authenticating; use CraftCms\Cms\User\Elements\User as UserElement; +use CraftCms\Cms\User\Events\DefineFriendlyName; +use CraftCms\Cms\User\Events\DefineName; +use Illuminate\Support\Facades\Event; + +/** + * @deprecated 6.0.0 use {@see UserElement} instead. + */ +class User extends UserElement +{ + /** + * @event DefineValueEvent The event that is triggered when defining the user’s name, as returned by [[getName()]] or [[__toString()]]. + * + * @since 3.7.0 + */ + public const string EVENT_DEFINE_NAME = 'defineName'; -/** @phpstan-ignore-next-line */ -if (false) { /** - * @deprecated 6.0.0 use {@see \CraftCms\Cms\User\Elements\User} instead. + * @event DefineValueEvent The event that is triggered when defining the user’s friendly name, as returned by [[getFriendlyName()]]. + * + * @since 3.7.0 */ - class User extends Element + public const string EVENT_DEFINE_FRIENDLY_NAME = 'defineFriendlyName'; + + /** + * @event AuthenticateUserEvent The event that is triggered before a user is authenticated. + * + * If you wish to offload authentication logic, then set [[AuthenticateUserEvent::$performAuthentication]] to `false`, and set [[$authError]] to + * something if there is an authentication error. + */ + public const string EVENT_BEFORE_AUTHENTICATE = 'beforeAuthenticate'; + + public static function registerEvents(): void { + Event::listen(function(DefineName $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_DEFINE_NAME)) { + $yiiEvent = new DefineValueEvent(); + $yiiEvent->sender = $event->user; + + YiiEvent::trigger(self::class, self::EVENT_DEFINE_NAME, $yiiEvent); + + if ($yiiEvent->value !== null) { + $event->name = $yiiEvent->value; + } + } + }); + + Event::listen(function(DefineFriendlyName $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_DEFINE_FRIENDLY_NAME)) { + $yiiEvent = new DefineValueEvent(); + $yiiEvent->sender = $event->user; + + YiiEvent::trigger(self::class, self::EVENT_DEFINE_FRIENDLY_NAME, $yiiEvent); + + if ($yiiEvent->value !== null) { + $event->name = $yiiEvent->value; + } + } + }); + + Event::listen(Authenticating::class, function(Authenticating $event) { + if (YiiEvent::hasHandlers(self::class, self::EVENT_BEFORE_AUTHENTICATE)) { + $yiiEvent = new AuthenticateUserEvent(['password' => $event->credentials['password']]); + + YiiEvent::trigger(self::class, self::EVENT_BEFORE_AUTHENTICATE, $yiiEvent); + + $event->performAuthentication = $yiiEvent->performAuthentication; + } + }); } } - -class_alias(UserElement::class, User::class); diff --git a/yii2-adapter/legacy/events/AssetEvent.php b/yii2-adapter/legacy/events/AssetEvent.php index b8343bb55e2..2ae2a7fa7a8 100644 --- a/yii2-adapter/legacy/events/AssetEvent.php +++ b/yii2-adapter/legacy/events/AssetEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 */ class AssetEvent extends CancelableEvent { diff --git a/yii2-adapter/legacy/events/DefineAltActionsEvent.php b/yii2-adapter/legacy/events/DefineAltActionsEvent.php index 7b0c1020373..f1a4b12227d 100644 --- a/yii2-adapter/legacy/events/DefineAltActionsEvent.php +++ b/yii2-adapter/legacy/events/DefineAltActionsEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 5.6.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\DefineAltActions} instead. */ class DefineAltActionsEvent extends Event { diff --git a/yii2-adapter/legacy/events/DefineAssetUrlEvent.php b/yii2-adapter/legacy/events/DefineAssetUrlEvent.php index 31cda41afa3..ba6119ea466 100644 --- a/yii2-adapter/legacy/events/DefineAssetUrlEvent.php +++ b/yii2-adapter/legacy/events/DefineAssetUrlEvent.php @@ -16,6 +16,7 @@ * * @author Pixel & Tonic, Inc. * @since 4.0.0 + * @deprecated 6.0.0 use {@see \CraftCms\Cms\Asset\Events\BeforeDefineAssetUrl} or {@see \CraftCms\Cms\Asset\Events\DefineAssetUrl} instead. */ class DefineAssetUrlEvent extends DefineUrlEvent { diff --git a/yii2-adapter/legacy/events/DefineAttributeHtmlEvent.php b/yii2-adapter/legacy/events/DefineAttributeHtmlEvent.php index 427767214af..40a1bd3d4aa 100644 --- a/yii2-adapter/legacy/events/DefineAttributeHtmlEvent.php +++ b/yii2-adapter/legacy/events/DefineAttributeHtmlEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 5.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\DefineAttributeHtml} or {@see \CraftCms\Cms\Element\Events\DefineInlineAttributeInputHtml} instead. */ class DefineAttributeHtmlEvent extends Event { diff --git a/yii2-adapter/legacy/events/DefineAttributeKeywordsEvent.php b/yii2-adapter/legacy/events/DefineAttributeKeywordsEvent.php index 81fd6a26174..ebcc7482f69 100644 --- a/yii2-adapter/legacy/events/DefineAttributeKeywordsEvent.php +++ b/yii2-adapter/legacy/events/DefineAttributeKeywordsEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.5.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\DefineKeywords} instead. */ class DefineAttributeKeywordsEvent extends Event { diff --git a/yii2-adapter/legacy/events/DefineEagerLoadingMapEvent.php b/yii2-adapter/legacy/events/DefineEagerLoadingMapEvent.php index a5f09d38826..d597e6fcedf 100644 --- a/yii2-adapter/legacy/events/DefineEagerLoadingMapEvent.php +++ b/yii2-adapter/legacy/events/DefineEagerLoadingMapEvent.php @@ -15,6 +15,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.1.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\DefineEagerLoadingMap} instead. * @phpstan-import-type EagerLoadingMapItem from ElementInterface */ class DefineEagerLoadingMapEvent extends Event diff --git a/yii2-adapter/legacy/events/DefineEntryTypesEvent.php b/yii2-adapter/legacy/events/DefineEntryTypesEvent.php index 1f8ff995f93..e30703042a2 100644 --- a/yii2-adapter/legacy/events/DefineEntryTypesEvent.php +++ b/yii2-adapter/legacy/events/DefineEntryTypesEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.6.0 + * @deprecated 6.0.0 use {@see \CraftCms\Cms\Entry\Events\DefineEntryTypes} instead. */ class DefineEntryTypesEvent extends Event { diff --git a/yii2-adapter/legacy/events/DefineHtmlEvent.php b/yii2-adapter/legacy/events/DefineHtmlEvent.php index 7a9c464cb7a..3d560e285f4 100644 --- a/yii2-adapter/legacy/events/DefineHtmlEvent.php +++ b/yii2-adapter/legacy/events/DefineHtmlEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.7.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\DefineAdditionalButtons}, {@see \CraftCms\Cms\Element\Events\DefineSidebarHtml}, or {@see \CraftCms\Cms\Element\Events\DefineMetaFieldsHtml} instead. */ class DefineHtmlEvent extends Event { diff --git a/yii2-adapter/legacy/events/DefineMenuItemsEvent.php b/yii2-adapter/legacy/events/DefineMenuItemsEvent.php index a88b0787c74..46b05e61b6f 100644 --- a/yii2-adapter/legacy/events/DefineMenuItemsEvent.php +++ b/yii2-adapter/legacy/events/DefineMenuItemsEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 5.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\DefineActionMenuItems} instead. */ class DefineMenuItemsEvent extends Event { diff --git a/yii2-adapter/legacy/events/DefineMetaFields.php b/yii2-adapter/legacy/events/DefineMetaFields.php index f30a543ed23..2dbe1af2845 100644 --- a/yii2-adapter/legacy/events/DefineMetaFields.php +++ b/yii2-adapter/legacy/events/DefineMetaFields.php @@ -16,6 +16,7 @@ * * @author Pixel & Tonic, Inc. * @since 5.9.0 + * @deprecated 6.0.0 use {@see \CraftCms\Cms\Entry\Events\DefineMetaFields} instead. */ class DefineMetaFields extends Event diff --git a/yii2-adapter/legacy/events/DefineMetadataEvent.php b/yii2-adapter/legacy/events/DefineMetadataEvent.php index 95ae1a7c7b4..ecd39301cf2 100644 --- a/yii2-adapter/legacy/events/DefineMetadataEvent.php +++ b/yii2-adapter/legacy/events/DefineMetadataEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.7.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\DefineMetadata} instead. */ class DefineMetadataEvent extends Event { diff --git a/yii2-adapter/legacy/events/DefineUrlEvent.php b/yii2-adapter/legacy/events/DefineUrlEvent.php index adc9fa74534..60513f73234 100644 --- a/yii2-adapter/legacy/events/DefineUrlEvent.php +++ b/yii2-adapter/legacy/events/DefineUrlEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 4.3.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\BeforeDefineUrl} or {@see \CraftCms\Cms\Element\Events\DefineUrl} instead. */ class DefineUrlEvent extends Event { diff --git a/yii2-adapter/legacy/events/DefineValueEvent.php b/yii2-adapter/legacy/events/DefineValueEvent.php index e689af26583..341c5bd78b4 100644 --- a/yii2-adapter/legacy/events/DefineValueEvent.php +++ b/yii2-adapter/legacy/events/DefineValueEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.7.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\DefineCacheTags} instead. */ class DefineValueEvent extends Event { diff --git a/yii2-adapter/legacy/events/ElementCriteriaEvent.php b/yii2-adapter/legacy/events/ElementCriteriaEvent.php index ed8021f8001..62238eb06fd 100644 --- a/yii2-adapter/legacy/events/ElementCriteriaEvent.php +++ b/yii2-adapter/legacy/events/ElementCriteriaEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.4.16 + * @deprecated 6.0.0 use {@see \CraftCms\Cms\Entry\Events\DefineParentSelectionCriteria} instead. */ class ElementCriteriaEvent extends Event { diff --git a/yii2-adapter/legacy/events/ElementIndexTableAttributeEvent.php b/yii2-adapter/legacy/events/ElementIndexTableAttributeEvent.php index 191c21cae4e..82878a2151a 100644 --- a/yii2-adapter/legacy/events/ElementIndexTableAttributeEvent.php +++ b/yii2-adapter/legacy/events/ElementIndexTableAttributeEvent.php @@ -17,6 +17,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.7.14 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\PrepQueryForTableAttribute} instead. */ class ElementIndexTableAttributeEvent extends Event { diff --git a/yii2-adapter/legacy/events/ElementStructureEvent.php b/yii2-adapter/legacy/events/ElementStructureEvent.php index 4073b408ec2..37a8f3deac8 100644 --- a/yii2-adapter/legacy/events/ElementStructureEvent.php +++ b/yii2-adapter/legacy/events/ElementStructureEvent.php @@ -12,6 +12,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\BeforeMoveInStructure} or {@see \CraftCms\Cms\Element\Events\AfterMoveInStructure} instead. */ class ElementStructureEvent extends ModelEvent { diff --git a/yii2-adapter/legacy/events/GenerateTransformEvent.php b/yii2-adapter/legacy/events/GenerateTransformEvent.php index ac041481b29..b54f0ab8f81 100644 --- a/yii2-adapter/legacy/events/GenerateTransformEvent.php +++ b/yii2-adapter/legacy/events/GenerateTransformEvent.php @@ -16,6 +16,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 use {@see \CraftCms\Cms\Asset\Events\BeforeGenerateTransform} or {@see \CraftCms\Cms\Asset\Events\AfterGenerateTransform} instead. */ class GenerateTransformEvent extends Event { diff --git a/yii2-adapter/legacy/events/ModelEvent.php b/yii2-adapter/legacy/events/ModelEvent.php index d45411c760d..f28882445b1 100644 --- a/yii2-adapter/legacy/events/ModelEvent.php +++ b/yii2-adapter/legacy/events/ModelEvent.php @@ -12,6 +12,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\BeforeSave}, {@see \CraftCms\Cms\Element\Events\AfterSave}, {@see \CraftCms\Cms\Element\Events\AfterPropagate}, {@see \CraftCms\Cms\Element\Events\BeforeDelete}, {@see \CraftCms\Cms\Element\Events\AfterDelete}, {@see \CraftCms\Cms\Element\Events\BeforeRestore}, or {@see \CraftCms\Cms\Element\Events\AfterRestore} instead. */ class ModelEvent extends \yii\base\ModelEvent { diff --git a/yii2-adapter/legacy/events/RegisterCpNavItemsEvent.php b/yii2-adapter/legacy/events/RegisterCpNavItemsEvent.php index 596b19d4339..8344605f0f4 100644 --- a/yii2-adapter/legacy/events/RegisterCpNavItemsEvent.php +++ b/yii2-adapter/legacy/events/RegisterCpNavItemsEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated in 6.0.0. Use {@see \CraftCms\Cms\Cp\Events\RegisterCpNavItems} instead. */ class RegisterCpNavItemsEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementActionsEvent.php b/yii2-adapter/legacy/events/RegisterElementActionsEvent.php index e2ab7c4d15a..b3d6307273d 100644 --- a/yii2-adapter/legacy/events/RegisterElementActionsEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementActionsEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterActions} instead. */ class RegisterElementActionsEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementCardAttributesEvent.php b/yii2-adapter/legacy/events/RegisterElementCardAttributesEvent.php index 7b0bc84782b..8afcb7eb4a6 100644 --- a/yii2-adapter/legacy/events/RegisterElementCardAttributesEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementCardAttributesEvent.php @@ -15,6 +15,7 @@ * * @author Pixel & Tonic, Inc. * @since 5.5.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterCardAttributes} instead. */ class RegisterElementCardAttributesEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementDefaultCardAttributesEvent.php b/yii2-adapter/legacy/events/RegisterElementDefaultCardAttributesEvent.php index c4b50789003..8afbfd64de4 100644 --- a/yii2-adapter/legacy/events/RegisterElementDefaultCardAttributesEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementDefaultCardAttributesEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 5.5.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterDefaultCardAttributes} instead. */ class RegisterElementDefaultCardAttributesEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementDefaultTableAttributesEvent.php b/yii2-adapter/legacy/events/RegisterElementDefaultTableAttributesEvent.php index 533f1c819fe..8b61cec6961 100644 --- a/yii2-adapter/legacy/events/RegisterElementDefaultTableAttributesEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementDefaultTableAttributesEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterDefaultTableAttributes} instead. */ class RegisterElementDefaultTableAttributesEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementExportersEvent.php b/yii2-adapter/legacy/events/RegisterElementExportersEvent.php index 21bb8197d19..9c84116d996 100644 --- a/yii2-adapter/legacy/events/RegisterElementExportersEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementExportersEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.4.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterExporters} instead. */ class RegisterElementExportersEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementFieldLayoutsEvent.php b/yii2-adapter/legacy/events/RegisterElementFieldLayoutsEvent.php index f588f30e300..0e8e697108f 100644 --- a/yii2-adapter/legacy/events/RegisterElementFieldLayoutsEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementFieldLayoutsEvent.php @@ -15,6 +15,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.5.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterFieldLayouts} instead. */ class RegisterElementFieldLayoutsEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementHtmlAttributesEvent.php b/yii2-adapter/legacy/events/RegisterElementHtmlAttributesEvent.php index 6dd153ebb31..a7f5fb50af9 100644 --- a/yii2-adapter/legacy/events/RegisterElementHtmlAttributesEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementHtmlAttributesEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterHtmlAttributes} instead. */ class RegisterElementHtmlAttributesEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementSearchableAttributesEvent.php b/yii2-adapter/legacy/events/RegisterElementSearchableAttributesEvent.php index e4b6958d3c7..288c3e5cc51 100644 --- a/yii2-adapter/legacy/events/RegisterElementSearchableAttributesEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementSearchableAttributesEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterSearchableAttributes} instead. */ class RegisterElementSearchableAttributesEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementSortOptionsEvent.php b/yii2-adapter/legacy/events/RegisterElementSortOptionsEvent.php index a49c5f70737..662ca671f58 100644 --- a/yii2-adapter/legacy/events/RegisterElementSortOptionsEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementSortOptionsEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterSortOptions} instead. */ class RegisterElementSortOptionsEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementSourcesEvent.php b/yii2-adapter/legacy/events/RegisterElementSourcesEvent.php index d9f8ab3d311..b3578347d2c 100644 --- a/yii2-adapter/legacy/events/RegisterElementSourcesEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementSourcesEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterSources} instead. */ class RegisterElementSourcesEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterElementTableAttributesEvent.php b/yii2-adapter/legacy/events/RegisterElementTableAttributesEvent.php index 6258cd177dc..0134b8b453b 100644 --- a/yii2-adapter/legacy/events/RegisterElementTableAttributesEvent.php +++ b/yii2-adapter/legacy/events/RegisterElementTableAttributesEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterTableAttributes} instead. */ class RegisterElementTableAttributesEvent extends Event { diff --git a/yii2-adapter/legacy/events/RegisterPreviewTargetsEvent.php b/yii2-adapter/legacy/events/RegisterPreviewTargetsEvent.php index e438d454d93..9a6538db695 100644 --- a/yii2-adapter/legacy/events/RegisterPreviewTargetsEvent.php +++ b/yii2-adapter/legacy/events/RegisterPreviewTargetsEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.2.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\RegisterPreviewTargets} instead. */ class RegisterPreviewTargetsEvent extends Event { diff --git a/yii2-adapter/legacy/events/RenderElementEvent.php b/yii2-adapter/legacy/events/RenderElementEvent.php index 79df2cc35cf..1b2684ccb1f 100644 --- a/yii2-adapter/legacy/events/RenderElementEvent.php +++ b/yii2-adapter/legacy/events/RenderElementEvent.php @@ -15,6 +15,7 @@ * * @author Pixel & Tonic, Inc. * @since 5.8.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\Render} instead. */ class RenderElementEvent extends Event { diff --git a/yii2-adapter/legacy/events/SetEagerLoadedElementsEvent.php b/yii2-adapter/legacy/events/SetEagerLoadedElementsEvent.php index bb16fbaa98a..28661a6ac37 100644 --- a/yii2-adapter/legacy/events/SetEagerLoadedElementsEvent.php +++ b/yii2-adapter/legacy/events/SetEagerLoadedElementsEvent.php @@ -16,6 +16,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.5.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\SetEagerLoadedElements} instead. */ class SetEagerLoadedElementsEvent extends Event { diff --git a/yii2-adapter/legacy/events/SetElementRouteEvent.php b/yii2-adapter/legacy/events/SetElementRouteEvent.php index fc49ff2b5a5..34c3db3d8b0 100644 --- a/yii2-adapter/legacy/events/SetElementRouteEvent.php +++ b/yii2-adapter/legacy/events/SetElementRouteEvent.php @@ -14,6 +14,7 @@ * * @author Pixel & Tonic, Inc. * @since 3.0.0 + * @deprecated 6.0.0 Use {@see \CraftCms\Cms\Element\Events\SetRoute} instead. */ class SetElementRouteEvent extends Event { diff --git a/yii2-adapter/legacy/services/Elements.php b/yii2-adapter/legacy/services/Elements.php index d530b79791d..d1f82866cfd 100644 --- a/yii2-adapter/legacy/services/Elements.php +++ b/yii2-adapter/legacy/services/Elements.php @@ -48,6 +48,7 @@ use CraftCms\Cms\Element\Drafts; use CraftCms\Cms\Element\Element; use CraftCms\Cms\Element\ElementCollection; +use CraftCms\Cms\Element\Events\AfterPropagate; use CraftCms\Cms\Element\Exceptions\InvalidElementException; use CraftCms\Cms\Element\Models\Element as ElementModel; use CraftCms\Cms\Element\Models\ElementSiteSettings; @@ -76,6 +77,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Log; use Throwable; @@ -4229,7 +4231,11 @@ private function _saveElementInternal( $siteElements[$element->siteId] = $element; $siteSettingsRecords[$element->siteId] = $siteSettingsRecord; - $element->on(Element::EVENT_AFTER_PROPAGATE, function() use ($generatedFields, $siteElements, $siteSettingsRecords) { + Event::listen(function(AfterPropagate $event) use ($element, $generatedFields, $siteElements, $siteSettingsRecords) { + if ($event->element->id !== $element->id) { + return; + } + foreach ($siteElements as $siteId => $siteElement) { $siteSettingsRecord = $siteSettingsRecords[$siteId]; $content = $siteSettingsRecord->content ?? []; diff --git a/yii2-adapter/legacy/web/twig/variables/Cp.php b/yii2-adapter/legacy/web/twig/variables/Cp.php index 26171457c53..1aec9ba4496 100644 --- a/yii2-adapter/legacy/web/twig/variables/Cp.php +++ b/yii2-adapter/legacy/web/twig/variables/Cp.php @@ -16,6 +16,7 @@ use craft\models\FieldLayout; use craft\web\twig\TemplateLoaderException; use CraftCms\Cms\Cms; +use CraftCms\Cms\Cp\Events\RegisterCpNavItems; use CraftCms\Cms\Cp\Events\RegisterCpSettings; use CraftCms\Cms\Cp\Events\RegisterReadonlyCpSettings; use CraftCms\Cms\Cp\SelectOptions; @@ -357,12 +358,9 @@ public function nav(): array ]; } - // Fire a 'registerCpNavItems' event - if ($this->hasEventHandlers(self::EVENT_REGISTER_CP_NAV_ITEMS)) { - $event = new RegisterCpNavItemsEvent(['navItems' => $navItems]); - $this->trigger(self::EVENT_REGISTER_CP_NAV_ITEMS, $event); - $navItems = $event->navItems; - } + event($event = new RegisterCpNavItems($navItems)); + + $navItems = $event->navItems; // Figure out which item is selected, and normalize the items $path = Craft::$app->getRequest()->getPathInfo(); diff --git a/yii2-adapter/src/Yii2ServiceProvider.php b/yii2-adapter/src/Yii2ServiceProvider.php index 464963bfb38..a8d83b35515 100644 --- a/yii2-adapter/src/Yii2ServiceProvider.php +++ b/yii2-adapter/src/Yii2ServiceProvider.php @@ -3,9 +3,12 @@ namespace CraftCms\Yii2Adapter; use Craft; +use craft\base\Event as YiiEvent; use craft\console\controllers\HelpController; use craft\controllers\UsersController; +use craft\elements\Asset; use craft\elements\Category; +use craft\elements\Entry; use craft\elements\GlobalSet; use craft\elements\Tag; use craft\events\DefineFieldLayoutFieldsEvent; @@ -77,6 +80,7 @@ use CraftCms\Aliases\Aliases; use CraftCms\Cms\Cms; use CraftCms\Cms\Config\BaseConfig; +use CraftCms\Cms\Cp\Events\RegisterCpNavItems; use CraftCms\Cms\Dashboard\Widgets\Widget; use CraftCms\Cms\Database\Table; use CraftCms\Cms\Edition\Events\EditionChanged; @@ -128,7 +132,6 @@ use PDOException; use RuntimeException; use Symfony\Component\Finder\Finder; -use yii\base\Event as YiiEvent; use yii\BaseYii; use yii\caching\TagDependency as YiiTagDependency; use Yiisoft\Translator\CategorySource; @@ -471,6 +474,14 @@ public function convertDefinition(array $definition, string $type): string */ private function bootEvents(): void { + /** + * Elements + */ + \craft\base\Element::registerEvents(); + Asset::registerEvents(); + Entry::registerEvents(); + \craft\elements\User::registerEvents(); + /** * Services */ @@ -509,7 +520,17 @@ private function bootEvents(): void */ Cp::registerEvents(); - Event::listen(EditionChanged::class, function(EditionChanged $event) { + Event::listen(function(RegisterCpNavItems $event) { + if (YiiEvent::hasHandlers(CpVariable::class, 'registerCpNavItems')) { + $yiiEvent = new RegisterCpNavItemsEvent(['navItems' => $event->navItems]); + + YiiEvent::trigger(CpVariable::class, 'registerCpNavItems', $yiiEvent); + + $event->navItems = $yiiEvent->navItems; + } + }); + + Event::listen(function(EditionChanged $event) { /** @var \craft\web\Application $craft */ $craft = app('Craft'); diff --git a/yii2-adapter/tests/unit/elements/EntryElementTest.php b/yii2-adapter/tests/unit/elements/EntryElementTest.php index 845777dee05..6c273f57172 100644 --- a/yii2-adapter/tests/unit/elements/EntryElementTest.php +++ b/yii2-adapter/tests/unit/elements/EntryElementTest.php @@ -7,10 +7,11 @@ namespace crafttests\unit\elements; +use craft\base\Element; +use craft\base\Event; use craft\events\DefineUrlEvent; use craft\helpers\UrlHelper; use craft\test\TestCase; -use CraftCms\Cms\Element\Element; use CraftCms\Cms\Entry\Elements\Entry; use UnitTester; @@ -41,11 +42,11 @@ public function testGetUrl(string|callable|null $expected, ?string $uri, ?callab $entry->uri = $uri; if ($beforeEvent) { - $entry->on(Element::EVENT_BEFORE_DEFINE_URL, $beforeEvent); + Event::on(\craft\elements\Entry::class, Element::EVENT_BEFORE_DEFINE_URL, $beforeEvent); } if ($afterEvent) { - $entry->on(Element::EVENT_DEFINE_URL, $afterEvent); + Event::on(\craft\elements\Entry::class, Element::EVENT_DEFINE_URL, $afterEvent); } if (is_callable($expected)) {