Skip to content

Commit 7f50c95

Browse files
authored
[5.x] Fix user name and email logic (#14079)
1 parent f41c332 commit 7f50c95

7 files changed

Lines changed: 128 additions & 21 deletions

File tree

src/Actions/Impersonate.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function run($users, $values)
5151

5252
$guard->login($users->first());
5353
session()->put('statamic_impersonated_by', $impersonator->getKey());
54-
Toast::success(__('You are now impersonating').' '.$impersonated->name());
54+
Toast::success(__('You are now impersonating').' '.($impersonated->name() ?? $impersonated->email()));
5555

5656
ImpersonationStarted::dispatch($impersonator, $impersonated);
5757
} finally {

src/Auth/User.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,13 @@ public function title()
7777

7878
public function initials()
7979
{
80+
if (! $name = $this->name()) {
81+
return '?';
82+
}
83+
8084
$surname = '';
81-
if ($name = $this->get('name')) {
82-
if (Str::contains($name, ' ')) {
83-
[$name, $surname] = explode(' ', $name);
84-
}
85-
} else {
86-
$name = (string) $this->email();
85+
if (Str::contains($name, ' ')) {
86+
[$name, $surname] = explode(' ', $name, 2);
8787
}
8888

8989
return strtoupper(mb_substr($name, 0, 1).mb_substr($surname, 0, 1));
@@ -320,7 +320,7 @@ public function name()
320320
return $name;
321321
}
322322

323-
return $this->email();
323+
return null;
324324
}
325325

326326
public function defaultAugmentedArrayKeys()

src/Fieldtypes/Users.php

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Statamic\Fieldtypes;
44

55
use Illuminate\Support\Collection;
6+
use Statamic\Contracts\Auth\User as UserContract;
67
use Statamic\CP\Column;
78
use Statamic\Facades\GraphQL;
89
use Statamic\Facades\Scope;
@@ -86,8 +87,10 @@ public function preProcess($data)
8687
protected function toItemArray($id, $site = null)
8788
{
8889
if ($user = User::find($id)) {
90+
$canViewUsers = $this->canViewUser($user);
91+
8992
return [
90-
'title' => $user->name(),
93+
'title' => $this->userTitle($user, $canViewUsers),
9194
'id' => $id,
9295
'edit_url' => $user->editUrl(),
9396
'editable' => User::current()->can('edit', $user),
@@ -132,11 +135,18 @@ public function getIndexItems($request)
132135
$user = $user->getSearchable();
133136
}
134137

135-
return [
138+
$canViewUsers = $this->canViewUser($user);
139+
140+
$fields = [
136141
'id' => $user->id(),
137-
'title' => $user->name(),
138-
'email' => $user->email(),
142+
'title' => $this->userTitle($user, $canViewUsers),
139143
];
144+
145+
if ($canViewUsers) {
146+
$fields['email'] = $user->email();
147+
}
148+
149+
return $fields;
140150
};
141151

142152
if ($request->boolean('paginate', true)) {
@@ -152,24 +162,54 @@ public function getIndexItems($request)
152162

153163
protected function getColumns()
154164
{
155-
return [
165+
$columns = [
156166
Column::make('title')->label('Name'),
157-
Column::make('email'),
158167
];
168+
169+
if ($this->canViewUsers()) {
170+
$columns[] = Column::make('email');
171+
}
172+
173+
return $columns;
159174
}
160175

161176
public function preProcessIndex($data)
162177
{
163-
return $this->getItemsForPreProcessIndex($data)->map(function ($user) {
178+
$canViewUsers = $this->canViewUsers();
179+
180+
return $this->getItemsForPreProcessIndex($data)->map(function ($user) use ($canViewUsers) {
164181
return [
165182
'id' => $user->id(),
166-
'title' => $user->name(),
183+
'title' => $this->userTitle($user, $canViewUsers),
167184
'edit_url' => $user->editUrl(),
168185
'published' => null,
169186
];
170187
})->filter()->values();
171188
}
172189

190+
private function userTitle($user, bool $canViewUsers): ?string
191+
{
192+
return $user->name() ?? ($canViewUsers ? $user->email() : $user->id());
193+
}
194+
195+
private function canViewUsers(): bool
196+
{
197+
if (! $current = User::current()) {
198+
return false;
199+
}
200+
201+
return $current->can('index', UserContract::class);
202+
}
203+
204+
private function canViewUser($user): bool
205+
{
206+
if (! $current = User::current()) {
207+
return false;
208+
}
209+
210+
return $current->can('view', $user);
211+
}
212+
173213
protected function getItemsForPreProcessIndex($values): Collection
174214
{
175215
if (! $augmented = $this->augment($values)) {

src/Query/Scopes/Filters/Fields/User.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ public function badge($values)
6969
return null;
7070
}
7171

72-
$user = Users::find($user)->name();
72+
$user = Users::find($user);
73+
$user = $user->name() ?? $user->email();
7374

7475
return $field.' '.strtolower($operator).' '.$user;
7576
}

src/Revisions/Revision.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public function toArray()
113113
$user = [
114114
'id' => $user->id(),
115115
'email' => $user->email(),
116-
'name' => $user->name(),
116+
'name' => $user->name() ?? $user->email(),
117117
'avatar' => $user->avatar(),
118118
'initials' => $user->initials(),
119119
];

tests/Auth/UserContractTests.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public function gets_the_name()
6666
$this->assertEquals('John Smith', $this->makeUser()->set('name', 'John Smith')->name());
6767
$this->assertEquals('John', $this->makeUser()->data(['name' => null, 'first_name' => 'John'])->name());
6868
$this->assertEquals('John Smith', $this->makeUser()->data(['name' => null, 'first_name' => 'John', 'last_name' => 'Smith'])->name());
69-
$this->assertEquals('john@example.com', $this->makeUser()->remove('name')->email('john@example.com')->name());
69+
$this->assertNull($this->makeUser()->remove('name')->email('john@example.com')->name());
7070
}
7171

7272
#[Test]
@@ -324,11 +324,11 @@ public function it_gets_initials_from_name_with_no_surname()
324324
}
325325

326326
#[Test]
327-
public function it_gets_initials_from_email_if_name_doesnt_exist()
327+
public function it_gets_question_mark_initials_if_name_doesnt_exist()
328328
{
329329
$user = $this->user()->remove('name');
330330

331-
$this->assertEquals('J', $user->initials());
331+
$this->assertEquals('?', $user->initials());
332332
}
333333

334334
#[Test]

tests/Fieldtypes/UsersTest.php

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

33
namespace Tests\Fieldtypes;
44

5+
use Illuminate\Http\Request;
56
use Illuminate\Support\Collection;
67
use PHPUnit\Framework\Attributes\Test;
78
use Statamic\Auth\UserCollection;
@@ -11,12 +12,14 @@
1112
use Statamic\Facades;
1213
use Statamic\Fields\Field;
1314
use Statamic\Fieldtypes\Users;
15+
use Tests\FakesRoles;
1416
use Tests\Fieldtypes\Concerns\TestsQueryableValueWithMaxItems;
1517
use Tests\PreventSavingStacheItemsToDisk;
1618
use Tests\TestCase;
1719

1820
class UsersTest extends TestCase
1921
{
22+
use FakesRoles;
2023
use PreventSavingStacheItemsToDisk;
2124
use TestsQueryableValueWithMaxItems;
2225

@@ -26,6 +29,7 @@ public function setUp(): void
2629

2730
Facades\User::make()->id('123')->set('name', 'One')->email('one@domain.com')->save();
2831
Facades\User::make()->id('456')->set('name', 'Two')->email('two@domain.com')->save();
32+
Facades\User::make()->id('789')->email('nameless@domain.com')->save();
2933
}
3034

3135
#[Test]
@@ -94,6 +98,53 @@ public function it_shallow_augments_to_a_single_user_when_max_items_is_one()
9498
], $augmented->toArray());
9599
}
96100

101+
#[Test]
102+
public function it_hides_email_from_index_items_without_view_users_permission()
103+
{
104+
$this->actingAs($this->cpUserWithPermissions(['access cp']));
105+
106+
$items = $this->fieldtype()->getIndexItems(new Request(['paginate' => false]));
107+
$namelessUser = $items->firstWhere('id', '789');
108+
109+
$this->assertArrayNotHasKey('email', $namelessUser);
110+
$this->assertEquals('789', $namelessUser['title']);
111+
}
112+
113+
#[Test]
114+
public function it_includes_email_in_index_items_with_view_users_permission()
115+
{
116+
$this->actingAs($this->cpUserWithPermissions(['access cp', 'view users']));
117+
118+
$items = $this->fieldtype()->getIndexItems(new Request(['paginate' => false]));
119+
$namelessUser = $items->firstWhere('id', '789');
120+
121+
$this->assertEquals('nameless@domain.com', $namelessUser['title']);
122+
$this->assertEquals('nameless@domain.com', $namelessUser['email']);
123+
}
124+
125+
#[Test]
126+
public function it_hides_the_email_column_without_view_users_permission()
127+
{
128+
$this->actingAs($this->cpUserWithPermissions(['access cp']));
129+
130+
$columns = $this->getColumns($this->fieldtype());
131+
132+
$this->assertCount(1, $columns);
133+
$this->assertEquals('title', $columns[0]->field);
134+
}
135+
136+
#[Test]
137+
public function it_includes_the_email_column_with_view_users_permission()
138+
{
139+
$this->actingAs($this->cpUserWithPermissions(['access cp', 'view users']));
140+
141+
$columns = $this->getColumns($this->fieldtype());
142+
143+
$this->assertCount(2, $columns);
144+
$this->assertEquals('title', $columns[0]->field);
145+
$this->assertEquals('email', $columns[1]->field);
146+
}
147+
97148
public function fieldtype($config = [])
98149
{
99150
$field = new Field('test', array_merge([
@@ -102,4 +153,19 @@ public function fieldtype($config = [])
102153

103154
return (new Users)->setField($field);
104155
}
156+
157+
private function cpUserWithPermissions(array $permissions)
158+
{
159+
$this->setTestRoles(['test' => $permissions]);
160+
161+
return tap(Facades\User::make()->id(uniqid())->assignRole('test'))->save();
162+
}
163+
164+
private function getColumns(Users $fieldtype): array
165+
{
166+
$method = new \ReflectionMethod($fieldtype, 'getColumns');
167+
$method->setAccessible(true);
168+
169+
return $method->invoke($fieldtype);
170+
}
105171
}

0 commit comments

Comments
 (0)