Skip to content

Commit 7ac3389

Browse files
committed
MBS-11828: Add admin page to check whether username is locked
This allows searching by exact name (the most common need), but also supports a regular expression search since that was trivial to implement and might prove useful.
1 parent 60ce54d commit 7ac3389

File tree

7 files changed

+165
-0
lines changed

7 files changed

+165
-0
lines changed

lib/MusicBrainz/Server/Controller/Admin.pm

+37
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use Try::Tiny;
55
BEGIN { extends 'MusicBrainz::Server::Controller' };
66

77
use MusicBrainz::Server::Translation qw(l ln );
8+
use MusicBrainz::Server::Data::Utils qw( boolean_to_json );
89

910
sub edit_user : Path('/admin/user/edit') Args(1) RequireAuth HiddenOnSlaves SecureForm
1011
{
@@ -202,6 +203,42 @@ sub ip_lookup : Path('/admin/ip-lookup') Args(1) RequireAuth(account_admin) Hidd
202203
);
203204
}
204205

206+
sub locked_username_search : Path('/admin/locked-usernames/search') Args(0) RequireAuth(account_admin) HiddenOnSlaves {
207+
my ($self, $c) = @_;
208+
209+
my $form = $c->form(form => 'Admin::LockedUsernameSearch');
210+
my @results;
211+
my $show_results = 0;
212+
213+
if ($c->form_posted_and_valid($form, $c->req->body_params)) {
214+
try {
215+
@results = $c->model('Editor')->search_old_editor_names(
216+
$form->field('username')->value // '',
217+
$form->field('use_regular_expression')->value,
218+
);
219+
$show_results = 1;
220+
} catch {
221+
my $error = $_;
222+
if ("$error" =~ m/invalid regular expression/) {
223+
$form->field('username')->add_error(l('Invalid regular expression.'));
224+
$c->response->status(400);
225+
} else {
226+
die $error;
227+
}
228+
};
229+
}
230+
231+
$c->stash(
232+
current_view => 'Node',
233+
component_path => 'admin/LockedUsernameSearch',
234+
component_props => {
235+
form => $form->TO_JSON,
236+
@results ? (results => \@results) : (),
237+
showResults => boolean_to_json($show_results),
238+
},
239+
);
240+
}
241+
205242
1;
206243

207244
=head1 COPYRIGHT AND LICENSE

lib/MusicBrainz/Server/Data/Editor.pm

+9
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,15 @@ sub insert
265265
}, $self->sql);
266266
}
267267

