Skip to content

Commit b1c1d6b

Browse files
authored
Tighten Localize action validation and cover bulk visibility
Constrain the site field to offered options, drop the unreachable edit soft-skip, and add tests for visibleToBulk, authorize, and site validation.
1 parent 71139cd commit b1c1d6b

2 files changed

Lines changed: 110 additions & 8 deletions

File tree

src/Actions/Localize.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Statamic\Actions;
44

5+
use Illuminate\Validation\Rule;
56
use Statamic\Contracts\Entries\Entry;
67
use Statamic\Facades\Site;
78
use Statamic\Facades\User;
@@ -80,12 +81,6 @@ public function run($entries, $values)
8081
return;
8182
}
8283

83-
if (! User::current()->can('edit', $entry)) {
84-
$skipped++;
85-
86-
return;
87-
}
88-
8984
$entry->makeLocalization($site)->store(['user' => User::current()]);
9085
$created++;
9186
});
@@ -115,8 +110,8 @@ protected function fieldItems()
115110
'hide_display' => true,
116111
'type' => 'select',
117112
'placeholder' => __('Choose a site...'),
118-
'options' => $this->siteOptions(),
119-
'validate' => 'required',
113+
'options' => $options = $this->siteOptions(),
114+
'validate' => ['required', Rule::in(array_keys($options))],
120115
],
121116
];
122117
}

tests/Actions/LocalizeTest.php

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Tests\Actions;
44

55
use Facades\Tests\Factories\EntryFactory;
6+
use Illuminate\Support\Facades\Config;
67
use PHPUnit\Framework\Attributes\Test;
78
use Statamic\Actions\Localize;
89
use Statamic\Facades\Collection;
@@ -62,6 +63,95 @@ public function it_is_hidden_for_single_site_collections()
6263
$this->assertFalse($action->visibleTo($entry));
6364
}
6465

66+
#[Test]
67+
public function it_is_visible_to_bulk_when_any_entry_is_missing_a_localization()
68+
{
69+
$alfa = EntryFactory::id('alfa')->collection('test')->slug('alfa')->locale('en')->create();
70+
$bravo = EntryFactory::id('bravo')->collection('test')->slug('bravo')->locale('en')->create();
71+
EntryFactory::id('bravo-fr')->collection('test')->slug('bravo')->locale('fr')->origin('bravo')->create();
72+
EntryFactory::id('bravo-de')->collection('test')->slug('bravo')->locale('de')->origin('bravo')->create();
73+
74+
$items = collect([$alfa, $bravo]);
75+
$action = (new Localize)->context(['view' => 'list'])->items($items);
76+
77+
$this->assertTrue($action->visibleToBulk($items));
78+
}
79+
80+
#[Test]
81+
public function it_is_hidden_from_bulk_when_all_entries_are_fully_localized()
82+
{
83+
$alfa = EntryFactory::id('alfa')->collection('test')->slug('alfa')->locale('en')->create();
84+
EntryFactory::id('alfa-fr')->collection('test')->slug('alfa')->locale('fr')->origin('alfa')->create();
85+
EntryFactory::id('alfa-de')->collection('test')->slug('alfa')->locale('de')->origin('alfa')->create();
86+
87+
$items = collect([$alfa]);
88+
$action = (new Localize)->context(['view' => 'list'])->items($items);
89+
90+
$this->assertFalse($action->visibleToBulk($items));
91+
}
92+
93+
#[Test]
94+
public function it_is_hidden_from_bulk_when_selection_includes_non_entries()
95+
{
96+
$entry = EntryFactory::id('alfa')->collection('test')->slug('alfa')->locale('en')->create();
97+
$items = collect([$entry, 'not-an-entry']);
98+
$action = (new Localize)->context(['view' => 'list'])->items($items);
99+
100+
$this->assertFalse($action->visibleToBulk($items));
101+
}
102+
103+
#[Test]
104+
public function it_is_hidden_from_bulk_when_entries_are_from_different_collections()
105+
{
106+
Collection::make('other')->sites(['en', 'fr', 'de'])->save();
107+
108+
$alfa = EntryFactory::id('alfa')->collection('test')->slug('alfa')->locale('en')->create();
109+
$bravo = EntryFactory::id('bravo')->collection('other')->slug('bravo')->locale('en')->create();
110+
$items = collect([$alfa, $bravo]);
111+
$action = (new Localize)->context(['view' => 'list'])->items($items);
112+
113+
$this->assertFalse($action->visibleToBulk($items));
114+
}
115+
116+
#[Test]
117+
public function it_is_hidden_from_bulk_when_multisite_is_disabled()
118+
{
119+
Config::set('statamic.system.multisite', false);
120+
121+
$entry = EntryFactory::id('alfa')->collection('test')->slug('alfa')->locale('en')->create();
122+
$items = collect([$entry]);
123+
$action = (new Localize)->context(['view' => 'list'])->items($items);
124+
125+
$this->assertFalse($action->visibleToBulk($items));
126+
}
127+
128+
#[Test]
129+
public function it_is_hidden_from_bulk_for_single_site_collections()
130+
{
131+
Collection::make('single')->sites(['en'])->save();
132+
$entry = EntryFactory::id('alfa')->collection('single')->slug('alfa')->locale('en')->create();
133+
$items = collect([$entry]);
134+
$action = (new Localize)->context(['view' => 'list'])->items($items);
135+
136+
$this->assertFalse($action->visibleToBulk($items));
137+
}
138+
139+
#[Test]
140+
public function it_authorizes_users_who_can_edit_the_entry()
141+
{
142+
$this->setTestRoles([
143+
'editor' => ['edit test entries', 'access en site'],
144+
'viewer' => ['view test entries', 'access en site'],
145+
]);
146+
147+
$userWithPermission = tap(User::make()->assignRole('editor'))->save();
148+
$userWithoutPermission = tap(User::make()->assignRole('viewer'))->save();
149+
$entry = EntryFactory::id('alfa')->collection('test')->slug('alfa')->locale('en')->create();
150+
151+
$this->assertTrue((new Localize)->authorize($userWithPermission, $entry));
152+
$this->assertFalse((new Localize)->authorize($userWithoutPermission, $entry));
153+
}
154+
65155
#[Test]
66156
public function it_localizes_entries_missing_the_selected_site()
67157
{
@@ -93,4 +183,21 @@ public function it_only_offers_sites_that_are_missing_for_selected_entries()
93183
'de' => 'German',
94184
], $siteField->get('options'));
95185
}
186+
187+
#[Test]
188+
public function it_rejects_sites_that_are_not_offered()
189+
{
190+
$en = EntryFactory::id('alfa')->collection('test')->slug('alfa')->locale('en')->create();
191+
EntryFactory::id('alfa-fr')->collection('test')->slug('alfa')->locale('fr')->origin('alfa')->create();
192+
193+
$action = (new Localize)->context(['view' => 'list'])->items([$en]);
194+
195+
$this->assertFalse(
196+
$action->fields()->addValues(['site' => 'fr'])->validator()->validator()->passes()
197+
);
198+
199+
$this->assertTrue(
200+
$action->fields()->addValues(['site' => 'de'])->validator()->validator()->passes()
201+
);
202+
}
96203
}

0 commit comments

Comments
 (0)