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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lang/en/validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@
'email_available' => 'A user with this email already exists.',
'fieldset_imported_recursively' => 'Fieldset :handle is being imported recursively.',
'one_site_without_origin' => 'At least one site must not have an origin.',
'at_least_one_site_enabled' => 'At least one site must be enabled.',
'options_require_keys' => 'All options must have keys.',
'origin_cannot_be_disabled' => 'Cannot select a disabled origin.',
'parent_cannot_be_itself' => 'Cannot be its own parent.',
Expand Down
15 changes: 13 additions & 2 deletions resources/js/components/collections/OneOrManySitesField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,20 @@ export default {
},

sites() {
if (!this.publishContainer.values.value.sites) return [];
const sites = this.publishContainer.values.value.sites;

return this.publishContainer.values.value.sites.map((handle, i) => {
if (!sites?.length) return [];

if (typeof sites[0] === 'object') {
return sites
.filter((site) => site.enabled)
.map((site) => ({
handle: site.handle,
name: site.name,
}));
}

return sites.map((handle, i) => {
return {
handle,
name: this.publishContainer.meta.value.sites.data[i].title,
Expand Down
24 changes: 18 additions & 6 deletions resources/js/components/globals/Sites.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<template>
<div class="flex flex-col gap-3">
<div class="flex flex-wrap items-center gap-3 rounded-xl border border-gray-200 bg-gray-50 px-3 pr-2 py-2 dark:border-gray-700 dark:bg-gray-800">
<div
v-if="showOrigins"
class="flex flex-wrap items-center gap-3 rounded-xl border border-gray-200 bg-gray-50 px-3 pr-2 py-2 dark:border-gray-700 dark:bg-gray-800"
>
<Checkbox
size="sm"
solo
Expand Down Expand Up @@ -53,15 +56,15 @@
<table class="grid-table">
<thead>
<tr>
<th scope="col" class="checkbox-column w-8">
<th v-if="showOrigins" scope="col" class="checkbox-column w-8">
<span class="sr-only">{{ __('Select') }}</span>
</th>
<th scope="col">
<div class="flex items-center justify-between">
{{ __('Site') }}
</div>
</th>
<th scope="col">
<th v-if="showOrigins" scope="col">
<div class="flex items-center justify-between">
{{ __('Origin') }}
</div>
Expand All @@ -71,9 +74,10 @@
<tbody>
<template v-for="group in siteGroups" :key="group.key">
<tr v-if="hasNamedGroups">
<td colspan="3" class="sticky top-[calc(--spacing(7)+1px)] z-(--z-index-above) bg-gray-50 py-2! dark:bg-gray-800">
<td :colspan="columnCount" class="sticky top-[calc(--spacing(7)+1px)] z-(--z-index-above) bg-gray-50 py-2! dark:bg-gray-800">
<div class="flex items-center gap-4 ps-1!">
<Checkbox
v-if="showOrigins"
size="sm"
solo
:model-value="isGroupSelected(group)"
Expand All @@ -90,7 +94,7 @@
</td>
</tr>
<tr v-for="site in group.items" :key="site.handle">
<td class="checkbox-column ps-3!">
<td v-if="showOrigins" class="checkbox-column ps-3!">
<Checkbox
size="sm"
class="pt-2.5"
Expand All @@ -106,7 +110,7 @@
<Heading :text="__(site.name)" />
</div>
</td>
<td class="grid-cell">
<td v-if="showOrigins" class="grid-cell">
<Select
class="w-full"
:options="siteOriginOptions(site)"
Expand Down Expand Up @@ -180,6 +184,14 @@ export default {
},

computed: {
showOrigins() {
return this.config.origins !== false;
},

columnCount() {
return this.showOrigins ? 3 : 1;
},

hasNamedGroups() {
return hasNamedSiteGroups(this.sites);
},
Expand Down
55 changes: 52 additions & 3 deletions src/Fieldtypes/GlobalSetSites.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,63 @@ class GlobalSetSites extends Fieldtype
{
protected $selectable = false;

public function rules(): array
protected function configFieldItems(): array
{
return [
$this->cannotAllHaveOriginsRule(),
$this->originsMustBeEnabledRule(),
'origins' => [
'display' => __('Origins'),
'type' => 'toggle',
'default' => true,
],
];
}

public function process($data)
{
if ($this->config('origins', true)) {
return $data;
}

return collect($data ?? [])
->filter(fn ($site) => is_string($site) ? filled($site) : ($site['enabled'] ?? false))
->map(fn ($site) => is_string($site) ? $site : $site['handle'])
->filter()
->values()
->all();
}

public function rules(): array
{
$rules = [
$this->atLeastOneSiteEnabledRule(),
];

if ($this->config('origins', true)) {
$rules[] = $this->cannotAllHaveOriginsRule();
$rules[] = $this->originsMustBeEnabledRule();
}

return $rules;
}

private function atLeastOneSiteEnabledRule()
{
return new class implements ValidationRule
{
public function passes($attribute, $value)
{
return collect($value)->contains(function ($site) {
return is_string($site) ? filled($site) : ($site['enabled'] ?? false);
});
}

public function message()
{
return __('statamic::validation.at_least_one_site_enabled');
}
};
}

private function cannotAllHaveOriginsRule()
{
return new class implements ValidationRule
Expand Down
19 changes: 15 additions & 4 deletions src/Http/Controllers/CP/Collections/CollectionsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,18 @@ public function edit($collection)
'default_publish_state' => $collection->defaultPublishState(),
'template' => $collection->template(),
'layout' => $collection->layout(),
'sites' => $collection->sites()->all(),
'sites' => Site::multiEnabled()
? Site::all()->map(function ($site) use ($collection) {
return [
'name' => $site->name(),
'handle' => $site->handle(),
'group' => $site->group(),
'group_handle' => $site->groupHandle(),
'enabled' => $collection->sites()->contains($site->handle()),
'origin' => null,
];
})->values()->all()
: $collection->sites()->all(),
'propagate' => $collection->propagate(),
'routes' => $collection->routes()->unique()->count() === 1
? $collection->routes()->first()
Expand Down Expand Up @@ -595,11 +606,11 @@ protected function editFormBlueprint($collection)

if (Site::multiEnabled()) {
$fields['sites'] = [
'display' => __('Sites'),
'display' => __('Localizations'),
'fields' => [
'sites' => [
'type' => 'sites',
'mode' => 'select',
'type' => 'global_set_sites',
'origins' => false,
'required' => true,
],
'propagate' => [
Expand Down
19 changes: 15 additions & 4 deletions src/Http/Controllers/CP/Taxonomies/TaxonomiesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,18 @@ public function edit($taxonomy)
'title' => $taxonomy->title(),
'blueprints' => $taxonomy->termBlueprints()->map->handle()->all(),
'collections' => $taxonomy->collections()->map->handle()->all(),
'sites' => $taxonomy->sites()->all(),
'sites' => Site::multiEnabled()
? Site::all()->map(function ($site) use ($taxonomy) {
return [
'name' => $site->name(),
'handle' => $site->handle(),
'group' => $site->group(),
'group_handle' => $site->groupHandle(),
'enabled' => $taxonomy->sites()->contains($site->handle()),
'origin' => null,
];
})->values()->all()
: $taxonomy->sites()->all(),
'preview_targets' => $taxonomy->basePreviewTargets(),
'term_template' => $taxonomy->hasCustomTermTemplate() ? $taxonomy->termTemplate() : null,
'template' => $taxonomy->hasCustomTemplate() ? $taxonomy->template() : null,
Expand Down Expand Up @@ -287,11 +298,11 @@ protected function editFormBlueprint($taxonomy)

if (Site::multiEnabled()) {
$fields['sites'] = [
'display' => __('Sites'),
'display' => __('Localizations'),
'fields' => [
'sites' => [
'type' => 'sites',
'mode' => 'select',
'type' => 'global_set_sites',
'origins' => false,
'required' => true,
],
],
Expand Down
60 changes: 60 additions & 0 deletions tests/Feature/Collections/UpdateCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,66 @@ public function settings_links_to_true_will_also_create_the_default_blueprint_if
$this->assertEquals(['test', 'link'], $blueprints->map->handle()->values()->all());
}

#[Test]
public function it_updates_collection_sites_from_enabled_rows_and_ignores_origins()
{
$this->setSites([
'en' => ['name' => 'English', 'locale' => 'en_US', 'url' => 'http://test.com/'],
'fr' => ['name' => 'French', 'locale' => 'fr_FR', 'url' => 'http://fr.test.com/'],
'de' => ['name' => 'German', 'locale' => 'de_DE', 'url' => 'http://de.test.com/'],
]);

$collection = Collection::make('test')->sites(['en', 'fr'])->save();

$this
->actingAs($this->userWithPermission())
->update($collection, [
'sites' => [
['name' => 'English', 'handle' => 'en', 'enabled' => true, 'origin' => null],
['name' => 'French', 'handle' => 'fr', 'enabled' => false, 'origin' => 'en'],
['name' => 'German', 'handle' => 'de', 'enabled' => true, 'origin' => 'en'],
],
'origin_behavior' => 'root',
'propagate' => true,
'structured' => false,
'require_slugs' => true,
'preview_targets' => [],
])
->assertOk();

$updated = Collection::findByHandle('test');

$this->assertEquals(['en', 'de'], $updated->sites()->all());
$this->assertEquals('root', $updated->originBehavior());
$this->assertTrue($updated->propagate());
}

#[Test]
public function it_updates_collection_sites_from_legacy_handle_array()
{
$this->setSites([
'en' => ['name' => 'English', 'locale' => 'en_US', 'url' => 'http://test.com/'],
'fr' => ['name' => 'French', 'locale' => 'fr_FR', 'url' => 'http://fr.test.com/'],
'de' => ['name' => 'German', 'locale' => 'de_DE', 'url' => 'http://de.test.com/'],
]);

$collection = Collection::make('test')->sites(['en', 'fr'])->save();

$this
->actingAs($this->userWithPermission())
->update($collection, [
'sites' => ['en', 'de'],
'origin_behavior' => 'root',
'propagate' => false,
'structured' => false,
'require_slugs' => true,
'preview_targets' => [],
])
->assertOk();

$this->assertEquals(['en', 'de'], Collection::findByHandle('test')->sites()->all());
}

private function userWithoutPermission()
{
$this->setTestRoles(['test' => ['access cp']]);
Expand Down
50 changes: 50 additions & 0 deletions tests/Feature/Taxonomies/UpdateTaxonomyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,56 @@ public function it_associates_taxonomies_with_collections()
$this->assertTrue($collectionThree->taxonomies()->contains($taxonomy));
}

#[Test]
public function it_updates_taxonomy_sites_from_enabled_rows_and_ignores_origins()
{
$this->setSites([
'en' => ['name' => 'English', 'locale' => 'en_US', 'url' => 'http://test.com/'],
'fr' => ['name' => 'French', 'locale' => 'fr_FR', 'url' => 'http://fr.test.com/'],
'de' => ['name' => 'German', 'locale' => 'de_DE', 'url' => 'http://de.test.com/'],
]);

$taxonomy = tap(Taxonomy::make('test')->sites(['en', 'fr']))->save();

$this
->actingAs($this->userWithPermission())
->update($taxonomy, [
'sites' => [
['name' => 'English', 'handle' => 'en', 'enabled' => true, 'origin' => null],
['name' => 'French', 'handle' => 'fr', 'enabled' => false, 'origin' => 'en'],
['name' => 'German', 'handle' => 'de', 'enabled' => true, 'origin' => 'en'],
],
'preview_targets' => [],
'collections' => [],
])
->assertOk();

$this->assertEquals(['en', 'de'], Taxonomy::findByHandle('test')->sites()->all());
}

#[Test]
public function it_updates_taxonomy_sites_from_legacy_handle_array()
{
$this->setSites([
'en' => ['name' => 'English', 'locale' => 'en_US', 'url' => 'http://test.com/'],
'fr' => ['name' => 'French', 'locale' => 'fr_FR', 'url' => 'http://fr.test.com/'],
'de' => ['name' => 'German', 'locale' => 'de_DE', 'url' => 'http://de.test.com/'],
]);

$taxonomy = tap(Taxonomy::make('test')->sites(['en', 'fr']))->save();

$this
->actingAs($this->userWithPermission())
->update($taxonomy, [
'sites' => ['en', 'de'],
'preview_targets' => [],
'collections' => [],
])
->assertOk();

$this->assertEquals(['en', 'de'], Taxonomy::findByHandle('test')->sites()->all());
}

private function userWithoutPermission()
{
$this->setTestRoles(['test' => ['access cp']]);
Expand Down
Loading