Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
29711b5
Single sites should save variables in folder-based structure.
duncanmcclean Mar 17, 2025
5699fed
Add store/update methods to `GlobalSetPolicy`
duncanmcclean Mar 17, 2025
5327299
Fix v-model on global sites fieldtype
duncanmcclean Mar 17, 2025
7decfa8
Remove tests covering the previous single-site format
duncanmcclean Mar 17, 2025
fed701d
Fix typo
duncanmcclean Mar 18, 2025
3939f0e
Move "sites" config into global set
duncanmcclean Mar 18, 2025
910d8f0
Pint
duncanmcclean Mar 18, 2025
825df0b
Ensure localizations are synced correctly when saving global sets.
duncanmcclean Mar 18, 2025
8f32ffd
Only save the variables, not the entire global set.
duncanmcclean Mar 18, 2025
4fb1300
Add test to ensure edit form is shown even when variables don't exist.
duncanmcclean Mar 18, 2025
5ea65c1
Fix failing test
duncanmcclean Mar 18, 2025
05ed524
Test the localization syncing process here.
duncanmcclean Mar 18, 2025
019438e
Pint
duncanmcclean Mar 18, 2025
ef27407
wip
duncanmcclean Mar 18, 2025
71f7583
Split tests between single site / multisite again.
duncanmcclean Mar 18, 2025
ad737ac
Update `multisite` command
duncanmcclean Mar 18, 2025
cedf5c0
Global and global variables stores don't need the same directory anym…
duncanmcclean Mar 18, 2025
0d41ffa
wip
duncanmcclean Mar 18, 2025
6982958
Pint
duncanmcclean Mar 18, 2025
d11a439
Not all tests require multisite.
duncanmcclean Mar 19, 2025
accdd47
Update script
duncanmcclean Mar 19, 2025
33f6e45
Refactor how the `GlobalFactory` handles creating global variables
duncanmcclean Mar 19, 2025
a72607a
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 19, 2025
162d819
Write global variables after saving
duncanmcclean Mar 20, 2025
581891c
we probably don't need this anymore
duncanmcclean Mar 20, 2025
203e636
Drop `addLocalization` and `removeLocalization` methods
duncanmcclean Mar 20, 2025
bec1e2b
Global Variable events should trigger content listeners
duncanmcclean Mar 20, 2025
91fb98f
Global Variable events should trigger Git commits
duncanmcclean Mar 20, 2025
27333b0
Invalidate static cache based on global variable events
duncanmcclean Mar 20, 2025
69de722
wip
duncanmcclean Mar 20, 2025
5018ae8
Pint
duncanmcclean Mar 20, 2025
a9c8454
wip
duncanmcclean Mar 20, 2025
7192573
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 21, 2025
c07f6cf
Re-enable updating stache indexes after clearing cache in multisite c…
duncanmcclean Mar 21, 2025
5d89e4a
Enable/disable stache indexes just for the global set stuff
duncanmcclean Mar 21, 2025
9a221b6
Merge branch 'master' into separate-globals
jasonvarga Mar 21, 2025
68c70a8
plural
jasonvarga Mar 21, 2025
9470766
Same the way that it was
jasonvarga Mar 21, 2025
5bc45ae
Since GlobalFactory::make() no longer makes the variables, being able…
jasonvarga Mar 24, 2025
e428128
For consistency, avoid using GlobalSet::makeLocalization from outside
jasonvarga Mar 24, 2025
4970d5c
Remove globals factory
jasonvarga Mar 24, 2025
024426c
variable content is irrelevant in these tests and syntax these syntax…
jasonvarga Mar 24, 2025
c0916b6
rework sites method
jasonvarga Mar 24, 2025
a7efe4e
revert these. we still want the 404s. add tests.
jasonvarga Mar 24, 2025
d464409
didnt really need to change
jasonvarga Mar 24, 2025
8ccdac9
not anymore
jasonvarga Mar 24, 2025
54bb8b7
404 was fine but aborting results in a blank screen. might as well fi…
jasonvarga Mar 24, 2025
953a00c
multisite conversion can be simplified now ...
jasonvarga Mar 24, 2025
e0b7045
nitpick
jasonvarga Mar 24, 2025
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
2 changes: 1 addition & 1 deletion resources/js/components/globals/Sites.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<td class="text-sm">
<v-select
:options="siteOriginOptions(site)"
:value="site.origin"
:model-value="site.origin"
:searchable="false"
:reduce="(opt) => opt.value"
@update:model-value="site.origin = $event"
Expand Down
43 changes: 21 additions & 22 deletions src/Console/Commands/Multisite.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,12 +11,10 @@
use Statamic\Facades\Collection;
use Statamic\Facades\Config;
use Statamic\Facades\File;
use Statamic\Facades\GlobalSet;
use Statamic\Facades\Nav;
use Statamic\Facades\Role;
use Statamic\Facades\Site;
use Statamic\Facades\Stache;
use Statamic\Facades\YAML;
use Statamic\Rules\Handle;
use Statamic\Statamic;
use Statamic\Support\Traits\Hookable;
Expand DownExpand Up@@ -103,9 +101,9 @@ private function collectionsHaveBeenMoved(string $siteHandle): bool

private function globalsHaveBeenMoved(string $siteHandle): bool
{
$directory = Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle;

return File::isDirectory($directory);
return File::exists(
Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle
);
}

private function navsHaveBeenMoved(string $siteHandle): bool
Expand DownExpand Up@@ -264,29 +262,30 @@ private function moveCollectionTrees($collection): void

private function convertGlobalSets(): self
{
Config::set('statamic.system.multisite', true);
if ($this->siteHandle === 'default') {
// If it's default, the variables are already in the right spot.
return $this;
}

GlobalSet::all()->each(function ($set) {
$this->components->task(
description: "Updating global [{$set->handle()}]...",
task: function () use ($set) {
$this->moveGlobalSet($set);
}
);
});
$directory = Stache::store('globals')->directory();
$originalDirectory = $directory.DIRECTORY_SEPARATOR.'default';
$newDirectory = $directory.DIRECTORY_SEPARATOR.$this->siteHandle;

return $this;
}
File::makeDirectory($newDirectory);

private function moveGlobalSet($set): void
{
$yaml = YAML::file($set->path())->parse();
collect(File::getFiles($originalDirectory))
->each(function ($path) use ($originalDirectory, $newDirectory) {
$basename = pathinfo($path, PATHINFO_BASENAME);

$data = $yaml['data'] ?? [];
File::move(
$originalDirectory.DIRECTORY_SEPARATOR.$basename,
$newDirectory.DIRECTORY_SEPARATOR.$basename
);
});

$set->addLocalization($set->makeLocalization($this->siteHandle)->data($data));
File::delete($originalDirectory);

$set->save();
return $this;
}

private function convertNavs(): self
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Concerns/ListensForContentEvents.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,8 @@ trait ListensForContentEvents
\Statamic\Events\FormSaved::class,
\Statamic\Events\GlobalSetDeleted::class,
\Statamic\Events\GlobalSetSaved::class,
\Statamic\Events\GlobalVariablesDeleted::class,
\Statamic\Events\GlobalVariablesSaved::class,
\Statamic\Events\NavDeleted::class,
\Statamic\Events\NavSaved::class,
\Statamic\Events\NavTreeSaved::class,
Expand Down
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesDeleted.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesDeleted extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesDeleted extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables deleted', [], config('statamic.git.locale'));
}
}
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesSaved.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesSaved extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesSaved extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables saved', [], config('statamic.git.locale'));
}
}
74 changes: 49 additions & 25 deletions src/Globals/GlobalSet.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@ class GlobalSet implements Contract
protected $handle;
protected $afterSaveCallbacks = [];
protected $withEvents = true;
private $sites = [];

public function id()
{
Expand DownExpand Up@@ -118,12 +119,14 @@ public function save()

protected function saveOrDeleteLocalizations()
{
$localizations = $this->localizations();
$localizations = $this->freshLocalizations();

$localizations->each->save();
$this->sites()
->reject(fn ($site) => $localizations->has($site))
->each(fn ($site) => $this->makeLocalization($site)->save());

$this->freshLocalizations()
->diffKeys($localizations)
$localizations
->filter(fn ($localization) => ! $this->sites()->contains($localization->locale()))
->each->delete();
}

Expand DownExpand Up@@ -156,17 +159,10 @@ public function delete()

public function fileData()
{
$data = [
return Arr::removeNullValues([
'title' => $this->title(),
];

if (! Site::multiEnabled() && ($variables = $this->in(Site::default()->handle()))) {
$data['data'] = Arr::removeNullValues(
$variables->data()->all()
);
}

return $data;
'sites' => Site::multiEnabled() ? $this->origins()->all() : null,
]);
}

public function makeLocalization($site)
Expand All@@ -176,30 +172,53 @@ public function makeLocalization($site)
->locale($site);
}

public function addLocalization($localization)
public function sites($sites = null)
{
$localization->globalSet($this);
if (func_num_args() === 0) {
$sites = collect($this->sites);

$this->localizations()[$localization->locale()] = $localization;
if ($sites->isEmpty()) {
return collect([Site::default()->handle()]);
}

return $this;
}
return collect($this->sites)->keys();
}

public function removeLocalization($localization)
{
$this->localizations()->forget($localization->locale());
$this->sites = collect($sites)->mapWithKeys(function ($value, $key) {
if (is_int($key)) {
return [$value => ['origin' => null]];
}

if (is_string($value) || is_null($value)) {
return [$key => ['origin' => $value]];
}

return [$key => $value];
})->all();

return $this;
}

public function sites()
public function origins()
{
return $this->localizations()->map->locale()->values()->toBase();
$sites = empty($this->sites)
? [Site::default()->handle() => ['origin' => null]]
: $this->sites;

return collect($sites)->map(fn ($value, $key) => $value['origin'] ?? null);
}

public function in($locale)
{
return $this->localizations()->get($locale);
if (! $this->sites()->contains($locale)) {
return null;
}

if (! $variables = $this->localizations()->get($locale)) {
$variables = $this->makeLocalization($locale);
}

return $variables;
}

public function inSelectedSite()
Expand DownExpand Up@@ -239,6 +258,11 @@ public function editUrl()
return cp_route('globals.edit', $this->handle());
}

public function updateUrl()
{
return cp_route('globals.update', $this->handle());
}

public function deleteUrl()
{
return cp_route('globals.destroy', $this->handle());
Expand Down
17 changes: 12 additions & 5 deletions src/Globals/Variables.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -77,7 +77,7 @@ public function path()
{
return vsprintf('%s/%s%s.%s', [
rtrim(Stache::store('global-variables')->directory(), '/'),
Site::multiEnabled() ? $this->locale().'/' : '',
$this->locale().'/',
$this->handle(),
'yaml',
]);
Expand DownExpand Up@@ -152,6 +152,8 @@ public function save()
GlobalVariablesSaved::dispatch($this);
}

Blink::forget('global-set-localizations-'.$this->globalSet()->id());

return $this;
}

Expand DownExpand Up@@ -227,13 +229,18 @@ protected function fallbackBlueprint()

public function fileData()
{
$data = $this->data()->all();
return $this->data()->all();
}

if ($this->hasOrigin()) {
$data['origin'] = $this->origin()->locale();
public function origin($origin = null)
{
if (func_num_args() === 0) {
return $this->getOriginByString(
$this->globalSet()->origins()->get($this->locale())
);
}

return $data;
throw new \Exception('The origin cannot be set directly. It must be defined on the global set.');
}

protected function shouldRemoveNullsFromFileData()
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,7 @@ public function edit(Request $request, $id)
}

if (! $variables = $set->in($site)) {
return abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $variables);
Expand DownExpand Up@@ -87,7 +87,7 @@ public function update(Request $request, $handle)
}

if (! $set = $set->in($site)) {
abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $set);
Expand All@@ -104,7 +104,7 @@ public function update(Request $request, $handle)

$set->data($values);

$save = $set->globalSet()->addLocalization($set)->save();
$save = $set->save();

return response()->json([
'saved' => is_bool($save) ? $save : true,
Expand Down
26 changes: 8 additions & 18 deletions src/Http/Controllers/CP/Globals/GlobalsController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,6 @@
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Rules\Handle;
use Statamic\Support\Arr;
use Statamic\Support\Str;

class GlobalsController extends CpController
Expand DownExpand Up@@ -58,8 +57,8 @@ public function edit($set)
return [
'name' => $site->name(),
'handle' => $site->handle(),
'enabled' => $enabled = $set->existsIn($site->handle()),
'origin' => $enabled ? optional($set->in($site->handle())->origin())->locale() : null,
'enabled' => $set->sites()->contains($site->handle()),
'origin' => $set->origins()->get($site->handle()),
];
})->values(),
];
Expand DownExpand Up@@ -112,19 +111,11 @@ public function update(Request $request, $set)
->blueprint($values['blueprint']);

if (Site::multiEnabled()) {
$sites = collect(Arr::get($values, 'sites'));
$sites = collect($values['sites'])
->filter(fn ($site) => $site['enabled'])
->mapWithKeys(fn ($site) => [$site['handle'] => $site['origin']]);

foreach ($sites->filter->enabled as $site) {
$vars = $set->in($site['handle']) ?? $set->makeLocalization($site['handle']);
$vars->origin($site['origin']);
$set->addLocalization($vars);
}

foreach ($sites->reject->enabled as $site) {
if ($set->existsIn($site['handle'])) {
$set->removeLocalization($set->in($site['handle']));
}
}
$set->sites($sites);
}

$set->save();
Expand DownExpand Up@@ -161,11 +152,10 @@ public function store(Request $request)
}

$global = GlobalSet::make($handle)->title($data['title']);

$global->addLocalization($global->makeLocalization(Site::default()->handle()));

$global->save();

$global->in(Site::default()->handle())->save();

session()->flash('message', __('Global Set created'));