268+
sub search_old_editor_names {
269+
my ($self, $name, $use_regular_expression) = @_;
270+
271+
my $condition = $use_regular_expression ? "name ~* ?" : "LOWER(name) = LOWER(?)";
272+
my $query = "SELECT name FROM old_editor_name WHERE $condition LIMIT 100";
273+
274+
@{ $self->sql->select_single_column_array($query, $name) };
275+
}
276+
268277
sub update_email
269278
{
270279
my ($self, $editor, $email) = @_;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package MusicBrainz::Server::Form::Admin::LockedUsernameSearch;
2+
3+
use HTML::FormHandler::Moose;
4+
5+
extends 'MusicBrainz::Server::Form';
6+
7+
has '+name' => (default => 'lockedusernamesearch');
8+
9+
has_field 'username' => (type => 'Text');
10+
has_field 'use_regular_expression' => (type => 'Boolean');
11+
has_field 'submit' => (type => 'Submit');
12+
13+
1;
14+
15+
=head1 COPYRIGHT AND LICENSE
16+
17+
Copyright (C) 2021 MetaBrainz Foundation
18+
19+
This file is part of MusicBrainz, the open internet music database,
20+
and is licensed under the GPL version 2, or (at your option) any
21+
later version: http://www.gnu.org/licenses/gpl-2.0.txt
22+
23+
=cut

root/admin/LockedUsernameSearch.js

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* @flow strict-local
3+
* Copyright (C) 2021 MetaBrainz Foundation
4+
*
5+
* This file is part of MusicBrainz, the open internet music database,
6+
* and is licensed under the GPL version 2, or (at your option) any
7+
* later version: http://www.gnu.org/licenses/gpl-2.0.txt
8+
*/
9+
10+
import * as React from 'react';
11+
12+
import FormRowCheckbox from '../components/FormRowCheckbox';
13+
import FormRowText from '../components/FormRowText';
14+
import FormSubmit from '../components/FormSubmit';
15+
import Layout from '../layout';
16+
import expand2react from '../static/scripts/common/i18n/expand2react';
17+
18+
type Props = {
19+
+form: FormT<{
20+
+use_regular_expression: ReadOnlyFieldT<boolean>,
21+
+username: ReadOnlyFieldT<string>,
22+
}>,
23+
+results?: $ReadOnlyArray<string>,
24+
+showResults: boolean,
25+
};
26+
27+
const LockedUsernameSearch = ({
28+
form,
29+
results,
30+
showResults,
31+
}: Props): React.Element<typeof Layout> => (
32+
<Layout fullWidth title="Search locked usernames">
33+
<div id="content">
34+
<h1>{'Search locked usernames'}</h1>
35+
36+
<form action="/admin/locked-usernames/search" method="post">
37+
<p>
38+
{expand2react(
39+
'Enter a username or a {link|POSIX regular expression}.',
40+
{
41+
link: 'https://www.postgresql.org/docs/12/' +
42+
'functions-matching.html#FUNCTIONS-POSIX-REGEXP',
43+
},
44+
)}
45+
</p>
46+
47+
<FormRowText
48+
field={form.field.username}
49+
label="Username:"
50+
size={50}
51+
uncontrolled
52+
/>
53+
54+
<FormRowCheckbox
55+
field={form.field.use_regular_expression}
56+
label="Search using regular expression"
57+
uncontrolled
58+
/>
59+
60+
<div className="row no-label">
61+
<FormSubmit
62+
label="Search"
63+
name="lockedusernamesearch.submit"
64+
value="1"
65+
/>
66+
</div>
67+
68+
{showResults ? (
69+
<>
70+
<h3>{'Matching locked names:'}</h3>
71+
{results?.length ? (
72+
<ul>
73+
{results.map(result => (
74+
<li key={result}>{result}</li>
75+
))}
76+
</ul>
77+
) : (
78+
<p>
79+
{'No locked usernames matched your search.'}
80+
</p>
81+
)}
82+
</>
83+
) : null}
84+
</form>
85+
</div>
86+
</Layout>
87+
);
88+
89+
export default LockedUsernameSearch;

root/layout/components/TopMenu.js

+5
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ const AdminMenu = ({user}: UserProp) => (
167167
<li>
168168
<a href="/admin/email-search">{l('Email Search')}</a>
169169
</li>
170+
<li>
171+
<a href="/admin/locked-usernames/search">
172+
{l('Locked Username Search')}
173+
</a>
174+
</li>
170175
</>
171176
) : null}
172177
</ul>

root/server/components.js

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ module.exports = {
3838
'admin/EditBanner': require('../admin/EditBanner'),
3939
'admin/EmailSearch': require('../admin/EmailSearch'),
4040
'admin/IpLookup': require('../admin/IpLookup'),
41+
'admin/LockedUsernameSearch': require('../admin/LockedUsernameSearch'),
4142
'admin/attributes/Attribute': require('../admin/attributes/Attribute'),
4243
'admin/attributes/CannotRemoveAttribute': require('../admin/attributes/CannotRemoveAttribute'),
4344
'admin/attributes/Index': require('../admin/attributes/Index'),

t/lib/t/MusicBrainz/Server/Controller/UnconfirmedEmailAddresses.pm

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ test 'Paths that allow browsing without a confirmed email address' => sub {
9090
'Controller::Admin::edit_user',
9191
'Controller::Admin::email_search',
9292
'Controller::Admin::ip_lookup',
93+
'Controller::Admin::locked_username_search',
9394
'Controller::Ajax::filter_artist_recordings_form',
9495
'Controller::Ajax::filter_artist_release_groups_form',
9596
'Controller::Ajax::filter_artist_releases_form',

0 commit comments

Comments
 (0)