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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/elements/db/ElementQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,13 @@ class ElementQuery extends Query implements ElementQueryInterface
Element::STATUS_ENABLED,
];

/**
* @var bool Whether to only include elements whose owner is also enabled (recursively).
* @used-by enabledByOwner()
* @since 5.9.0
*/
public bool $enabledByOwner = false;

/**
* @var bool Whether to return only archived elements.
* @used-by archived()
Expand Down Expand Up @@ -986,6 +993,16 @@ public function status(array|string|null $value): static
return $this;
}

/**
* @inheritdoc
* @uses $enabledByOwner
*/
public function enabledByOwner(bool $value = true): static
{
$this->enabledByOwner = $value;
return $this;
}

/**
* @inheritdoc
* @uses $archived
Expand Down Expand Up @@ -1708,6 +1725,10 @@ public function prepare($builder): Query
$this->subQuery->andWhere(Db::parseNumericParam('elements_sites.id', $this->siteSettingsId));
}

if ($this->enabledByOwner) {
$this->subQuery->andWhere(['elements_sites.enabledByOwner' => true]);
}

if ($this->archived) {
$this->subQuery->andWhere(['elements.archived' => true]);
} else {
Expand Down
9 changes: 9 additions & 0 deletions src/elements/db/ElementQueryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,15 @@ public function fixedOrder(bool $value = true): static;
*/
public function status(array|string|null $value): static;

/**
* Narrows the query results to those whose owner is also enabled (recursively).
*
* @param bool $value The property value
* @return static self reference
* @since 5.9.0
*/
public function enabledByOwner(bool $value = true): static;