return ['redirect' => $global->editUrl()];
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[6.x] Separate globals config and content by duncanmcclean · Pull Request #11585 · statamic/cms · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
29711b5
Single sites should save variables in folder-based structure.
duncanmcclean Mar 17, 2025
5699fed
Add store/update methods to `GlobalSetPolicy`
duncanmcclean Mar 17, 2025
5327299
Fix v-model on global sites fieldtype
duncanmcclean Mar 17, 2025
7decfa8
Remove tests covering the previous single-site format
duncanmcclean Mar 17, 2025
fed701d
Fix typo
duncanmcclean Mar 18, 2025
3939f0e
Move "sites" config into global set
duncanmcclean Mar 18, 2025
910d8f0
Pint
duncanmcclean Mar 18, 2025
825df0b
Ensure localizations are synced correctly when saving global sets.
duncanmcclean Mar 18, 2025
8f32ffd
Only save the variables, not the entire global set.
duncanmcclean Mar 18, 2025
4fb1300
Add test to ensure edit form is shown even when variables don't exist.
duncanmcclean Mar 18, 2025
5ea65c1
Fix failing test
duncanmcclean Mar 18, 2025
05ed524
Test the localization syncing process here.
duncanmcclean Mar 18, 2025
019438e
Pint
duncanmcclean Mar 18, 2025
ef27407
wip
duncanmcclean Mar 18, 2025
71f7583
Split tests between single site / multisite again.
duncanmcclean Mar 18, 2025
ad737ac
Update `multisite` command
duncanmcclean Mar 18, 2025
cedf5c0
Global and global variables stores don't need the same directory anym…
duncanmcclean Mar 18, 2025
0d41ffa
wip
duncanmcclean Mar 18, 2025
6982958
Pint
duncanmcclean Mar 18, 2025
d11a439
Not all tests require multisite.
duncanmcclean Mar 19, 2025
accdd47
Update script
duncanmcclean Mar 19, 2025
33f6e45
Refactor how the `GlobalFactory` handles creating global variables
duncanmcclean Mar 19, 2025
a72607a
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 19, 2025
162d819
Write global variables after saving
duncanmcclean Mar 20, 2025
581891c
we probably don't need this anymore
duncanmcclean Mar 20, 2025
203e636
Drop `addLocalization` and `removeLocalization` methods
duncanmcclean Mar 20, 2025
bec1e2b
Global Variable events should trigger content listeners
duncanmcclean Mar 20, 2025
91fb98f
Global Variable events should trigger Git commits
duncanmcclean Mar 20, 2025
27333b0
Invalidate static cache based on global variable events
duncanmcclean Mar 20, 2025
69de722
wip
duncanmcclean Mar 20, 2025
5018ae8
Pint
duncanmcclean Mar 20, 2025
a9c8454
wip
duncanmcclean Mar 20, 2025
7192573
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 21, 2025
c07f6cf
Re-enable updating stache indexes after clearing cache in multisite c…
duncanmcclean Mar 21, 2025
5d89e4a
Enable/disable stache indexes just for the global set stuff
duncanmcclean Mar 21, 2025
9a221b6
Merge branch 'master' into separate-globals
jasonvarga Mar 21, 2025
68c70a8
plural
jasonvarga Mar 21, 2025
9470766
Same the way that it was
jasonvarga Mar 21, 2025
5bc45ae
Since GlobalFactory::make() no longer makes the variables, being able…
jasonvarga Mar 24, 2025
e428128
For consistency, avoid using GlobalSet::makeLocalization from outside
jasonvarga Mar 24, 2025
4970d5c
Remove globals factory
jasonvarga Mar 24, 2025
024426c
variable content is irrelevant in these tests and syntax these syntax…
jasonvarga Mar 24, 2025
c0916b6
rework sites method
jasonvarga Mar 24, 2025
a7efe4e
revert these. we still want the 404s. add tests.
jasonvarga Mar 24, 2025
d464409
didnt really need to change
jasonvarga Mar 24, 2025
8ccdac9
not anymore
jasonvarga Mar 24, 2025
54bb8b7
404 was fine but aborting results in a blank screen. might as well fi…
jasonvarga Mar 24, 2025
953a00c
multisite conversion can be simplified now ...
jasonvarga Mar 24, 2025
e0b7045
nitpick
jasonvarga Mar 24, 2025
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
2 changes: 1 addition & 1 deletion resources/js/components/globals/Sites.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<td class="text-sm">
<v-select
:options="siteOriginOptions(site)"
:value="site.origin"
:model-value="site.origin"
:searchable="false"
:reduce="(opt) => opt.value"
@update:model-value="site.origin = $event"
Expand Down
43 changes: 21 additions & 22 deletions src/Console/Commands/Multisite.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,12 +11,10 @@
use Statamic\Facades\Collection;
use Statamic\Facades\Config;
use Statamic\Facades\File;
use Statamic\Facades\GlobalSet;
use Statamic\Facades\Nav;
use Statamic\Facades\Role;
use Statamic\Facades\Site;
use Statamic\Facades\Stache;
use Statamic\Facades\YAML;
use Statamic\Rules\Handle;
use Statamic\Statamic;
use Statamic\Support\Traits\Hookable;
Expand DownExpand Up@@ -103,9 +101,9 @@ private function collectionsHaveBeenMoved(string $siteHandle): bool

private function globalsHaveBeenMoved(string $siteHandle): bool
{
$directory = Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle;

return File::isDirectory($directory);
return File::exists(
Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle
);
}

private function navsHaveBeenMoved(string $siteHandle): bool
Expand DownExpand Up@@ -264,29 +262,30 @@ private function moveCollectionTrees($collection): void

private function convertGlobalSets(): self
{
Config::set('statamic.system.multisite', true);
if ($this->siteHandle === 'default') {
// If it's default, the variables are already in the right spot.
return $this;
}

GlobalSet::all()->each(function ($set) {
$this->components->task(
description: "Updating global [{$set->handle()}]...",
task: function () use ($set) {
$this->moveGlobalSet($set);
}
);
});
$directory = Stache::store('globals')->directory();
$originalDirectory = $directory.DIRECTORY_SEPARATOR.'default';
$newDirectory = $directory.DIRECTORY_SEPARATOR.$this->siteHandle;

return $this;
}
File::makeDirectory($newDirectory);

private function moveGlobalSet($set): void
{
$yaml = YAML::file($set->path())->parse();
collect(File::getFiles($originalDirectory))
->each(function ($path) use ($originalDirectory, $newDirectory) {
$basename = pathinfo($path, PATHINFO_BASENAME);

$data = $yaml['data'] ?? [];
File::move(
$originalDirectory.DIRECTORY_SEPARATOR.$basename,
$newDirectory.DIRECTORY_SEPARATOR.$basename
);
});

$set->addLocalization($set->makeLocalization($this->siteHandle)->data($data));
File::delete($originalDirectory);

$set->save();
return $this;
}

private function convertNavs(): self
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Concerns/ListensForContentEvents.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,8 @@ trait ListensForContentEvents
\Statamic\Events\FormSaved::class,
\Statamic\Events\GlobalSetDeleted::class,
\Statamic\Events\GlobalSetSaved::class,
\Statamic\Events\GlobalVariablesDeleted::class,
\Statamic\Events\GlobalVariablesSaved::class,
\Statamic\Events\NavDeleted::class,
\Statamic\Events\NavSaved::class,
\Statamic\Events\NavTreeSaved::class,
Expand Down
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesDeleted.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesDeleted extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesDeleted extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables deleted', [], config('statamic.git.locale'));
}
}
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesSaved.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesSaved extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesSaved extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables saved', [], config('statamic.git.locale'));
}
}
74 changes: 49 additions & 25 deletions src/Globals/GlobalSet.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@ class GlobalSet implements Contract
protected $handle;
protected $afterSaveCallbacks = [];
protected $withEvents = true;
private $sites = [];

public function id()
{
Expand DownExpand Up@@ -118,12 +119,14 @@ public function save()

protected function saveOrDeleteLocalizations()
{
$localizations = $this->localizations();
$localizations = $this->freshLocalizations();

$localizations->each->save();
$this->sites()
->reject(fn ($site) => $localizations->has($site))
->each(fn ($site) => $this->makeLocalization($site)->save());

$this->freshLocalizations()
->diffKeys($localizations)
$localizations
->filter(fn ($localization) => ! $this->sites()->contains($localization->locale()))
->each->delete();
}

Expand DownExpand Up@@ -156,17 +159,10 @@ public function delete()

public function fileData()
{
$data = [
return Arr::removeNullValues([
'title' => $this->title(),
];

if (! Site::multiEnabled() && ($variables = $this->in(Site::default()->handle()))) {
$data['data'] = Arr::removeNullValues(
$variables->data()->all()
);
}

return $data;
'sites' => Site::multiEnabled() ? $this->origins()->all() : null,
]);
}

public function makeLocalization($site)
Expand All@@ -176,30 +172,53 @@ public function makeLocalization($site)
->locale($site);
}

public function addLocalization($localization)
public function sites($sites = null)
{
$localization->globalSet($this);
if (func_num_args() === 0) {
$sites = collect($this->sites);

$this->localizations()[$localization->locale()] = $localization;
if ($sites->isEmpty()) {
return collect([Site::default()->handle()]);
}

return $this;
}
return collect($this->sites)->keys();
}

public function removeLocalization($localization)
{
$this->localizations()->forget($localization->locale());
$this->sites = collect($sites)->mapWithKeys(function ($value, $key) {
if (is_int($key)) {
return [$value => ['origin' => null]];
}

if (is_string($value) || is_null($value)) {
return [$key => ['origin' => $value]];
}

return [$key => $value];
})->all();

return $this;
}

public function sites()
public function origins()
{
return $this->localizations()->map->locale()->values()->toBase();
$sites = empty($this->sites)
? [Site::default()->handle() => ['origin' => null]]
: $this->sites;

return collect($sites)->map(fn ($value, $key) => $value['origin'] ?? null);
}

public function in($locale)
{
return $this->localizations()->get($locale);
if (! $this->sites()->contains($locale)) {
return null;
}

if (! $variables = $this->localizations()->get($locale)) {
$variables = $this->makeLocalization($locale);
}

return $variables;
}

public function inSelectedSite()
Expand DownExpand Up@@ -239,6 +258,11 @@ public function editUrl()
return cp_route('globals.edit', $this->handle());
}

public function updateUrl()
{
return cp_route('globals.update', $this->handle());
}

public function deleteUrl()
{
return cp_route('globals.destroy', $this->handle());
Expand Down
17 changes: 12 additions & 5 deletions src/Globals/Variables.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -77,7 +77,7 @@ public function path()
{
return vsprintf('%s/%s%s.%s', [
rtrim(Stache::store('global-variables')->directory(), '/'),
Site::multiEnabled() ? $this->locale().'/' : '',
$this->locale().'/',
$this->handle(),
'yaml',
]);
Expand DownExpand Up@@ -152,6 +152,8 @@ public function save()
GlobalVariablesSaved::dispatch($this);
}

Blink::forget('global-set-localizations-'.$this->globalSet()->id());

return $this;
}

Expand DownExpand Up@@ -227,13 +229,18 @@ protected function fallbackBlueprint()

public function fileData()
{
$data = $this->data()->all();
return $this->data()->all();
}

if ($this->hasOrigin()) {
$data['origin'] = $this->origin()->locale();
public function origin($origin = null)
{
if (func_num_args() === 0) {
return $this->getOriginByString(
$this->globalSet()->origins()->get($this->locale())
);
}

return $data;
throw new \Exception('The origin cannot be set directly. It must be defined on the global set.');
}

protected function shouldRemoveNullsFromFileData()
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,7 @@ public function edit(Request $request, $id)
}

if (! $variables = $set->in($site)) {
return abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $variables);
Expand DownExpand Up@@ -87,7 +87,7 @@ public function update(Request $request, $handle)
}

if (! $set = $set->in($site)) {
abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $set);
Expand All@@ -104,7 +104,7 @@ public function update(Request $request, $handle)

$set->data($values);

$save = $set->globalSet()->addLocalization($set)->save();
$save = $set->save();

return response()->json([
'saved' => is_bool($save) ? $save : true,
Expand Down
26 changes: 8 additions & 18 deletions src/Http/Controllers/CP/Globals/GlobalsController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,6 @@
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Rules\Handle;
use Statamic\Support\Arr;
use Statamic\Support\Str;

class GlobalsController extends CpController
Expand DownExpand Up@@ -58,8 +57,8 @@ public function edit($set)
return [
'name' => $site->name(),
'handle' => $site->handle(),
'enabled' => $enabled = $set->existsIn($site->handle()),
'origin' => $enabled ? optional($set->in($site->handle())->origin())->locale() : null,
'enabled' => $set->sites()->contains($site->handle()),
'origin' => $set->origins()->get($site->handle()),
];
})->values(),
];
Expand DownExpand Up@@ -112,19 +111,11 @@ public function update(Request $request, $set)
->blueprint($values['blueprint']);

if (Site::multiEnabled()) {
$sites = collect(Arr::get($values, 'sites'));
$sites = collect($values['sites'])
->filter(fn ($site) => $site['enabled'])
->mapWithKeys(fn ($site) => [$site['handle'] => $site['origin']]);

foreach ($sites->filter->enabled as $site) {
$vars = $set->in($site['handle']) ?? $set->makeLocalization($site['handle']);
$vars->origin($site['origin']);
$set->addLocalization($vars);
}

foreach ($sites->reject->enabled as $site) {
if ($set->existsIn($site['handle'])) {
$set->removeLocalization($set->in($site['handle']));
}
}
$set->sites($sites);
}

$set->save();
Expand DownExpand Up@@ -161,11 +152,10 @@ public function store(Request $request)
}

$global = GlobalSet::make($handle)->title($data['title']);

$global->addLocalization($global->makeLocalization(Site::default()->handle()));

$global->save();

$global->in(Site::default()->handle())->save();

session()->flash('message', __('Global Set created'));

