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

Filter by extension

Filter by extension

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

## Unreleased

- Fixed a bug where the stdio MCP server (`mcp/serve`) didn’t see project config changes made by other processes, such as in the control panel or via the CLI, and rejected all project config changes after them until it was restarted. ([#20597](https://github.com/craftcms/cms/pull/20597))
- Fixed a bug where stroke-based icons, such as Lucide icons in custom icon sets, were shown as filled shapes in the control panel. ([#20613](https://github.com/craftcms/cms/issues/20613))
- Fixed an error that could occur when saving an image cropped against the left or top edge in the Image Editor. ([#20601](https://github.com/craftcms/cms/issues/20601))
- Fixed a bug where element indexes’ Link columns showed links for disabled, archived, pending, and expired elements. ([#20604](https://github.com/craftcms/cms/issues/20604))
Expand Down
43 changes: 38 additions & 5 deletions src/mcp/StdioEventDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,60 @@
namespace craft\mcp;

use Craft;
use craft\db\Query;
use craft\db\Table;
use Mcp\Event\ErrorEvent;
use Mcp\Event\RequestEvent;
use Mcp\Event\ResponseEvent;
use Psr\EventDispatcher\EventDispatcherInterface;

/**
* Saves project config changes before each stdio response is sent.
* Keeps the stdio server’s project config in sync with other processes.
*
* Over HTTP, changes are saved as each request ends. The stdio server is one
* long-running command, so without this they’d only be saved when it stops,
* and nothing else would see them until then.
* Over HTTP, each request loads the project config fresh, and changes are saved as it ends. The stdio server is one
* long-running command, so without this, changes would only be saved when it stops, with nothing else seeing them
* until then, and it would never see changes other processes made, such as in the control panel or CLI.
*
* @internal
*/
class StdioEventDispatcher implements EventDispatcherInterface
{
public function dispatch(object $event): object
{
if ($event instanceof ResponseEvent || $event instanceof ErrorEvent) {
if ($event instanceof RequestEvent) {
// User permissions aren’t all stored in the project config, so they can change without a new config version
Craft::$app->getUserPermissions()->reset();
$this->reloadChangedProjectConfig();
} elseif ($event instanceof ResponseEvent || $event instanceof ErrorEvent) {
Craft::$app->getProjectConfig()->flush();
}

return $event;
}

/**
* Reloads the project config and the records cached from it if another process has changed it.
*/
private function reloadChangedProjectConfig(): void
{
$configVersion = (new Query())
->select(['configVersion'])
->from([Table::INFO])
->scalar();

if ($configVersion === Craft::$app->getInfo()->configVersion) {
return;
}

// Reload the info row, whose configVersion acquireLock() compares against
Craft::$app->getIsInstalled(true);
Craft::$app->getProjectConfig()->forgetLoadedConfig();
Craft::$app->getFields()->reset();
Craft::$app->getEntries()->reset();
Craft::$app->getSites()->reset();
Craft::$app->getVolumes()->reset();
Craft::$app->getFs()->reset();
Craft::$app->getImageTransforms()->reset();
Craft::$app->getElementSources()->refreshSources();
Comment thread
riasvdv marked this conversation as resolved.
Resolved
}
}
11 changes: 11 additions & 0 deletions src/services/Entries.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@ public function getEditableSectionIds(): array
return array_values(array_map(fn(Section $section) => $section->id, $this->getEditableSections()));
}

/**
* Forgets the cached sections and entry types, so they’re loaded again the next time they’re needed.
*
* @internal
*/
public function reset(): void
{
$this->_sections = null;
$this->_entryTypes = null;
}

/**
* Returns a memoizable array of all sections.
*
Expand Down
26 changes: 26 additions & 0 deletions src/services/Fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,32 @@ public function applyFieldDelete(string $fieldUid): void
Craft::$app->getElements()->invalidateAllCaches();
}

/**
* Forgets the cached fields and field layouts, so they’re loaded again the next time they’re needed.
*
* Unlike [[refreshFields()]], this doesn’t update the field version. Instead, it tells the already-loaded
* [[CustomFieldBehavior]] class about any field handles that have been added since.
*
* @internal
*/
public function reset(): void
{
$this->_fields = null;
$this->_layouts = null;

foreach ($this->getAllFields(false) as $field) {
CustomFieldBehavior::$fieldHandles[$field->handle] = true;
}

foreach ($this->getAllLayouts() as $layout) {
foreach ($layout->getCustomFieldElements() as $layoutElement) {
if (isset($layoutElement->handle)) {
CustomFieldBehavior::$fieldHandles[$layoutElement->handle] = true;
}
}
}
}

/**
* Refreshes the internal field cache.
*
Expand Down
10 changes: 10 additions & 0 deletions src/services/Fs.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ public function getAllFilesystemTypes(): array
return $fsTypes;
}

/**
* Forgets the cached filesystems, so they’re loaded again the next time they’re needed.
*
* @internal
*/
public function reset(): void
{
$this->_filesystems = null;
}

/**
* Returns a memoizable array of all filesystems.
*
Expand Down
10 changes: 10 additions & 0 deletions src/services/ImageTransforms.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ public function init(): void
$this->db = Instance::ensure($this->db, Connection::class);
}

/**
* Forgets the cached transforms, so they’re loaded again the next time they’re needed.
*
* @internal
*/
public function reset(): void
{
$this->_transforms = null;
}

/**
* Returns a memoizable array of all named asset transforms.
*
Expand Down
15 changes: 15 additions & 0 deletions src/services/ProjectConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,21 @@ public function reset(): void
$this->init();
}

/**
* Forgets the loaded project config, so it’s loaded again the next time it’s needed.
*
* Unlike [[reset()]], this doesn’t register the event handlers again. Call it only once changes have been saved.
*
* @internal
*/
public function forgetLoadedConfig(): void
{
$this->_internalConfig = null;
$this->_externalConfig = null;
$this->_currentWorkingConfig = null;
$this->_configFileList = [];
}

/**
* Returns a config item value by its path.
*
Expand Down
15 changes: 15 additions & 0 deletions src/services/Sites.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ public function init(): void
$this->_loadAllSites();
}

/**
* Forgets the cached site groups and reloads the sites.
*
* The current site is reloaded too. If it was the primary site or no longer exists, it becomes the new primary site.
*
* @internal
*/
public function reset(): void
{
$currentSiteId = $this->_currentSite?->primary ? null : $this->_currentSite?->id;
$this->_groups = null;
$this->refreshSites();
Comment thread
riasvdv marked this conversation as resolved.
Resolved
$this->_currentSite = $currentSiteId ? $this->getSiteById($currentSiteId, true) : null;
}

// Groups
// -------------------------------------------------------------------------

Expand Down
10 changes: 10 additions & 0 deletions src/services/Volumes.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,16 @@ public function getTotalViewableVolumes(): int
return count($this->getViewableVolumes());
}

/**
* Forgets the cached volumes, so they’re loaded again the next time they’re needed.
*
* @internal
*/
public function reset(): void
{
$this->_volumes = null;
}

/**
* Returns a memoizable array of all volumes.
*
Expand Down
Loading
Loading