/**
* Sets the [[$archived]] property.
*
Expand Down
2 changes: 2 additions & 0 deletions src/migrations/Install.php
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ public function createTables(): void
'uri' => $this->string(),
'content' => $this->json(),
'enabled' => $this->boolean()->notNull()->defaultValue(true),
'enabledByOwner' => $this->boolean()->notNull()->defaultValue(true),
'dateCreated' => $this->dateTime()->notNull(),
'dateUpdated' => $this->dateTime()->notNull(),
'uid' => $this->uid(),
Expand Down Expand Up @@ -907,6 +908,7 @@ public function createIndexes(): void
$this->createIndex(null, Table::ELEMENTS_SITES, ['title', 'siteId'], false);
$this->createIndex(null, Table::ELEMENTS_SITES, ['slug', 'siteId'], false);
$this->createIndex(null, Table::ELEMENTS_SITES, ['enabled'], false);
$this->createIndex(null, Table::ELEMENTS_SITES, ['enabledByOwner'], false);
$this->createIndex(null, Table::SYSTEMMESSAGES, ['key', 'language'], true);
$this->createIndex(null, Table::SYSTEMMESSAGES, ['language'], false);
$this->createIndex(null, Table::ENTRIES, ['postDate'], false);
Expand Down
35 changes: 35 additions & 0 deletions src/migrations/m251208_193926_enabledByOwner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace craft\migrations;

use craft\db\Migration;
use craft\db\Table;

/**
* m251208_193926_enabledByOwner migration.
*/
class m251208_193926_enabledByOwner extends Migration
{
/**
* @inheritdoc
*/
public function safeUp(): bool
{
$this->addColumn(Table::ELEMENTS_SITES, 'enabledByOwner', $this->boolean()->notNull()->defaultValue(true)->after('enabled'));
$this->createIndex(null, Table::ELEMENTS_SITES, ['enabledByOwner'], false);
return true;
}

/**
* @inheritdoc
*/
public function safeDown(): bool
{
if ($this->db->columnExists(Table::ELEMENTS_SITES, 'enabledByOwner')) {
$this->dropIndexIfExists(Table::ELEMENTS_SITES, 'enabledByOwner');
$this->dropColumn(Table::ELEMENTS_SITES, 'enabledByOwner');
}

return true;
}
}
1 change: 1 addition & 0 deletions src/records/Element_SiteSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* @property string|null $uri URI
* @property array|string|null $content Content
* @property bool $enabled Enabled
* @property bool $enabledByOwner Enabled by owner
* @property Element $element Element
* @property Site $site Site
* @author Pixel & Tonic, Inc. <support@pixelandtonic.com>
Expand Down
57 changes: 54 additions & 3 deletions src/services/Elements.php
Original file line number Diff line number Diff line change
Expand Up @@ -3691,6 +3691,7 @@ public function propagateElement(
* regardless of whether it’s being resaved
* @param bool $crossSiteValidate Whether the element should be validated across all supported sites
* @param bool $saveContent Whether all the element’s content should be saved. When false (default) only dirty fields will be saved.
* @param bool $statusChanged Whether the element’s `enabled` status just changed
* @return bool
* @throws ElementNotFoundException if $element has an invalid $id
* @throws UnsupportedSiteException if the element is being saved for a site it doesn’t support
Expand All @@ -3705,6 +3706,7 @@ private function _saveElementInternal(
bool $forceTouch = false,
bool $crossSiteValidate = false,
bool $saveContent = false,
bool $statusChanged = false,
): bool {
/** @var ElementInterface&DraftBehavior $element */
$isNewElement = !$element->id;
Expand Down Expand Up @@ -3835,6 +3837,7 @@ private function _saveElementInternal(
$originalPropagateAll,
$forceTouch,
$saveContent,
$statusChanged,
$trackChanges,
$dirtyAttributes,
$updateSearchIndex,
Expand All @@ -3854,6 +3857,8 @@ private function _saveElementInternal(
$newSiteIds = $element->newSiteIds;
$element->newSiteIds = [];

$siteStatusChanged = false;

$transaction = Craft::$app->getDb()->beginTransaction();

try {
Expand Down Expand Up @@ -3881,11 +3886,18 @@ private function _saveElementInternal(
$elementRecord->draftId = (int)$element->draftId ?: null;
$elementRecord->revisionId = (int)$element->revisionId ?: null;
$elementRecord->fieldLayoutId = $element->fieldLayoutId = (int)($element->fieldLayoutId ?? $fieldLayout->id ?? 0) ?: null;
$elementRecord->enabled = (bool)$element->enabled;
$elementRecord->archived = (bool)$element->archived;
$elementRecord->dateLastMerged = Db::prepareDateForDb($element->dateLastMerged);
$elementRecord->dateDeleted = Db::prepareDateForDb($element->dateDeleted);

// Avoid `enabled` getting marked as dirty if it’s not really changing
if ($isNewElement || $elementRecord->enabled != $element->enabled) {
$elementRecord->enabled = (bool)$element->enabled;
if (!$isNewElement) {
$statusChanged = true;
}
}

if ($isNewElement) {
if (isset($element->dateCreated)) {
$elementRecord->dateCreated = Db::prepareValueForDb($element->dateCreated);
Expand Down Expand Up @@ -3961,6 +3973,31 @@ private function _saveElementInternal(
$enabledForSite = $element->getEnabledForSite();
if ($siteSettingsRecord->getIsNewRecord() || $siteSettingsRecord->enabled != $enabledForSite) {
$siteSettingsRecord->enabled = $enabledForSite;
if (!$siteSettingsRecord->getIsNewRecord()) {
$siteStatusChanged = true;
}
}

if ($element instanceof NestedElementInterface && $siteSettingsRecord->getIsNewRecord()) {
$ownerId = $element->getPrimaryOwnerId();
if ($ownerId) {
$ownerStatuses = (new Query())
->select(['e.enabled', 'enabledForSite' => 's.enabled', 's.enabledByOwner'])
->from(['e' => Table::ELEMENTS])
->innerJoin(['s' => Table::ELEMENTS_SITES], '[[s.elementId]] = [[e.id]]')
->where([
'e.id' => $ownerId,
's.siteId' => $element->siteId,
])
->one();
if ($ownerStatuses) {
$siteSettingsRecord->enabledByOwner = (
$ownerStatuses['enabled'] &&
$ownerStatuses['enabledForSite'] &&
$ownerStatuses['enabledByOwner']
);
}
}
}

// Update our list of dirty attributes
Expand Down Expand Up @@ -4056,6 +4093,10 @@ private function _saveElementInternal(
$element->setDirtyAttributes($dirtyAttributes, false);
}

if ($statusChanged || $siteStatusChanged) {
$this->updateNestedEnabledByOwnerValues($element->id, $element->siteId, $element->enabled && $enabledForSite);
}

// It is now officially saved
$element->afterSave($isNewElement);

Expand Down Expand Up @@ -4087,7 +4128,7 @@ private function _saveElementInternal(
$siteId,
$siteElement,
crossSiteValidate: $runValidation && $crossSiteValidate,
saveContent: true,
statusChanged: $statusChanged,
)) {
throw new InvalidConfigException();
}
Expand Down Expand Up @@ -4219,6 +4260,13 @@ private function _saveElementInternal(
return true;
}

private function updateNestedEnabledByOwnerValues(int $elementId, int $siteId, bool $enabledByOwner): void
{
// update nested elements' enabledByOwner values,
// but only if they are primarily owned by this element (recursively)
// ...
}

private function updateSearchIndex(
ElementInterface $element,
array $searchableDirtyFields,
Expand Down Expand Up @@ -4263,6 +4311,7 @@ private function updateSearchIndex(
* @param-out ElementInterface $siteElement
* @param bool $crossSiteValidate Whether the element should be validated across all supported sites
* @param bool $saveContent Whether the element’s content should be saved
* @param bool $statusChanged Whether the element’s `enabled` status just changed
* @retrun bool
* @throws Exception if the element couldn't be propagated
*/
Expand All @@ -4272,6 +4321,7 @@ private function _propagateElement(
int $siteId,
ElementInterface|false|null &$siteElement = null,
bool $crossSiteValidate = false,
bool $statusChanged = false,
bool $saveContent = true,
): bool {
// Make sure the element actually supports the site it's being saved in
Expand Down Expand Up @@ -4420,7 +4470,8 @@ private function _propagateElement(
$crossSiteValidate,
false,
supportedSites: $supportedSites,
saveContent: $saveContent
saveContent: $saveContent,
statusChanged: $statusChanged,
);

if (!$success) {
Expand Down
Loading