return ['redirect' => $global->editUrl()];
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [6.x] Separate globals config and content by duncanmcclean · Pull Request #11585 · statamic/cms · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
29711b5
Single sites should save variables in folder-based structure.
duncanmcclean Mar 17, 2025
5699fed
Add store/update methods to `GlobalSetPolicy`
duncanmcclean Mar 17, 2025
5327299
Fix v-model on global sites fieldtype
duncanmcclean Mar 17, 2025
7decfa8
Remove tests covering the previous single-site format
duncanmcclean Mar 17, 2025
fed701d
Fix typo
duncanmcclean Mar 18, 2025
3939f0e
Move "sites" config into global set
duncanmcclean Mar 18, 2025
910d8f0
Pint
duncanmcclean Mar 18, 2025
825df0b
Ensure localizations are synced correctly when saving global sets.
duncanmcclean Mar 18, 2025
8f32ffd
Only save the variables, not the entire global set.
duncanmcclean Mar 18, 2025
4fb1300
Add test to ensure edit form is shown even when variables don't exist.
duncanmcclean Mar 18, 2025
5ea65c1
Fix failing test
duncanmcclean Mar 18, 2025
05ed524
Test the localization syncing process here.
duncanmcclean Mar 18, 2025
019438e
Pint
duncanmcclean Mar 18, 2025
ef27407
wip
duncanmcclean Mar 18, 2025
71f7583
Split tests between single site / multisite again.
duncanmcclean Mar 18, 2025
ad737ac
Update `multisite` command
duncanmcclean Mar 18, 2025
cedf5c0
Global and global variables stores don't need the same directory anym…
duncanmcclean Mar 18, 2025
0d41ffa
wip
duncanmcclean Mar 18, 2025
6982958
Pint
duncanmcclean Mar 18, 2025
d11a439
Not all tests require multisite.
duncanmcclean Mar 19, 2025
accdd47
Update script
duncanmcclean Mar 19, 2025
33f6e45
Refactor how the `GlobalFactory` handles creating global variables
duncanmcclean Mar 19, 2025
a72607a
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 19, 2025
162d819
Write global variables after saving
duncanmcclean Mar 20, 2025
581891c
we probably don't need this anymore
duncanmcclean Mar 20, 2025
203e636
Drop `addLocalization` and `removeLocalization` methods
duncanmcclean Mar 20, 2025
bec1e2b
Global Variable events should trigger content listeners
duncanmcclean Mar 20, 2025
91fb98f
Global Variable events should trigger Git commits
duncanmcclean Mar 20, 2025
27333b0
Invalidate static cache based on global variable events
duncanmcclean Mar 20, 2025
69de722
wip
duncanmcclean Mar 20, 2025
5018ae8
Pint
duncanmcclean Mar 20, 2025
a9c8454
wip
duncanmcclean Mar 20, 2025
7192573
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 21, 2025
c07f6cf
Re-enable updating stache indexes after clearing cache in multisite c…
duncanmcclean Mar 21, 2025
5d89e4a
Enable/disable stache indexes just for the global set stuff
duncanmcclean Mar 21, 2025
9a221b6
Merge branch 'master' into separate-globals
jasonvarga Mar 21, 2025
68c70a8
plural
jasonvarga Mar 21, 2025
9470766
Same the way that it was
jasonvarga Mar 21, 2025
5bc45ae
Since GlobalFactory::make() no longer makes the variables, being able…
jasonvarga Mar 24, 2025
e428128
For consistency, avoid using GlobalSet::makeLocalization from outside
jasonvarga Mar 24, 2025
4970d5c
Remove globals factory
jasonvarga Mar 24, 2025
024426c
variable content is irrelevant in these tests and syntax these syntax…
jasonvarga Mar 24, 2025
c0916b6
rework sites method
jasonvarga Mar 24, 2025
a7efe4e
revert these. we still want the 404s. add tests.
jasonvarga Mar 24, 2025
d464409
didnt really need to change
jasonvarga Mar 24, 2025
8ccdac9
not anymore
jasonvarga Mar 24, 2025
54bb8b7
404 was fine but aborting results in a blank screen. might as well fi…
jasonvarga Mar 24, 2025
953a00c
multisite conversion can be simplified now ...
jasonvarga Mar 24, 2025
e0b7045
nitpick
jasonvarga Mar 24, 2025
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
2 changes: 1 addition & 1 deletion resources/js/components/globals/Sites.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<td class="text-sm">
<v-select
:options="siteOriginOptions(site)"
:value="site.origin"
:model-value="site.origin"
:searchable="false"
:reduce="(opt) => opt.value"
@update:model-value="site.origin = $event"
Expand Down
43 changes: 21 additions & 22 deletions src/Console/Commands/Multisite.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,12 +11,10 @@
use Statamic\Facades\Collection;
use Statamic\Facades\Config;
use Statamic\Facades\File;
use Statamic\Facades\GlobalSet;
use Statamic\Facades\Nav;
use Statamic\Facades\Role;
use Statamic\Facades\Site;
use Statamic\Facades\Stache;
use Statamic\Facades\YAML;
use Statamic\Rules\Handle;
use Statamic\Statamic;
use Statamic\Support\Traits\Hookable;
Expand DownExpand Up@@ -103,9 +101,9 @@ private function collectionsHaveBeenMoved(string $siteHandle): bool

private function globalsHaveBeenMoved(string $siteHandle): bool
{
$directory = Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle;

return File::isDirectory($directory);
return File::exists(
Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle
);
}

private function navsHaveBeenMoved(string $siteHandle): bool
Expand DownExpand Up@@ -264,29 +262,30 @@ private function moveCollectionTrees($collection): void

private function convertGlobalSets(): self
{
Config::set('statamic.system.multisite', true);
if ($this->siteHandle === 'default') {
// If it's default, the variables are already in the right spot.
return $this;
}

GlobalSet::all()->each(function ($set) {
$this->components->task(
description: "Updating global [{$set->handle()}]...",
task: function () use ($set) {
$this->moveGlobalSet($set);
}
);
});
$directory = Stache::store('globals')->directory();
$originalDirectory = $directory.DIRECTORY_SEPARATOR.'default';
$newDirectory = $directory.DIRECTORY_SEPARATOR.$this->siteHandle;

return $this;
}
File::makeDirectory($newDirectory);

private function moveGlobalSet($set): void
{
$yaml = YAML::file($set->path())->parse();
collect(File::getFiles($originalDirectory))
->each(function ($path) use ($originalDirectory, $newDirectory) {
$basename = pathinfo($path, PATHINFO_BASENAME);

$data = $yaml['data'] ?? [];
File::move(
$originalDirectory.DIRECTORY_SEPARATOR.$basename,
$newDirectory.DIRECTORY_SEPARATOR.$basename
);
});

$set->addLocalization($set->makeLocalization($this->siteHandle)->data($data));
File::delete($originalDirectory);

$set->save();
return $this;
}

private function convertNavs(): self
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Concerns/ListensForContentEvents.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,8 @@ trait ListensForContentEvents
\Statamic\Events\FormSaved::class,
\Statamic\Events\GlobalSetDeleted::class,
\Statamic\Events\GlobalSetSaved::class,
\Statamic\Events\GlobalVariablesDeleted::class,
\Statamic\Events\GlobalVariablesSaved::class,
\Statamic\Events\NavDeleted::class,
\Statamic\Events\NavSaved::class,
\Statamic\Events\NavTreeSaved::class,
Expand Down
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesDeleted.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesDeleted extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesDeleted extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables deleted', [], config('statamic.git.locale'));
}
}
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesSaved.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesSaved extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesSaved extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables saved', [], config('statamic.git.locale'));
}
}
74 changes: 49 additions & 25 deletions src/Globals/GlobalSet.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@ class GlobalSet implements Contract
protected $handle;
protected $afterSaveCallbacks = [];
protected $withEvents = true;
private $sites = [];

public function id()
{
Expand DownExpand Up@@ -118,12 +119,14 @@ public function save()

protected function saveOrDeleteLocalizations()
{
$localizations = $this->localizations();
$localizations = $this->freshLocalizations();

$localizations->each->save();
$this->sites()
->reject(fn ($site) => $localizations->has($site))
->each(fn ($site) => $this->makeLocalization($site)->save());

$this->freshLocalizations()
->diffKeys($localizations)
$localizations
->filter(fn ($localization) => ! $this->sites()->contains($localization->locale()))
->each->delete();
}

Expand DownExpand Up@@ -156,17 +159,10 @@ public function delete()

public function fileData()
{
$data = [
return Arr::removeNullValues([
'title' => $this->title(),
];

if (! Site::multiEnabled() && ($variables = $this->in(Site::default()->handle()))) {
$data['data'] = Arr::removeNullValues(
$variables->data()->all()
);
}

return $data;
'sites' => Site::multiEnabled() ? $this->origins()->all() : null,
]);
}

public function makeLocalization($site)
Expand All@@ -176,30 +172,53 @@ public function makeLocalization($site)
->locale($site);
}

public function addLocalization($localization)
public function sites($sites = null)
{
$localization->globalSet($this);
if (func_num_args() === 0) {
$sites = collect($this->sites);

$this->localizations()[$localization->locale()] = $localization;
if ($sites->isEmpty()) {
return collect([Site::default()->handle()]);
}

return $this;
}
return collect($this->sites)->keys();
}

public function removeLocalization($localization)
{
$this->localizations()->forget($localization->locale());
$this->sites = collect($sites)->mapWithKeys(function ($value, $key) {
if (is_int($key)) {
return [$value => ['origin' => null]];
}

if (is_string($value) || is_null($value)) {
return [$key => ['origin' => $value]];
}

return [$key => $value];
})->all();

return $this;
}

public function sites()
public function origins()
{
return $this->localizations()->map->locale()->values()->toBase();
$sites = empty($this->sites)
? [Site::default()->handle() => ['origin' => null]]
: $this->sites;

return collect($sites)->map(fn ($value, $key) => $value['origin'] ?? null);
}

public function in($locale)
{
return $this->localizations()->get($locale);
if (! $this->sites()->contains($locale)) {
return null;
}

if (! $variables = $this->localizations()->get($locale)) {
$variables = $this->makeLocalization($locale);
}

return $variables;
}

public function inSelectedSite()
Expand DownExpand Up@@ -239,6 +258,11 @@ public function editUrl()
return cp_route('globals.edit', $this->handle());
}

public function updateUrl()
{
return cp_route('globals.update', $this->handle());
}

public function deleteUrl()
{
return cp_route('globals.destroy', $this->handle());
Expand Down
17 changes: 12 additions & 5 deletions src/Globals/Variables.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -77,7 +77,7 @@ public function path()
{
return vsprintf('%s/%s%s.%s', [
rtrim(Stache::store('global-variables')->directory(), '/'),
Site::multiEnabled() ? $this->locale().'/' : '',
$this->locale().'/',
$this->handle(),
'yaml',
]);
Expand DownExpand Up@@ -152,6 +152,8 @@ public function save()
GlobalVariablesSaved::dispatch($this);
}

Blink::forget('global-set-localizations-'.$this->globalSet()->id());

return $this;
}

Expand DownExpand Up@@ -227,13 +229,18 @@ protected function fallbackBlueprint()

public function fileData()
{
$data = $this->data()->all();
return $this->data()->all();
}

if ($this->hasOrigin()) {
$data['origin'] = $this->origin()->locale();
public function origin($origin = null)
{
if (func_num_args() === 0) {
return $this->getOriginByString(
$this->globalSet()->origins()->get($this->locale())
);
}

return $data;
throw new \Exception('The origin cannot be set directly. It must be defined on the global set.');
}

protected function shouldRemoveNullsFromFileData()
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,7 @@ public function edit(Request $request, $id)
}

if (! $variables = $set->in($site)) {
return abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $variables);
Expand DownExpand Up@@ -87,7 +87,7 @@ public function update(Request $request, $handle)
}

if (! $set = $set->in($site)) {
abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $set);
Expand All@@ -104,7 +104,7 @@ public function update(Request $request, $handle)

$set->data($values);

$save = $set->globalSet()->addLocalization($set)->save();
$save = $set->save();

return response()->json([
'saved' => is_bool($save) ? $save : true,
Expand Down
26 changes: 8 additions & 18 deletions src/Http/Controllers/CP/Globals/GlobalsController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,6 @@
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Rules\Handle;
use Statamic\Support\Arr;
use Statamic\Support\Str;

class GlobalsController extends CpController
Expand DownExpand Up@@ -58,8 +57,8 @@ public function edit($set)
return [
'name' => $site->name(),
'handle' => $site->handle(),
'enabled' => $enabled = $set->existsIn($site->handle()),
'origin' => $enabled ? optional($set->in($site->handle())->origin())->locale() : null,
'enabled' => $set->sites()->contains($site->handle()),
'origin' => $set->origins()->get($site->handle()),
];
})->values(),
];
Expand DownExpand Up@@ -112,19 +111,11 @@ public function update(Request $request, $set)
->blueprint($values['blueprint']);

if (Site::multiEnabled()) {
$sites = collect(Arr::get($values, 'sites'));
$sites = collect($values['sites'])
->filter(fn ($site) => $site['enabled'])
->mapWithKeys(fn ($site) => [$site['handle'] => $site['origin']]);

foreach ($sites->filter->enabled as $site) {
$vars = $set->in($site['handle']) ?? $set->makeLocalization($site['handle']);
$vars->origin($site['origin']);
$set->addLocalization($vars);
}

foreach ($sites->reject->enabled as $site) {
if ($set->existsIn($site['handle'])) {
$set->removeLocalization($set->in($site['handle']));
}
}
$set->sites($sites);
}

$set->save();
Expand DownExpand Up@@ -161,11 +152,10 @@ public function store(Request $request)
}

$global = GlobalSet::make($handle)->title($data['title']);

$global->addLocalization($global->makeLocalization(Site::default()->handle()));

$global->save();

$global->in(Site::default()->handle())->save();

session()->flash('message', __('Global Set created'));

return ['redirect' => $global->editUrl()];
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [6.x] Separate globals config and content by duncanmcclean · Pull Request #11585 · statamic/cms · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
29711b5
Single sites should save variables in folder-based structure.
duncanmcclean Mar 17, 2025
5699fed
Add store/update methods to `GlobalSetPolicy`
duncanmcclean Mar 17, 2025
5327299
Fix v-model on global sites fieldtype
duncanmcclean Mar 17, 2025
7decfa8
Remove tests covering the previous single-site format
duncanmcclean Mar 17, 2025
fed701d
Fix typo
duncanmcclean Mar 18, 2025
3939f0e
Move "sites" config into global set
duncanmcclean Mar 18, 2025
910d8f0
Pint
duncanmcclean Mar 18, 2025
825df0b
Ensure localizations are synced correctly when saving global sets.
duncanmcclean Mar 18, 2025
8f32ffd
Only save the variables, not the entire global set.
duncanmcclean Mar 18, 2025
4fb1300
Add test to ensure edit form is shown even when variables don't exist.
duncanmcclean Mar 18, 2025
5ea65c1
Fix failing test
duncanmcclean Mar 18, 2025
05ed524
Test the localization syncing process here.
duncanmcclean Mar 18, 2025
019438e
Pint
duncanmcclean Mar 18, 2025
ef27407
wip
duncanmcclean Mar 18, 2025
71f7583
Split tests between single site / multisite again.
duncanmcclean Mar 18, 2025
ad737ac
Update `multisite` command
duncanmcclean Mar 18, 2025
cedf5c0
Global and global variables stores don't need the same directory anym…
duncanmcclean Mar 18, 2025
0d41ffa
wip
duncanmcclean Mar 18, 2025
6982958
Pint
duncanmcclean Mar 18, 2025
d11a439
Not all tests require multisite.
duncanmcclean Mar 19, 2025
accdd47
Update script
duncanmcclean Mar 19, 2025
33f6e45
Refactor how the `GlobalFactory` handles creating global variables
duncanmcclean Mar 19, 2025
a72607a
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 19, 2025
162d819
Write global variables after saving
duncanmcclean Mar 20, 2025
581891c
we probably don't need this anymore
duncanmcclean Mar 20, 2025
203e636
Drop `addLocalization` and `removeLocalization` methods
duncanmcclean Mar 20, 2025
bec1e2b
Global Variable events should trigger content listeners
duncanmcclean Mar 20, 2025
91fb98f
Global Variable events should trigger Git commits
duncanmcclean Mar 20, 2025
27333b0
Invalidate static cache based on global variable events
duncanmcclean Mar 20, 2025
69de722
wip
duncanmcclean Mar 20, 2025
5018ae8
Pint
duncanmcclean Mar 20, 2025
a9c8454
wip
duncanmcclean Mar 20, 2025
7192573
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 21, 2025
c07f6cf
Re-enable updating stache indexes after clearing cache in multisite c…
duncanmcclean Mar 21, 2025
5d89e4a
Enable/disable stache indexes just for the global set stuff
duncanmcclean Mar 21, 2025
9a221b6
Merge branch 'master' into separate-globals
jasonvarga Mar 21, 2025
68c70a8
plural
jasonvarga Mar 21, 2025
9470766
Same the way that it was
jasonvarga Mar 21, 2025
5bc45ae
Since GlobalFactory::make() no longer makes the variables, being able…
jasonvarga Mar 24, 2025
e428128
For consistency, avoid using GlobalSet::makeLocalization from outside
jasonvarga Mar 24, 2025
4970d5c
Remove globals factory
jasonvarga Mar 24, 2025
024426c
variable content is irrelevant in these tests and syntax these syntax…
jasonvarga Mar 24, 2025
c0916b6
rework sites method
jasonvarga Mar 24, 2025
a7efe4e
revert these. we still want the 404s. add tests.
jasonvarga Mar 24, 2025
d464409
didnt really need to change
jasonvarga Mar 24, 2025
8ccdac9
not anymore
jasonvarga Mar 24, 2025
54bb8b7
404 was fine but aborting results in a blank screen. might as well fi…
jasonvarga Mar 24, 2025
953a00c
multisite conversion can be simplified now ...
jasonvarga Mar 24, 2025
e0b7045
nitpick
jasonvarga Mar 24, 2025
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
2 changes: 1 addition & 1 deletion resources/js/components/globals/Sites.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<td class="text-sm">
<v-select
:options="siteOriginOptions(site)"
:value="site.origin"
:model-value="site.origin"
:searchable="false"
:reduce="(opt) => opt.value"
@update:model-value="site.origin = $event"
Expand Down
43 changes: 21 additions & 22 deletions src/Console/Commands/Multisite.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,12 +11,10 @@
use Statamic\Facades\Collection;
use Statamic\Facades\Config;
use Statamic\Facades\File;
use Statamic\Facades\GlobalSet;
use Statamic\Facades\Nav;
use Statamic\Facades\Role;
use Statamic\Facades\Site;
use Statamic\Facades\Stache;
use Statamic\Facades\YAML;
use Statamic\Rules\Handle;
use Statamic\Statamic;
use Statamic\Support\Traits\Hookable;
Expand DownExpand Up@@ -103,9 +101,9 @@ private function collectionsHaveBeenMoved(string $siteHandle): bool

private function globalsHaveBeenMoved(string $siteHandle): bool
{
$directory = Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle;

return File::isDirectory($directory);
return File::exists(
Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle
);
}

private function navsHaveBeenMoved(string $siteHandle): bool
Expand DownExpand Up@@ -264,29 +262,30 @@ private function moveCollectionTrees($collection): void

private function convertGlobalSets(): self
{
Config::set('statamic.system.multisite', true);
if ($this->siteHandle === 'default') {
// If it's default, the variables are already in the right spot.
return $this;
}

GlobalSet::all()->each(function ($set) {
$this->components->task(
description: "Updating global [{$set->handle()}]...",
task: function () use ($set) {
$this->moveGlobalSet($set);
}
);
});
$directory = Stache::store('globals')->directory();
$originalDirectory = $directory.DIRECTORY_SEPARATOR.'default';
$newDirectory = $directory.DIRECTORY_SEPARATOR.$this->siteHandle;

return $this;
}
File::makeDirectory($newDirectory);

private function moveGlobalSet($set): void
{
$yaml = YAML::file($set->path())->parse();
collect(File::getFiles($originalDirectory))
->each(function ($path) use ($originalDirectory, $newDirectory) {
$basename = pathinfo($path, PATHINFO_BASENAME);

$data = $yaml['data'] ?? [];
File::move(
$originalDirectory.DIRECTORY_SEPARATOR.$basename,
$newDirectory.DIRECTORY_SEPARATOR.$basename
);
});

$set->addLocalization($set->makeLocalization($this->siteHandle)->data($data));
File::delete($originalDirectory);

$set->save();
return $this;
}

private function convertNavs(): self
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Concerns/ListensForContentEvents.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,8 @@ trait ListensForContentEvents
\Statamic\Events\FormSaved::class,
\Statamic\Events\GlobalSetDeleted::class,
\Statamic\Events\GlobalSetSaved::class,
\Statamic\Events\GlobalVariablesDeleted::class,
\Statamic\Events\GlobalVariablesSaved::class,
\Statamic\Events\NavDeleted::class,
\Statamic\Events\NavSaved::class,
\Statamic\Events\NavTreeSaved::class,
Expand Down
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesDeleted.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesDeleted extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesDeleted extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables deleted', [], config('statamic.git.locale'));
}
}
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesSaved.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesSaved extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesSaved extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables saved', [], config('statamic.git.locale'));
}
}
74 changes: 49 additions & 25 deletions src/Globals/GlobalSet.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@ class GlobalSet implements Contract
protected $handle;
protected $afterSaveCallbacks = [];
protected $withEvents = true;
private $sites = [];

public function id()
{
Expand DownExpand Up@@ -118,12 +119,14 @@ public function save()

protected function saveOrDeleteLocalizations()
{
$localizations = $this->localizations();
$localizations = $this->freshLocalizations();

$localizations->each->save();
$this->sites()
->reject(fn ($site) => $localizations->has($site))
->each(fn ($site) => $this->makeLocalization($site)->save());

$this->freshLocalizations()
->diffKeys($localizations)
$localizations
->filter(fn ($localization) => ! $this->sites()->contains($localization->locale()))
->each->delete();
}

Expand DownExpand Up@@ -156,17 +159,10 @@ public function delete()

public function fileData()
{
$data = [
return Arr::removeNullValues([
'title' => $this->title(),
];

if (! Site::multiEnabled() && ($variables = $this->in(Site::default()->handle()))) {
$data['data'] = Arr::removeNullValues(
$variables->data()->all()
);
}

return $data;
'sites' => Site::multiEnabled() ? $this->origins()->all() : null,
]);
}

public function makeLocalization($site)
Expand All@@ -176,30 +172,53 @@ public function makeLocalization($site)
->locale($site);
}

public function addLocalization($localization)
public function sites($sites = null)
{
$localization->globalSet($this);
if (func_num_args() === 0) {
$sites = collect($this->sites);

$this->localizations()[$localization->locale()] = $localization;
if ($sites->isEmpty()) {
return collect([Site::default()->handle()]);
}

return $this;
}
return collect($this->sites)->keys();
}

public function removeLocalization($localization)
{
$this->localizations()->forget($localization->locale());
$this->sites = collect($sites)->mapWithKeys(function ($value, $key) {
if (is_int($key)) {
return [$value => ['origin' => null]];
}

if (is_string($value) || is_null($value)) {
return [$key => ['origin' => $value]];
}

return [$key => $value];
})->all();

return $this;
}

public function sites()
public function origins()
{
return $this->localizations()->map->locale()->values()->toBase();
$sites = empty($this->sites)
? [Site::default()->handle() => ['origin' => null]]
: $this->sites;

return collect($sites)->map(fn ($value, $key) => $value['origin'] ?? null);
}

public function in($locale)
{
return $this->localizations()->get($locale);
if (! $this->sites()->contains($locale)) {
return null;
}

if (! $variables = $this->localizations()->get($locale)) {
$variables = $this->makeLocalization($locale);
}

return $variables;
}

public function inSelectedSite()
Expand DownExpand Up@@ -239,6 +258,11 @@ public function editUrl()
return cp_route('globals.edit', $this->handle());
}

public function updateUrl()
{
return cp_route('globals.update', $this->handle());
}

public function deleteUrl()
{
return cp_route('globals.destroy', $this->handle());
Expand Down
17 changes: 12 additions & 5 deletions src/Globals/Variables.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -77,7 +77,7 @@ public function path()
{
return vsprintf('%s/%s%s.%s', [
rtrim(Stache::store('global-variables')->directory(), '/'),
Site::multiEnabled() ? $this->locale().'/' : '',
$this->locale().'/',
$this->handle(),
'yaml',
]);
Expand DownExpand Up@@ -152,6 +152,8 @@ public function save()
GlobalVariablesSaved::dispatch($this);
}

Blink::forget('global-set-localizations-'.$this->globalSet()->id());

return $this;
}

Expand DownExpand Up@@ -227,13 +229,18 @@ protected function fallbackBlueprint()

public function fileData()
{
$data = $this->data()->all();
return $this->data()->all();
}

if ($this->hasOrigin()) {
$data['origin'] = $this->origin()->locale();
public function origin($origin = null)
{
if (func_num_args() === 0) {
return $this->getOriginByString(
$this->globalSet()->origins()->get($this->locale())
);
}

return $data;
throw new \Exception('The origin cannot be set directly. It must be defined on the global set.');
}

protected function shouldRemoveNullsFromFileData()
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,7 @@ public function edit(Request $request, $id)
}

if (! $variables = $set->in($site)) {
return abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $variables);
Expand DownExpand Up@@ -87,7 +87,7 @@ public function update(Request $request, $handle)
}

if (! $set = $set->in($site)) {
abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $set);
Expand All@@ -104,7 +104,7 @@ public function update(Request $request, $handle)

$set->data($values);

$save = $set->globalSet()->addLocalization($set)->save();
$save = $set->save();

return response()->json([
'saved' => is_bool($save) ? $save : true,
Expand Down
26 changes: 8 additions & 18 deletions src/Http/Controllers/CP/Globals/GlobalsController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,6 @@
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Rules\Handle;
use Statamic\Support\Arr;
use Statamic\Support\Str;

class GlobalsController extends CpController
Expand DownExpand Up@@ -58,8 +57,8 @@ public function edit($set)
return [
'name' => $site->name(),
'handle' => $site->handle(),
'enabled' => $enabled = $set->existsIn($site->handle()),
'origin' => $enabled ? optional($set->in($site->handle())->origin())->locale() : null,
'enabled' => $set->sites()->contains($site->handle()),
'origin' => $set->origins()->get($site->handle()),
];
})->values(),
];
Expand DownExpand Up@@ -112,19 +111,11 @@ public function update(Request $request, $set)
->blueprint($values['blueprint']);

if (Site::multiEnabled()) {
$sites = collect(Arr::get($values, 'sites'));
$sites = collect($values['sites'])
->filter(fn ($site) => $site['enabled'])
->mapWithKeys(fn ($site) => [$site['handle'] => $site['origin']]);

foreach ($sites->filter->enabled as $site) {
$vars = $set->in($site['handle']) ?? $set->makeLocalization($site['handle']);
$vars->origin($site['origin']);
$set->addLocalization($vars);
}

foreach ($sites->reject->enabled as $site) {
if ($set->existsIn($site['handle'])) {
$set->removeLocalization($set->in($site['handle']));
}
}
$set->sites($sites);
}

$set->save();
Expand DownExpand Up@@ -161,11 +152,10 @@ public function store(Request $request)
}

$global = GlobalSet::make($handle)->title($data['title']);

$global->addLocalization($global->makeLocalization(Site::default()->handle()));

$global->save();

$global->in(Site::default()->handle())->save();

session()->flash('message', __('Global Set created'));

return ['redirect' => $global->editUrl()];
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [6.x] Separate globals config and content by duncanmcclean · Pull Request #11585 · statamic/cms · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
29711b5
Single sites should save variables in folder-based structure.
duncanmcclean Mar 17, 2025
5699fed
Add store/update methods to `GlobalSetPolicy`
duncanmcclean Mar 17, 2025
5327299
Fix v-model on global sites fieldtype
duncanmcclean Mar 17, 2025
7decfa8
Remove tests covering the previous single-site format
duncanmcclean Mar 17, 2025
fed701d
Fix typo
duncanmcclean Mar 18, 2025
3939f0e
Move "sites" config into global set
duncanmcclean Mar 18, 2025
910d8f0
Pint
duncanmcclean Mar 18, 2025
825df0b
Ensure localizations are synced correctly when saving global sets.
duncanmcclean Mar 18, 2025
8f32ffd
Only save the variables, not the entire global set.
duncanmcclean Mar 18, 2025
4fb1300
Add test to ensure edit form is shown even when variables don't exist.
duncanmcclean Mar 18, 2025
5ea65c1
Fix failing test
duncanmcclean Mar 18, 2025
05ed524
Test the localization syncing process here.
duncanmcclean Mar 18, 2025
019438e
Pint
duncanmcclean Mar 18, 2025
ef27407
wip
duncanmcclean Mar 18, 2025
71f7583
Split tests between single site / multisite again.
duncanmcclean Mar 18, 2025
ad737ac
Update `multisite` command
duncanmcclean Mar 18, 2025
cedf5c0
Global and global variables stores don't need the same directory anym…
duncanmcclean Mar 18, 2025
0d41ffa
wip
duncanmcclean Mar 18, 2025
6982958
Pint
duncanmcclean Mar 18, 2025
d11a439
Not all tests require multisite.
duncanmcclean Mar 19, 2025
accdd47
Update script
duncanmcclean Mar 19, 2025
33f6e45
Refactor how the `GlobalFactory` handles creating global variables
duncanmcclean Mar 19, 2025
a72607a
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 19, 2025
162d819
Write global variables after saving
duncanmcclean Mar 20, 2025
581891c
we probably don't need this anymore
duncanmcclean Mar 20, 2025
203e636
Drop `addLocalization` and `removeLocalization` methods
duncanmcclean Mar 20, 2025
bec1e2b
Global Variable events should trigger content listeners
duncanmcclean Mar 20, 2025
91fb98f
Global Variable events should trigger Git commits
duncanmcclean Mar 20, 2025
27333b0
Invalidate static cache based on global variable events
duncanmcclean Mar 20, 2025
69de722
wip
duncanmcclean Mar 20, 2025
5018ae8
Pint
duncanmcclean Mar 20, 2025
a9c8454
wip
duncanmcclean Mar 20, 2025
7192573
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 21, 2025
c07f6cf
Re-enable updating stache indexes after clearing cache in multisite c…
duncanmcclean Mar 21, 2025
5d89e4a
Enable/disable stache indexes just for the global set stuff
duncanmcclean Mar 21, 2025
9a221b6
Merge branch 'master' into separate-globals
jasonvarga Mar 21, 2025
68c70a8
plural
jasonvarga Mar 21, 2025
9470766
Same the way that it was
jasonvarga Mar 21, 2025
5bc45ae
Since GlobalFactory::make() no longer makes the variables, being able…
jasonvarga Mar 24, 2025
e428128
For consistency, avoid using GlobalSet::makeLocalization from outside
jasonvarga Mar 24, 2025
4970d5c
Remove globals factory
jasonvarga Mar 24, 2025
024426c
variable content is irrelevant in these tests and syntax these syntax…
jasonvarga Mar 24, 2025
c0916b6
rework sites method
jasonvarga Mar 24, 2025
a7efe4e
revert these. we still want the 404s. add tests.
jasonvarga Mar 24, 2025
d464409
didnt really need to change
jasonvarga Mar 24, 2025
8ccdac9
not anymore
jasonvarga Mar 24, 2025
54bb8b7
404 was fine but aborting results in a blank screen. might as well fi…
jasonvarga Mar 24, 2025
953a00c
multisite conversion can be simplified now ...
jasonvarga Mar 24, 2025
e0b7045
nitpick
jasonvarga Mar 24, 2025
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
2 changes: 1 addition & 1 deletion resources/js/components/globals/Sites.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<td class="text-sm">
<v-select
:options="siteOriginOptions(site)"
:value="site.origin"
:model-value="site.origin"
:searchable="false"
:reduce="(opt) => opt.value"
@update:model-value="site.origin = $event"
Expand Down
43 changes: 21 additions & 22 deletions src/Console/Commands/Multisite.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,12 +11,10 @@
use Statamic\Facades\Collection;
use Statamic\Facades\Config;
use Statamic\Facades\File;
use Statamic\Facades\GlobalSet;
use Statamic\Facades\Nav;
use Statamic\Facades\Role;
use Statamic\Facades\Site;
use Statamic\Facades\Stache;
use Statamic\Facades\YAML;
use Statamic\Rules\Handle;
use Statamic\Statamic;
use Statamic\Support\Traits\Hookable;
Expand DownExpand Up@@ -103,9 +101,9 @@ private function collectionsHaveBeenMoved(string $siteHandle): bool

private function globalsHaveBeenMoved(string $siteHandle): bool
{
$directory = Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle;

return File::isDirectory($directory);
return File::exists(
Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle
);
}

private function navsHaveBeenMoved(string $siteHandle): bool
Expand DownExpand Up@@ -264,29 +262,30 @@ private function moveCollectionTrees($collection): void

private function convertGlobalSets(): self
{
Config::set('statamic.system.multisite', true);
if ($this->siteHandle === 'default') {
// If it's default, the variables are already in the right spot.
return $this;
}

GlobalSet::all()->each(function ($set) {
$this->components->task(
description: "Updating global [{$set->handle()}]...",
task: function () use ($set) {
$this->moveGlobalSet($set);
}
);
});
$directory = Stache::store('globals')->directory();
$originalDirectory = $directory.DIRECTORY_SEPARATOR.'default';
$newDirectory = $directory.DIRECTORY_SEPARATOR.$this->siteHandle;

return $this;
}
File::makeDirectory($newDirectory);

private function moveGlobalSet($set): void
{
$yaml = YAML::file($set->path())->parse();
collect(File::getFiles($originalDirectory))
->each(function ($path) use ($originalDirectory, $newDirectory) {
$basename = pathinfo($path, PATHINFO_BASENAME);

$data = $yaml['data'] ?? [];
File::move(
$originalDirectory.DIRECTORY_SEPARATOR.$basename,
$newDirectory.DIRECTORY_SEPARATOR.$basename
);
});

$set->addLocalization($set->makeLocalization($this->siteHandle)->data($data));
File::delete($originalDirectory);

$set->save();
return $this;
}

private function convertNavs(): self
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Concerns/ListensForContentEvents.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,8 @@ trait ListensForContentEvents
\Statamic\Events\FormSaved::class,
\Statamic\Events\GlobalSetDeleted::class,
\Statamic\Events\GlobalSetSaved::class,
\Statamic\Events\GlobalVariablesDeleted::class,
\Statamic\Events\GlobalVariablesSaved::class,
\Statamic\Events\NavDeleted::class,
\Statamic\Events\NavSaved::class,
\Statamic\Events\NavTreeSaved::class,
Expand Down
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesDeleted.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesDeleted extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesDeleted extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables deleted', [], config('statamic.git.locale'));
}
}
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesSaved.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesSaved extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesSaved extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables saved', [], config('statamic.git.locale'));
}
}
74 changes: 49 additions & 25 deletions src/Globals/GlobalSet.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@ class GlobalSet implements Contract
protected $handle;
protected $afterSaveCallbacks = [];
protected $withEvents = true;
private $sites = [];

public function id()
{
Expand DownExpand Up@@ -118,12 +119,14 @@ public function save()

protected function saveOrDeleteLocalizations()
{
$localizations = $this->localizations();
$localizations = $this->freshLocalizations();

$localizations->each->save();
$this->sites()
->reject(fn ($site) => $localizations->has($site))
->each(fn ($site) => $this->makeLocalization($site)->save());

$this->freshLocalizations()
->diffKeys($localizations)
$localizations
->filter(fn ($localization) => ! $this->sites()->contains($localization->locale()))
->each->delete();
}

Expand DownExpand Up@@ -156,17 +159,10 @@ public function delete()

public function fileData()
{
$data = [
return Arr::removeNullValues([
'title' => $this->title(),
];

if (! Site::multiEnabled() && ($variables = $this->in(Site::default()->handle()))) {
$data['data'] = Arr::removeNullValues(
$variables->data()->all()
);
}

return $data;
'sites' => Site::multiEnabled() ? $this->origins()->all() : null,
]);
}

public function makeLocalization($site)
Expand All@@ -176,30 +172,53 @@ public function makeLocalization($site)
->locale($site);
}

public function addLocalization($localization)
public function sites($sites = null)
{
$localization->globalSet($this);
if (func_num_args() === 0) {
$sites = collect($this->sites);

$this->localizations()[$localization->locale()] = $localization;
if ($sites->isEmpty()) {
return collect([Site::default()->handle()]);
}

return $this;
}
return collect($this->sites)->keys();
}

public function removeLocalization($localization)
{
$this->localizations()->forget($localization->locale());
$this->sites = collect($sites)->mapWithKeys(function ($value, $key) {
if (is_int($key)) {
return [$value => ['origin' => null]];
}

if (is_string($value) || is_null($value)) {
return [$key => ['origin' => $value]];
}

return [$key => $value];
})->all();

return $this;
}

public function sites()
public function origins()
{
return $this->localizations()->map->locale()->values()->toBase();
$sites = empty($this->sites)
? [Site::default()->handle() => ['origin' => null]]
: $this->sites;

return collect($sites)->map(fn ($value, $key) => $value['origin'] ?? null);
}

public function in($locale)
{
return $this->localizations()->get($locale);
if (! $this->sites()->contains($locale)) {
return null;
}

if (! $variables = $this->localizations()->get($locale)) {
$variables = $this->makeLocalization($locale);
}

return $variables;
}

public function inSelectedSite()
Expand DownExpand Up@@ -239,6 +258,11 @@ public function editUrl()
return cp_route('globals.edit', $this->handle());
}

public function updateUrl()
{
return cp_route('globals.update', $this->handle());
}

public function deleteUrl()
{
return cp_route('globals.destroy', $this->handle());
Expand Down
17 changes: 12 additions & 5 deletions src/Globals/Variables.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -77,7 +77,7 @@ public function path()
{
return vsprintf('%s/%s%s.%s', [
rtrim(Stache::store('global-variables')->directory(), '/'),
Site::multiEnabled() ? $this->locale().'/' : '',
$this->locale().'/',
$this->handle(),
'yaml',
]);
Expand DownExpand Up@@ -152,6 +152,8 @@ public function save()
GlobalVariablesSaved::dispatch($this);
}

Blink::forget('global-set-localizations-'.$this->globalSet()->id());

return $this;
}

Expand DownExpand Up@@ -227,13 +229,18 @@ protected function fallbackBlueprint()

public function fileData()
{
$data = $this->data()->all();
return $this->data()->all();
}

if ($this->hasOrigin()) {
$data['origin'] = $this->origin()->locale();
public function origin($origin = null)
{
if (func_num_args() === 0) {
return $this->getOriginByString(
$this->globalSet()->origins()->get($this->locale())
);
}

return $data;
throw new \Exception('The origin cannot be set directly. It must be defined on the global set.');
}

protected function shouldRemoveNullsFromFileData()
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,7 @@ public function edit(Request $request, $id)
}

if (! $variables = $set->in($site)) {
return abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $variables);
Expand DownExpand Up@@ -87,7 +87,7 @@ public function update(Request $request, $handle)
}

if (! $set = $set->in($site)) {
abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $set);
Expand All@@ -104,7 +104,7 @@ public function update(Request $request, $handle)

$set->data($values);

$save = $set->globalSet()->addLocalization($set)->save();
$save = $set->save();

return response()->json([
'saved' => is_bool($save) ? $save : true,
Expand Down
26 changes: 8 additions & 18 deletions src/Http/Controllers/CP/Globals/GlobalsController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,6 @@
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Rules\Handle;
use Statamic\Support\Arr;
use Statamic\Support\Str;

class GlobalsController extends CpController
Expand DownExpand Up@@ -58,8 +57,8 @@ public function edit($set)
return [
'name' => $site->name(),
'handle' => $site->handle(),
'enabled' => $enabled = $set->existsIn($site->handle()),
'origin' => $enabled ? optional($set->in($site->handle())->origin())->locale() : null,
'enabled' => $set->sites()->contains($site->handle()),
'origin' => $set->origins()->get($site->handle()),
];
})->values(),
];
Expand DownExpand Up@@ -112,19 +111,11 @@ public function update(Request $request, $set)
->blueprint($values['blueprint']);

if (Site::multiEnabled()) {
$sites = collect(Arr::get($values, 'sites'));
$sites = collect($values['sites'])
->filter(fn ($site) => $site['enabled'])
->mapWithKeys(fn ($site) => [$site['handle'] => $site['origin']]);

foreach ($sites->filter->enabled as $site) {
$vars = $set->in($site['handle']) ?? $set->makeLocalization($site['handle']);
$vars->origin($site['origin']);
$set->addLocalization($vars);
}

foreach ($sites->reject->enabled as $site) {
if ($set->existsIn($site['handle'])) {
$set->removeLocalization($set->in($site['handle']));
}
}
$set->sites($sites);
}

$set->save();
Expand DownExpand Up@@ -161,11 +152,10 @@ public function store(Request $request)
}

$global = GlobalSet::make($handle)->title($data['title']);

$global->addLocalization($global->makeLocalization(Site::default()->handle()));

$global->save();

$global->in(Site::default()->handle())->save();

session()->flash('message', __('Global Set created'));

return ['redirect' => $global->editUrl()];
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [6.x] Separate globals config and content by duncanmcclean · Pull Request #11585 · statamic/cms · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
29711b5
Single sites should save variables in folder-based structure.
duncanmcclean Mar 17, 2025
5699fed
Add store/update methods to `GlobalSetPolicy`
duncanmcclean Mar 17, 2025
5327299
Fix v-model on global sites fieldtype
duncanmcclean Mar 17, 2025
7decfa8
Remove tests covering the previous single-site format
duncanmcclean Mar 17, 2025
fed701d
Fix typo
duncanmcclean Mar 18, 2025
3939f0e
Move "sites" config into global set
duncanmcclean Mar 18, 2025
910d8f0
Pint
duncanmcclean Mar 18, 2025
825df0b
Ensure localizations are synced correctly when saving global sets.
duncanmcclean Mar 18, 2025
8f32ffd
Only save the variables, not the entire global set.
duncanmcclean Mar 18, 2025
4fb1300
Add test to ensure edit form is shown even when variables don't exist.
duncanmcclean Mar 18, 2025
5ea65c1
Fix failing test
duncanmcclean Mar 18, 2025
05ed524
Test the localization syncing process here.
duncanmcclean Mar 18, 2025
019438e
Pint
duncanmcclean Mar 18, 2025
ef27407
wip
duncanmcclean Mar 18, 2025
71f7583
Split tests between single site / multisite again.
duncanmcclean Mar 18, 2025
ad737ac
Update `multisite` command
duncanmcclean Mar 18, 2025
cedf5c0
Global and global variables stores don't need the same directory anym…
duncanmcclean Mar 18, 2025
0d41ffa
wip
duncanmcclean Mar 18, 2025
6982958
Pint
duncanmcclean Mar 18, 2025
d11a439
Not all tests require multisite.
duncanmcclean Mar 19, 2025
accdd47
Update script
duncanmcclean Mar 19, 2025
33f6e45
Refactor how the `GlobalFactory` handles creating global variables
duncanmcclean Mar 19, 2025
a72607a
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 19, 2025
162d819
Write global variables after saving
duncanmcclean Mar 20, 2025
581891c
we probably don't need this anymore
duncanmcclean Mar 20, 2025
203e636
Drop `addLocalization` and `removeLocalization` methods
duncanmcclean Mar 20, 2025
bec1e2b
Global Variable events should trigger content listeners
duncanmcclean Mar 20, 2025
91fb98f
Global Variable events should trigger Git commits
duncanmcclean Mar 20, 2025
27333b0
Invalidate static cache based on global variable events
duncanmcclean Mar 20, 2025
69de722
wip
duncanmcclean Mar 20, 2025
5018ae8
Pint
duncanmcclean Mar 20, 2025
a9c8454
wip
duncanmcclean Mar 20, 2025
7192573
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 21, 2025
c07f6cf
Re-enable updating stache indexes after clearing cache in multisite c…
duncanmcclean Mar 21, 2025
5d89e4a
Enable/disable stache indexes just for the global set stuff
duncanmcclean Mar 21, 2025
9a221b6
Merge branch 'master' into separate-globals
jasonvarga Mar 21, 2025
68c70a8
plural
jasonvarga Mar 21, 2025
9470766
Same the way that it was
jasonvarga Mar 21, 2025
5bc45ae
Since GlobalFactory::make() no longer makes the variables, being able…
jasonvarga Mar 24, 2025
e428128
For consistency, avoid using GlobalSet::makeLocalization from outside
jasonvarga Mar 24, 2025
4970d5c
Remove globals factory
jasonvarga Mar 24, 2025
024426c
variable content is irrelevant in these tests and syntax these syntax…
jasonvarga Mar 24, 2025
c0916b6
rework sites method
jasonvarga Mar 24, 2025
a7efe4e
revert these. we still want the 404s. add tests.
jasonvarga Mar 24, 2025
d464409
didnt really need to change
jasonvarga Mar 24, 2025
8ccdac9
not anymore
jasonvarga Mar 24, 2025
54bb8b7
404 was fine but aborting results in a blank screen. might as well fi…
jasonvarga Mar 24, 2025
953a00c
multisite conversion can be simplified now ...
jasonvarga Mar 24, 2025
e0b7045
nitpick
jasonvarga Mar 24, 2025
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
2 changes: 1 addition & 1 deletion resources/js/components/globals/Sites.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<td class="text-sm">
<v-select
:options="siteOriginOptions(site)"
:value="site.origin"
:model-value="site.origin"
:searchable="false"
:reduce="(opt) => opt.value"
@update:model-value="site.origin = $event"
Expand Down
43 changes: 21 additions & 22 deletions src/Console/Commands/Multisite.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,12 +11,10 @@
use Statamic\Facades\Collection;
use Statamic\Facades\Config;
use Statamic\Facades\File;
use Statamic\Facades\GlobalSet;
use Statamic\Facades\Nav;
use Statamic\Facades\Role;
use Statamic\Facades\Site;
use Statamic\Facades\Stache;
use Statamic\Facades\YAML;
use Statamic\Rules\Handle;
use Statamic\Statamic;
use Statamic\Support\Traits\Hookable;
Expand DownExpand Up@@ -103,9 +101,9 @@ private function collectionsHaveBeenMoved(string $siteHandle): bool

private function globalsHaveBeenMoved(string $siteHandle): bool
{
$directory = Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle;

return File::isDirectory($directory);
return File::exists(
Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle
);
}

private function navsHaveBeenMoved(string $siteHandle): bool
Expand DownExpand Up@@ -264,29 +262,30 @@ private function moveCollectionTrees($collection): void

private function convertGlobalSets(): self
{
Config::set('statamic.system.multisite', true);
if ($this->siteHandle === 'default') {
// If it's default, the variables are already in the right spot.
return $this;
}

GlobalSet::all()->each(function ($set) {
$this->components->task(
description: "Updating global [{$set->handle()}]...",
task: function () use ($set) {
$this->moveGlobalSet($set);
}
);
});
$directory = Stache::store('globals')->directory();
$originalDirectory = $directory.DIRECTORY_SEPARATOR.'default';
$newDirectory = $directory.DIRECTORY_SEPARATOR.$this->siteHandle;

return $this;
}
File::makeDirectory($newDirectory);

private function moveGlobalSet($set): void
{
$yaml = YAML::file($set->path())->parse();
collect(File::getFiles($originalDirectory))
->each(function ($path) use ($originalDirectory, $newDirectory) {
$basename = pathinfo($path, PATHINFO_BASENAME);

$data = $yaml['data'] ?? [];
File::move(
$originalDirectory.DIRECTORY_SEPARATOR.$basename,
$newDirectory.DIRECTORY_SEPARATOR.$basename
);
});

$set->addLocalization($set->makeLocalization($this->siteHandle)->data($data));
File::delete($originalDirectory);

$set->save();
return $this;
}

private function convertNavs(): self
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Concerns/ListensForContentEvents.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,8 @@ trait ListensForContentEvents
\Statamic\Events\FormSaved::class,
\Statamic\Events\GlobalSetDeleted::class,
\Statamic\Events\GlobalSetSaved::class,
\Statamic\Events\GlobalVariablesDeleted::class,
\Statamic\Events\GlobalVariablesSaved::class,
\Statamic\Events\NavDeleted::class,
\Statamic\Events\NavSaved::class,
\Statamic\Events\NavTreeSaved::class,
Expand Down
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesDeleted.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesDeleted extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesDeleted extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables deleted', [], config('statamic.git.locale'));
}
}
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesSaved.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesSaved extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesSaved extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables saved', [], config('statamic.git.locale'));
}
}
74 changes: 49 additions & 25 deletions src/Globals/GlobalSet.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@ class GlobalSet implements Contract
protected $handle;
protected $afterSaveCallbacks = [];
protected $withEvents = true;
private $sites = [];

public function id()
{
Expand DownExpand Up@@ -118,12 +119,14 @@ public function save()

protected function saveOrDeleteLocalizations()
{
$localizations = $this->localizations();
$localizations = $this->freshLocalizations();

$localizations->each->save();
$this->sites()
->reject(fn ($site) => $localizations->has($site))
->each(fn ($site) => $this->makeLocalization($site)->save());

$this->freshLocalizations()
->diffKeys($localizations)
$localizations
->filter(fn ($localization) => ! $this->sites()->contains($localization->locale()))
->each->delete();
}

Expand DownExpand Up@@ -156,17 +159,10 @@ public function delete()

public function fileData()
{
$data = [
return Arr::removeNullValues([
'title' => $this->title(),
];

if (! Site::multiEnabled() && ($variables = $this->in(Site::default()->handle()))) {
$data['data'] = Arr::removeNullValues(
$variables->data()->all()
);
}

return $data;
'sites' => Site::multiEnabled() ? $this->origins()->all() : null,
]);
}

public function makeLocalization($site)
Expand All@@ -176,30 +172,53 @@ public function makeLocalization($site)
->locale($site);
}

public function addLocalization($localization)
public function sites($sites = null)
{
$localization->globalSet($this);
if (func_num_args() === 0) {
$sites = collect($this->sites);

$this->localizations()[$localization->locale()] = $localization;
if ($sites->isEmpty()) {
return collect([Site::default()->handle()]);
}

return $this;
}
return collect($this->sites)->keys();
}

public function removeLocalization($localization)
{
$this->localizations()->forget($localization->locale());
$this->sites = collect($sites)->mapWithKeys(function ($value, $key) {
if (is_int($key)) {
return [$value => ['origin' => null]];
}

if (is_string($value) || is_null($value)) {
return [$key => ['origin' => $value]];
}

return [$key => $value];
})->all();

return $this;
}

public function sites()
public function origins()
{
return $this->localizations()->map->locale()->values()->toBase();
$sites = empty($this->sites)
? [Site::default()->handle() => ['origin' => null]]
: $this->sites;

return collect($sites)->map(fn ($value, $key) => $value['origin'] ?? null);
}

public function in($locale)
{
return $this->localizations()->get($locale);
if (! $this->sites()->contains($locale)) {
return null;
}

if (! $variables = $this->localizations()->get($locale)) {
$variables = $this->makeLocalization($locale);
}

return $variables;
}

public function inSelectedSite()
Expand DownExpand Up@@ -239,6 +258,11 @@ public function editUrl()
return cp_route('globals.edit', $this->handle());
}

public function updateUrl()
{
return cp_route('globals.update', $this->handle());
}

public function deleteUrl()
{
return cp_route('globals.destroy', $this->handle());
Expand Down
17 changes: 12 additions & 5 deletions src/Globals/Variables.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -77,7 +77,7 @@ public function path()
{
return vsprintf('%s/%s%s.%s', [
rtrim(Stache::store('global-variables')->directory(), '/'),
Site::multiEnabled() ? $this->locale().'/' : '',
$this->locale().'/',
$this->handle(),
'yaml',
]);
Expand DownExpand Up@@ -152,6 +152,8 @@ public function save()
GlobalVariablesSaved::dispatch($this);
}

Blink::forget('global-set-localizations-'.$this->globalSet()->id());

return $this;
}

Expand DownExpand Up@@ -227,13 +229,18 @@ protected function fallbackBlueprint()

public function fileData()
{
$data = $this->data()->all();
return $this->data()->all();
}

if ($this->hasOrigin()) {
$data['origin'] = $this->origin()->locale();
public function origin($origin = null)
{
if (func_num_args() === 0) {
return $this->getOriginByString(
$this->globalSet()->origins()->get($this->locale())
);
}

return $data;
throw new \Exception('The origin cannot be set directly. It must be defined on the global set.');
}

protected function shouldRemoveNullsFromFileData()
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,7 @@ public function edit(Request $request, $id)
}

if (! $variables = $set->in($site)) {
return abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $variables);
Expand DownExpand Up@@ -87,7 +87,7 @@ public function update(Request $request, $handle)
}

if (! $set = $set->in($site)) {
abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $set);
Expand All@@ -104,7 +104,7 @@ public function update(Request $request, $handle)

$set->data($values);

$save = $set->globalSet()->addLocalization($set)->save();
$save = $set->save();

return response()->json([
'saved' => is_bool($save) ? $save : true,
Expand Down
26 changes: 8 additions & 18 deletions src/Http/Controllers/CP/Globals/GlobalsController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,6 @@
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Rules\Handle;
use Statamic\Support\Arr;
use Statamic\Support\Str;

class GlobalsController extends CpController
Expand DownExpand Up@@ -58,8 +57,8 @@ public function edit($set)
return [
'name' => $site->name(),
'handle' => $site->handle(),
'enabled' => $enabled = $set->existsIn($site->handle()),
'origin' => $enabled ? optional($set->in($site->handle())->origin())->locale() : null,
'enabled' => $set->sites()->contains($site->handle()),
'origin' => $set->origins()->get($site->handle()),
];
})->values(),
];
Expand DownExpand Up@@ -112,19 +111,11 @@ public function update(Request $request, $set)
->blueprint($values['blueprint']);

if (Site::multiEnabled()) {
$sites = collect(Arr::get($values, 'sites'));
$sites = collect($values['sites'])
->filter(fn ($site) => $site['enabled'])
->mapWithKeys(fn ($site) => [$site['handle'] => $site['origin']]);

foreach ($sites->filter->enabled as $site) {
$vars = $set->in($site['handle']) ?? $set->makeLocalization($site['handle']);
$vars->origin($site['origin']);
$set->addLocalization($vars);
}

foreach ($sites->reject->enabled as $site) {
if ($set->existsIn($site['handle'])) {
$set->removeLocalization($set->in($site['handle']));
}
}
$set->sites($sites);
}

$set->save();
Expand DownExpand Up@@ -161,11 +152,10 @@ public function store(Request $request)
}

$global = GlobalSet::make($handle)->title($data['title']);

$global->addLocalization($global->makeLocalization(Site::default()->handle()));

$global->save();

$global->in(Site::default()->handle())->save();

session()->flash('message', __('Global Set created'));

return ['redirect' => $global->editUrl()];
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [6.x] Separate globals config and content by duncanmcclean · Pull Request #11585 · statamic/cms · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
29711b5
Single sites should save variables in folder-based structure.
duncanmcclean Mar 17, 2025
5699fed
Add store/update methods to `GlobalSetPolicy`
duncanmcclean Mar 17, 2025
5327299
Fix v-model on global sites fieldtype
duncanmcclean Mar 17, 2025
7decfa8
Remove tests covering the previous single-site format
duncanmcclean Mar 17, 2025
fed701d
Fix typo
duncanmcclean Mar 18, 2025
3939f0e
Move "sites" config into global set
duncanmcclean Mar 18, 2025
910d8f0
Pint
duncanmcclean Mar 18, 2025
825df0b
Ensure localizations are synced correctly when saving global sets.
duncanmcclean Mar 18, 2025
8f32ffd
Only save the variables, not the entire global set.
duncanmcclean Mar 18, 2025
4fb1300
Add test to ensure edit form is shown even when variables don't exist.
duncanmcclean Mar 18, 2025
5ea65c1
Fix failing test
duncanmcclean Mar 18, 2025
05ed524
Test the localization syncing process here.
duncanmcclean Mar 18, 2025
019438e
Pint
duncanmcclean Mar 18, 2025
ef27407
wip
duncanmcclean Mar 18, 2025
71f7583
Split tests between single site / multisite again.
duncanmcclean Mar 18, 2025
ad737ac
Update `multisite` command
duncanmcclean Mar 18, 2025
cedf5c0
Global and global variables stores don't need the same directory anym…
duncanmcclean Mar 18, 2025
0d41ffa
wip
duncanmcclean Mar 18, 2025
6982958
Pint
duncanmcclean Mar 18, 2025
d11a439
Not all tests require multisite.
duncanmcclean Mar 19, 2025
accdd47
Update script
duncanmcclean Mar 19, 2025
33f6e45
Refactor how the `GlobalFactory` handles creating global variables
duncanmcclean Mar 19, 2025
a72607a
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 19, 2025
162d819
Write global variables after saving
duncanmcclean Mar 20, 2025
581891c
we probably don't need this anymore
duncanmcclean Mar 20, 2025
203e636
Drop `addLocalization` and `removeLocalization` methods
duncanmcclean Mar 20, 2025
bec1e2b
Global Variable events should trigger content listeners
duncanmcclean Mar 20, 2025
91fb98f
Global Variable events should trigger Git commits
duncanmcclean Mar 20, 2025
27333b0
Invalidate static cache based on global variable events
duncanmcclean Mar 20, 2025
69de722
wip
duncanmcclean Mar 20, 2025
5018ae8
Pint
duncanmcclean Mar 20, 2025
a9c8454
wip
duncanmcclean Mar 20, 2025
7192573
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 21, 2025
c07f6cf
Re-enable updating stache indexes after clearing cache in multisite c…
duncanmcclean Mar 21, 2025
5d89e4a
Enable/disable stache indexes just for the global set stuff
duncanmcclean Mar 21, 2025
9a221b6
Merge branch 'master' into separate-globals
jasonvarga Mar 21, 2025
68c70a8
plural
jasonvarga Mar 21, 2025
9470766
Same the way that it was
jasonvarga Mar 21, 2025
5bc45ae
Since GlobalFactory::make() no longer makes the variables, being able…
jasonvarga Mar 24, 2025
e428128
For consistency, avoid using GlobalSet::makeLocalization from outside
jasonvarga Mar 24, 2025
4970d5c
Remove globals factory
jasonvarga Mar 24, 2025
024426c
variable content is irrelevant in these tests and syntax these syntax…
jasonvarga Mar 24, 2025
c0916b6
rework sites method
jasonvarga Mar 24, 2025
a7efe4e
revert these. we still want the 404s. add tests.
jasonvarga Mar 24, 2025
d464409
didnt really need to change
jasonvarga Mar 24, 2025
8ccdac9
not anymore
jasonvarga Mar 24, 2025
54bb8b7
404 was fine but aborting results in a blank screen. might as well fi…
jasonvarga Mar 24, 2025
953a00c
multisite conversion can be simplified now ...
jasonvarga Mar 24, 2025
e0b7045
nitpick
jasonvarga Mar 24, 2025
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
2 changes: 1 addition & 1 deletion resources/js/components/globals/Sites.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<td class="text-sm">
<v-select
:options="siteOriginOptions(site)"
:value="site.origin"
:model-value="site.origin"
:searchable="false"
:reduce="(opt) => opt.value"
@update:model-value="site.origin = $event"
Expand Down
43 changes: 21 additions & 22 deletions src/Console/Commands/Multisite.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,12 +11,10 @@
use Statamic\Facades\Collection;
use Statamic\Facades\Config;
use Statamic\Facades\File;
use Statamic\Facades\GlobalSet;
use Statamic\Facades\Nav;
use Statamic\Facades\Role;
use Statamic\Facades\Site;
use Statamic\Facades\Stache;
use Statamic\Facades\YAML;
use Statamic\Rules\Handle;
use Statamic\Statamic;
use Statamic\Support\Traits\Hookable;
Expand DownExpand Up@@ -103,9 +101,9 @@ private function collectionsHaveBeenMoved(string $siteHandle): bool

private function globalsHaveBeenMoved(string $siteHandle): bool
{
$directory = Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle;

return File::isDirectory($directory);
return File::exists(
Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle
);
}

private function navsHaveBeenMoved(string $siteHandle): bool
Expand DownExpand Up@@ -264,29 +262,30 @@ private function moveCollectionTrees($collection): void

private function convertGlobalSets(): self
{
Config::set('statamic.system.multisite', true);
if ($this->siteHandle === 'default') {
// If it's default, the variables are already in the right spot.
return $this;
}

GlobalSet::all()->each(function ($set) {
$this->components->task(
description: "Updating global [{$set->handle()}]...",
task: function () use ($set) {
$this->moveGlobalSet($set);
}
);
});
$directory = Stache::store('globals')->directory();
$originalDirectory = $directory.DIRECTORY_SEPARATOR.'default';
$newDirectory = $directory.DIRECTORY_SEPARATOR.$this->siteHandle;

return $this;
}
File::makeDirectory($newDirectory);

private function moveGlobalSet($set): void
{
$yaml = YAML::file($set->path())->parse();
collect(File::getFiles($originalDirectory))
->each(function ($path) use ($originalDirectory, $newDirectory) {
$basename = pathinfo($path, PATHINFO_BASENAME);

$data = $yaml['data'] ?? [];
File::move(
$originalDirectory.DIRECTORY_SEPARATOR.$basename,
$newDirectory.DIRECTORY_SEPARATOR.$basename
);
});

$set->addLocalization($set->makeLocalization($this->siteHandle)->data($data));
File::delete($originalDirectory);

$set->save();
return $this;
}

private function convertNavs(): self
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Concerns/ListensForContentEvents.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,8 @@ trait ListensForContentEvents
\Statamic\Events\FormSaved::class,
\Statamic\Events\GlobalSetDeleted::class,
\Statamic\Events\GlobalSetSaved::class,
\Statamic\Events\GlobalVariablesDeleted::class,
\Statamic\Events\GlobalVariablesSaved::class,
\Statamic\Events\NavDeleted::class,
\Statamic\Events\NavSaved::class,
\Statamic\Events\NavTreeSaved::class,
Expand Down
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesDeleted.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesDeleted extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesDeleted extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables deleted', [], config('statamic.git.locale'));
}
}
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesSaved.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesSaved extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesSaved extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables saved', [], config('statamic.git.locale'));
}
}
74 changes: 49 additions & 25 deletions src/Globals/GlobalSet.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@ class GlobalSet implements Contract
protected $handle;
protected $afterSaveCallbacks = [];
protected $withEvents = true;
private $sites = [];

public function id()
{
Expand DownExpand Up@@ -118,12 +119,14 @@ public function save()

protected function saveOrDeleteLocalizations()
{
$localizations = $this->localizations();
$localizations = $this->freshLocalizations();

$localizations->each->save();
$this->sites()
->reject(fn ($site) => $localizations->has($site))
->each(fn ($site) => $this->makeLocalization($site)->save());

$this->freshLocalizations()
->diffKeys($localizations)
$localizations
->filter(fn ($localization) => ! $this->sites()->contains($localization->locale()))
->each->delete();
}

Expand DownExpand Up@@ -156,17 +159,10 @@ public function delete()

public function fileData()
{
$data = [
return Arr::removeNullValues([
'title' => $this->title(),
];

if (! Site::multiEnabled() && ($variables = $this->in(Site::default()->handle()))) {
$data['data'] = Arr::removeNullValues(
$variables->data()->all()
);
}

return $data;
'sites' => Site::multiEnabled() ? $this->origins()->all() : null,
]);
}

public function makeLocalization($site)
Expand All@@ -176,30 +172,53 @@ public function makeLocalization($site)
->locale($site);
}

public function addLocalization($localization)
public function sites($sites = null)
{
$localization->globalSet($this);
if (func_num_args() === 0) {
$sites = collect($this->sites);

$this->localizations()[$localization->locale()] = $localization;
if ($sites->isEmpty()) {
return collect([Site::default()->handle()]);
}

return $this;
}
return collect($this->sites)->keys();
}

public function removeLocalization($localization)
{
$this->localizations()->forget($localization->locale());
$this->sites = collect($sites)->mapWithKeys(function ($value, $key) {
if (is_int($key)) {
return [$value => ['origin' => null]];
}

if (is_string($value) || is_null($value)) {
return [$key => ['origin' => $value]];
}

return [$key => $value];
})->all();

return $this;
}

public function sites()
public function origins()
{
return $this->localizations()->map->locale()->values()->toBase();
$sites = empty($this->sites)
? [Site::default()->handle() => ['origin' => null]]
: $this->sites;

return collect($sites)->map(fn ($value, $key) => $value['origin'] ?? null);
}

public function in($locale)
{
return $this->localizations()->get($locale);
if (! $this->sites()->contains($locale)) {
return null;
}

if (! $variables = $this->localizations()->get($locale)) {
$variables = $this->makeLocalization($locale);
}

return $variables;
}

public function inSelectedSite()
Expand DownExpand Up@@ -239,6 +258,11 @@ public function editUrl()
return cp_route('globals.edit', $this->handle());
}

public function updateUrl()
{
return cp_route('globals.update', $this->handle());
}

public function deleteUrl()
{
return cp_route('globals.destroy', $this->handle());
Expand Down
17 changes: 12 additions & 5 deletions src/Globals/Variables.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -77,7 +77,7 @@ public function path()
{
return vsprintf('%s/%s%s.%s', [
rtrim(Stache::store('global-variables')->directory(), '/'),
Site::multiEnabled() ? $this->locale().'/' : '',
$this->locale().'/',
$this->handle(),
'yaml',
]);
Expand DownExpand Up@@ -152,6 +152,8 @@ public function save()
GlobalVariablesSaved::dispatch($this);
}

Blink::forget('global-set-localizations-'.$this->globalSet()->id());

return $this;
}

Expand DownExpand Up@@ -227,13 +229,18 @@ protected function fallbackBlueprint()

public function fileData()
{
$data = $this->data()->all();
return $this->data()->all();
}

if ($this->hasOrigin()) {
$data['origin'] = $this->origin()->locale();
public function origin($origin = null)
{
if (func_num_args() === 0) {
return $this->getOriginByString(
$this->globalSet()->origins()->get($this->locale())
);
}

return $data;
throw new \Exception('The origin cannot be set directly. It must be defined on the global set.');
}

protected function shouldRemoveNullsFromFileData()
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,7 @@ public function edit(Request $request, $id)
}

if (! $variables = $set->in($site)) {
return abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $variables);
Expand DownExpand Up@@ -87,7 +87,7 @@ public function update(Request $request, $handle)
}

if (! $set = $set->in($site)) {
abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $set);
Expand All@@ -104,7 +104,7 @@ public function update(Request $request, $handle)

$set->data($values);

$save = $set->globalSet()->addLocalization($set)->save();
$save = $set->save();

return response()->json([
'saved' => is_bool($save) ? $save : true,
Expand Down
26 changes: 8 additions & 18 deletions src/Http/Controllers/CP/Globals/GlobalsController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,6 @@
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Rules\Handle;
use Statamic\Support\Arr;
use Statamic\Support\Str;

class GlobalsController extends CpController
Expand DownExpand Up@@ -58,8 +57,8 @@ public function edit($set)
return [
'name' => $site->name(),
'handle' => $site->handle(),
'enabled' => $enabled = $set->existsIn($site->handle()),
'origin' => $enabled ? optional($set->in($site->handle())->origin())->locale() : null,
'enabled' => $set->sites()->contains($site->handle()),
'origin' => $set->origins()->get($site->handle()),
];
})->values(),
];
Expand DownExpand Up@@ -112,19 +111,11 @@ public function update(Request $request, $set)
->blueprint($values['blueprint']);

if (Site::multiEnabled()) {
$sites = collect(Arr::get($values, 'sites'));
$sites = collect($values['sites'])
->filter(fn ($site) => $site['enabled'])
->mapWithKeys(fn ($site) => [$site['handle'] => $site['origin']]);

foreach ($sites->filter->enabled as $site) {
$vars = $set->in($site['handle']) ?? $set->makeLocalization($site['handle']);
$vars->origin($site['origin']);
$set->addLocalization($vars);
}

foreach ($sites->reject->enabled as $site) {
if ($set->existsIn($site['handle'])) {
$set->removeLocalization($set->in($site['handle']));
}
}
$set->sites($sites);
}

$set->save();
Expand DownExpand Up@@ -161,11 +152,10 @@ public function store(Request $request)
}

$global = GlobalSet::make($handle)->title($data['title']);

$global->addLocalization($global->makeLocalization(Site::default()->handle()));

$global->save();

$global->in(Site::default()->handle())->save();

session()->flash('message', __('Global Set created'));

return ['redirect' => $global->editUrl()];
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [6.x] Separate globals config and content by duncanmcclean · Pull Request #11585 · statamic/cms · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
29711b5
Single sites should save variables in folder-based structure.
duncanmcclean Mar 17, 2025
5699fed
Add store/update methods to `GlobalSetPolicy`
duncanmcclean Mar 17, 2025
5327299
Fix v-model on global sites fieldtype
duncanmcclean Mar 17, 2025
7decfa8
Remove tests covering the previous single-site format
duncanmcclean Mar 17, 2025
fed701d
Fix typo
duncanmcclean Mar 18, 2025
3939f0e
Move "sites" config into global set
duncanmcclean Mar 18, 2025
910d8f0
Pint
duncanmcclean Mar 18, 2025
825df0b
Ensure localizations are synced correctly when saving global sets.
duncanmcclean Mar 18, 2025
8f32ffd
Only save the variables, not the entire global set.
duncanmcclean Mar 18, 2025
4fb1300
Add test to ensure edit form is shown even when variables don't exist.
duncanmcclean Mar 18, 2025
5ea65c1
Fix failing test
duncanmcclean Mar 18, 2025
05ed524
Test the localization syncing process here.
duncanmcclean Mar 18, 2025
019438e
Pint
duncanmcclean Mar 18, 2025
ef27407
wip
duncanmcclean Mar 18, 2025
71f7583
Split tests between single site / multisite again.
duncanmcclean Mar 18, 2025
ad737ac
Update `multisite` command
duncanmcclean Mar 18, 2025
cedf5c0
Global and global variables stores don't need the same directory anym…
duncanmcclean Mar 18, 2025
0d41ffa
wip
duncanmcclean Mar 18, 2025
6982958
Pint
duncanmcclean Mar 18, 2025
d11a439
Not all tests require multisite.
duncanmcclean Mar 19, 2025
accdd47
Update script
duncanmcclean Mar 19, 2025
33f6e45
Refactor how the `GlobalFactory` handles creating global variables
duncanmcclean Mar 19, 2025
a72607a
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 19, 2025
162d819
Write global variables after saving
duncanmcclean Mar 20, 2025
581891c
we probably don't need this anymore
duncanmcclean Mar 20, 2025
203e636
Drop `addLocalization` and `removeLocalization` methods
duncanmcclean Mar 20, 2025
bec1e2b
Global Variable events should trigger content listeners
duncanmcclean Mar 20, 2025
91fb98f
Global Variable events should trigger Git commits
duncanmcclean Mar 20, 2025
27333b0
Invalidate static cache based on global variable events
duncanmcclean Mar 20, 2025
69de722
wip
duncanmcclean Mar 20, 2025
5018ae8
Pint
duncanmcclean Mar 20, 2025
a9c8454
wip
duncanmcclean Mar 20, 2025
7192573
Merge remote-tracking branch 'origin/master' into separate-globals
duncanmcclean Mar 21, 2025
c07f6cf
Re-enable updating stache indexes after clearing cache in multisite c…
duncanmcclean Mar 21, 2025
5d89e4a
Enable/disable stache indexes just for the global set stuff
duncanmcclean Mar 21, 2025
9a221b6
Merge branch 'master' into separate-globals
jasonvarga Mar 21, 2025
68c70a8
plural
jasonvarga Mar 21, 2025
9470766
Same the way that it was
jasonvarga Mar 21, 2025
5bc45ae
Since GlobalFactory::make() no longer makes the variables, being able…
jasonvarga Mar 24, 2025
e428128
For consistency, avoid using GlobalSet::makeLocalization from outside
jasonvarga Mar 24, 2025
4970d5c
Remove globals factory
jasonvarga Mar 24, 2025
024426c
variable content is irrelevant in these tests and syntax these syntax…
jasonvarga Mar 24, 2025
c0916b6
rework sites method
jasonvarga Mar 24, 2025
a7efe4e
revert these. we still want the 404s. add tests.
jasonvarga Mar 24, 2025
d464409
didnt really need to change
jasonvarga Mar 24, 2025
8ccdac9
not anymore
jasonvarga Mar 24, 2025
54bb8b7
404 was fine but aborting results in a blank screen. might as well fi…
jasonvarga Mar 24, 2025
953a00c
multisite conversion can be simplified now ...
jasonvarga Mar 24, 2025
e0b7045
nitpick
jasonvarga Mar 24, 2025
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
2 changes: 1 addition & 1 deletion resources/js/components/globals/Sites.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@
<td class="text-sm">
<v-select
:options="siteOriginOptions(site)"
:value="site.origin"
:model-value="site.origin"
:searchable="false"
:reduce="(opt) => opt.value"
@update:model-value="site.origin = $event"
Expand Down
43 changes: 21 additions & 22 deletions src/Console/Commands/Multisite.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,12 +11,10 @@
use Statamic\Facades\Collection;
use Statamic\Facades\Config;
use Statamic\Facades\File;
use Statamic\Facades\GlobalSet;
use Statamic\Facades\Nav;
use Statamic\Facades\Role;
use Statamic\Facades\Site;
use Statamic\Facades\Stache;
use Statamic\Facades\YAML;
use Statamic\Rules\Handle;
use Statamic\Statamic;
use Statamic\Support\Traits\Hookable;
Expand DownExpand Up@@ -103,9 +101,9 @@ private function collectionsHaveBeenMoved(string $siteHandle): bool

private function globalsHaveBeenMoved(string $siteHandle): bool
{
$directory = Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle;

return File::isDirectory($directory);
return File::exists(
Stache::store('globals')->directory().DIRECTORY_SEPARATOR.$siteHandle
);
}

private function navsHaveBeenMoved(string $siteHandle): bool
Expand DownExpand Up@@ -264,29 +262,30 @@ private function moveCollectionTrees($collection): void

private function convertGlobalSets(): self
{
Config::set('statamic.system.multisite', true);
if ($this->siteHandle === 'default') {
// If it's default, the variables are already in the right spot.
return $this;
}

GlobalSet::all()->each(function ($set) {
$this->components->task(
description: "Updating global [{$set->handle()}]...",
task: function () use ($set) {
$this->moveGlobalSet($set);
}
);
});
$directory = Stache::store('globals')->directory();
$originalDirectory = $directory.DIRECTORY_SEPARATOR.'default';
$newDirectory = $directory.DIRECTORY_SEPARATOR.$this->siteHandle;

return $this;
}
File::makeDirectory($newDirectory);

private function moveGlobalSet($set): void
{
$yaml = YAML::file($set->path())->parse();
collect(File::getFiles($originalDirectory))
->each(function ($path) use ($originalDirectory, $newDirectory) {
$basename = pathinfo($path, PATHINFO_BASENAME);

$data = $yaml['data'] ?? [];
File::move(
$originalDirectory.DIRECTORY_SEPARATOR.$basename,
$newDirectory.DIRECTORY_SEPARATOR.$basename
);
});

$set->addLocalization($set->makeLocalization($this->siteHandle)->data($data));
File::delete($originalDirectory);

$set->save();
return $this;
}

private function convertNavs(): self
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Concerns/ListensForContentEvents.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,8 @@ trait ListensForContentEvents
\Statamic\Events\FormSaved::class,
\Statamic\Events\GlobalSetDeleted::class,
\Statamic\Events\GlobalSetSaved::class,
\Statamic\Events\GlobalVariablesDeleted::class,
\Statamic\Events\GlobalVariablesSaved::class,
\Statamic\Events\NavDeleted::class,
\Statamic\Events\NavSaved::class,
\Statamic\Events\NavTreeSaved::class,
Expand Down
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesDeleted.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesDeleted extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesDeleted extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables deleted', [], config('statamic.git.locale'));
}
}
9 changes: 8 additions & 1 deletion src/Events/GlobalVariablesSaved.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,9 +2,16 @@

namespace Statamic\Events;

class GlobalVariablesSaved extends Event
use Statamic\Contracts\Git\ProvidesCommitMessage;

class GlobalVariablesSaved extends Event implements ProvidesCommitMessage
{
public function __construct(public $variables)
{
}

public function commitMessage()
{
return __('Global variables saved', [], config('statamic.git.locale'));
}
}
74 changes: 49 additions & 25 deletions src/Globals/GlobalSet.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@ class GlobalSet implements Contract
protected $handle;
protected $afterSaveCallbacks = [];
protected $withEvents = true;
private $sites = [];

public function id()
{
Expand DownExpand Up@@ -118,12 +119,14 @@ public function save()

protected function saveOrDeleteLocalizations()
{
$localizations = $this->localizations();
$localizations = $this->freshLocalizations();

$localizations->each->save();
$this->sites()
->reject(fn ($site) => $localizations->has($site))
->each(fn ($site) => $this->makeLocalization($site)->save());

$this->freshLocalizations()
->diffKeys($localizations)
$localizations
->filter(fn ($localization) => ! $this->sites()->contains($localization->locale()))
->each->delete();
}

Expand DownExpand Up@@ -156,17 +159,10 @@ public function delete()

public function fileData()
{
$data = [
return Arr::removeNullValues([
'title' => $this->title(),
];

if (! Site::multiEnabled() && ($variables = $this->in(Site::default()->handle()))) {
$data['data'] = Arr::removeNullValues(
$variables->data()->all()
);
}

return $data;
'sites' => Site::multiEnabled() ? $this->origins()->all() : null,
]);
}

public function makeLocalization($site)
Expand All@@ -176,30 +172,53 @@ public function makeLocalization($site)
->locale($site);
}

public function addLocalization($localization)
public function sites($sites = null)
{
$localization->globalSet($this);
if (func_num_args() === 0) {
$sites = collect($this->sites);

$this->localizations()[$localization->locale()] = $localization;
if ($sites->isEmpty()) {
return collect([Site::default()->handle()]);
}

return $this;
}
return collect($this->sites)->keys();
}

public function removeLocalization($localization)
{
$this->localizations()->forget($localization->locale());
$this->sites = collect($sites)->mapWithKeys(function ($value, $key) {
if (is_int($key)) {
return [$value => ['origin' => null]];
}

if (is_string($value) || is_null($value)) {
return [$key => ['origin' => $value]];
}

return [$key => $value];
})->all();

return $this;
}

public function sites()
public function origins()
{
return $this->localizations()->map->locale()->values()->toBase();
$sites = empty($this->sites)
? [Site::default()->handle() => ['origin' => null]]
: $this->sites;

return collect($sites)->map(fn ($value, $key) => $value['origin'] ?? null);
}

public function in($locale)
{
return $this->localizations()->get($locale);
if (! $this->sites()->contains($locale)) {
return null;
}

if (! $variables = $this->localizations()->get($locale)) {
$variables = $this->makeLocalization($locale);
}

return $variables;
}

public function inSelectedSite()
Expand DownExpand Up@@ -239,6 +258,11 @@ public function editUrl()
return cp_route('globals.edit', $this->handle());
}

public function updateUrl()
{
return cp_route('globals.update', $this->handle());
}

public function deleteUrl()
{
return cp_route('globals.destroy', $this->handle());
Expand Down
17 changes: 12 additions & 5 deletions src/Globals/Variables.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -77,7 +77,7 @@ public function path()
{
return vsprintf('%s/%s%s.%s', [
rtrim(Stache::store('global-variables')->directory(), '/'),
Site::multiEnabled() ? $this->locale().'/' : '',
$this->locale().'/',
$this->handle(),
'yaml',
]);
Expand DownExpand Up@@ -152,6 +152,8 @@ public function save()
GlobalVariablesSaved::dispatch($this);
}

Blink::forget('global-set-localizations-'.$this->globalSet()->id());

return $this;
}

Expand DownExpand Up@@ -227,13 +229,18 @@ protected function fallbackBlueprint()

public function fileData()
{
$data = $this->data()->all();
return $this->data()->all();
}

if ($this->hasOrigin()) {
$data['origin'] = $this->origin()->locale();
public function origin($origin = null)
{
if (func_num_args() === 0) {
return $this->getOriginByString(
$this->globalSet()->origins()->get($this->locale())
);
}

return $data;
throw new \Exception('The origin cannot be set directly. It must be defined on the global set.');
}

protected function shouldRemoveNullsFromFileData()
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,7 @@ public function edit(Request $request, $id)
}

if (! $variables = $set->in($site)) {
return abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $variables);
Expand DownExpand Up@@ -87,7 +87,7 @@ public function update(Request $request, $handle)
}

if (! $set = $set->in($site)) {
abort(404);
return $this->pageNotFound();
}

$this->authorize('edit', $set);
Expand All@@ -104,7 +104,7 @@ public function update(Request $request, $handle)

$set->data($values);

$save = $set->globalSet()->addLocalization($set)->save();
$save = $set->save();

return response()->json([
'saved' => is_bool($save) ? $save : true,
Expand Down
26 changes: 8 additions & 18 deletions src/Http/Controllers/CP/Globals/GlobalsController.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,6 @@
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Rules\Handle;
use Statamic\Support\Arr;
use Statamic\Support\Str;

class GlobalsController extends CpController
Expand DownExpand Up@@ -58,8 +57,8 @@ public function edit($set)
return [
'name' => $site->name(),
'handle' => $site->handle(),
'enabled' => $enabled = $set->existsIn($site->handle()),
'origin' => $enabled ? optional($set->in($site->handle())->origin())->locale() : null,
'enabled' => $set->sites()->contains($site->handle()),
'origin' => $set->origins()->get($site->handle()),
];
})->values(),
];
Expand DownExpand Up@@ -112,19 +111,11 @@ public function update(Request $request, $set)
->blueprint($values['blueprint']);

if (Site::multiEnabled()) {
$sites = collect(Arr::get($values, 'sites'));
$sites = collect($values['sites'])
->filter(fn ($site) => $site['enabled'])
->mapWithKeys(fn ($site) => [$site['handle'] => $site['origin']]);

foreach ($sites->filter->enabled as $site) {
$vars = $set->in($site['handle']) ?? $set->makeLocalization($site['handle']);
$vars->origin($site['origin']);
$set->addLocalization($vars);
}

foreach ($sites->reject->enabled as $site) {
if ($set->existsIn($site['handle'])) {
$set->removeLocalization($set->in($site['handle']));
}
}
$set->sites($sites);
}

$set->save();
Expand DownExpand Up@@ -161,11 +152,10 @@ public function store(Request $request)
}

$global = GlobalSet::make($handle)->title($data['title']);

$global->addLocalization($global->makeLocalization(Site::default()->handle()));

$global->save();

$global->in(Site::default()->handle())->save();

session()->flash('message', __('Global Set created'));

return ['redirect' => $global->editUrl()];
Expand Down
Loading