Skip to content

Commit

Permalink
MBS-13770: Add new Admin approval/rejection votes
Browse files Browse the repository at this point in the history
This adds two special vote types ("Admin approval" and "Admin rejection")
that will be used in a subsequent commit for a feature that allows admins
to immediately accept or reject any edit. These are added first in
a separate commit so that it can be released on production while
the main part of the new feature is released on beta,
avoiding any temporary production ISEs when encountering the new vote types.

"Admin approval" is treated as an approval for icons and headers, while
"Admin rejection" is treated as a No vote. The new vote types are searchable
in edit searches, their stats can be seen on admin profiles, etc.
  • Loading branch information
reosarevok committed Jan 6, 2025
1 parent 568132b commit f803e5f
Show file tree
Hide file tree
Showing 16 changed files with 103 additions and 24 deletions.
10 changes: 6 additions & 4 deletions lib/MusicBrainz/Server/Constants.pm
Original file line number Diff line number Diff line change
Expand Up @@ -341,10 +341,12 @@ Readonly our $SCRIPT_FREQUENCY_UNCOMMON => 2;
Readonly our $SCRIPT_FREQUENCY_OTHER => 3;
Readonly our $SCRIPT_FREQUENCY_FREQUENT => 4;

Readonly our $VOTE_ABSTAIN => -1;
Readonly our $VOTE_NO => 0;
Readonly our $VOTE_YES => 1;
Readonly our $VOTE_APPROVE => 2;
Readonly our $VOTE_ABSTAIN => -1;
Readonly our $VOTE_NO => 0;
Readonly our $VOTE_YES => 1;
Readonly our $VOTE_APPROVE => 2;
Readonly our $VOTE_ADMIN_APPROVE => 3;
Readonly our $VOTE_ADMIN_REJECT => 4;

Readonly our $STATUS_OPEN => 1;
Readonly our $STATUS_APPLIED => 2;
Expand Down
2 changes: 2 additions & 0 deletions lib/MusicBrainz/Server/Controller/User/Edits.pm
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@ sub votes : Chained('/user/load') PathPart('votes') RequireAuth HiddenOnMirrors
'conditions.0.args.1' => $VOTE_NO,
'conditions.0.args.2' => $VOTE_YES,
'conditions.0.args.3' => $VOTE_APPROVE,
'conditions.0.args.4' => $VOTE_ADMIN_APPROVE,
'conditions.0.args.5' => $VOTE_ADMIN_REJECT,
};

$c->stash(
Expand Down
9 changes: 7 additions & 2 deletions lib/MusicBrainz/Server/Data/Edit.pm
Original file line number Diff line number Diff line change
Expand Up @@ -934,8 +934,13 @@ sub insert_votes_and_notes {
my @votes = @{ $data{votes} || [] };
my @notes = @{ $data{notes} || [] };

# Filter out approvals, they can only be entered via the approve method
@votes = grep { $_->{vote} != $VOTE_APPROVE } @votes;
# Filter out approvals (they can only be entered via the approve method)
# and admin votes (they too have their own separate mechanisms)
@votes = grep {
$_->{vote} != $VOTE_APPROVE &&
$_->{vote} != $VOTE_ADMIN_APPROVE &&
$_->{vote} != $VOTE_ADMIN_REJECT
} @votes;

Sql::run_in_transaction(sub {
$self->c->model('Vote')->enter_votes($editor, \@votes);
Expand Down
22 changes: 18 additions & 4 deletions lib/MusicBrainz/Server/Data/Statistics.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1553,10 +1553,12 @@ my %stats = (
my %dist = map { @$_ } @$data;

+{
'count.vote.yes' => $dist{$VOTE_YES} || 0,
'count.vote.no' => $dist{$VOTE_NO} || 0,
'count.vote.abstain' => $dist{$VOTE_ABSTAIN} || 0,
'count.vote.approve' => $dist{$VOTE_APPROVE} || 0,
'count.vote.yes' => $dist{$VOTE_YES} || 0,
'count.vote.no' => $dist{$VOTE_NO} || 0,
'count.vote.abstain' => $dist{$VOTE_ABSTAIN} || 0,
'count.vote.approve' => $dist{$VOTE_APPROVE} || 0,
'count.vote.admin_approve' => $dist{$VOTE_ADMIN_APPROVE} || 0,
'count.vote.admin_reject' => $dist{$VOTE_ADMIN_REJECT} || 0,
};
},
NONREPLICATED => 1,
Expand All @@ -1579,6 +1581,18 @@ my %stats = (
PREREQ_ONLY => 1,
NONREPLICATED => 1,
},
'count.vote.admin_approve' => {
DESC => 'Count of admin approvals',
PREREQ => [qw[ count.vote.yes ]],
PREREQ_ONLY => 1,
NONREPLICATED => 1,
},
'count.vote.admin_reject' => {
DESC => 'Count of admin rejections',
PREREQ => [qw[ count.vote.yes ]],
PREREQ_ONLY => 1,
NONREPLICATED => 1,
},
'count.vote.perday' => {
DESC => 'Count of votes per day',
SQL => q{SELECT count(id) FROM vote
Expand Down
8 changes: 8 additions & 0 deletions lib/MusicBrainz/Server/Data/Vote.pm
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ sub editor_statistics
? $self->summarize_votes($VOTE_APPROVE, $all_votes, $recent_votes)
: (),

# Show admin votes only if editor is an admin
$editor->is_account_admin ? (
$self->summarize_votes($VOTE_ADMIN_APPROVE, $all_votes, $recent_votes),
$self->summarize_votes($VOTE_ADMIN_REJECT, $all_votes, $recent_votes),
) : (),

# Add totals
{
name => l('Total'),
Expand All @@ -190,6 +196,8 @@ sub summarize_votes
$VOTE_NO => lp('No', 'vote'),
$VOTE_YES => lp('Yes', 'vote'),
$VOTE_APPROVE => lp('Approve', 'vote'),
$VOTE_ADMIN_APPROVE => lp('Admin approval', 'vote'),
$VOTE_ADMIN_REJECT => lp('Admin rejection', 'vote'),
);

return (
Expand Down
2 changes: 2 additions & 0 deletions lib/MusicBrainz/Server/Entity/Vote.pm
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ sub vote_name
$VOTE_NO => 'No',
$VOTE_YES => 'Yes',
$VOTE_APPROVE => 'Approve',
$VOTE_ADMIN_APPROVE => 'Admin approval',
$VOTE_ADMIN_REJECT => 'Admin rejection',
);
return $names{$self->vote};
}
Expand Down
2 changes: 1 addition & 1 deletion lib/MusicBrainz/Server/Types.pm
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ subtype AutoEditorElectionStatus,

subtype VoteOption,
as Int,
where { $_ >= $VOTE_ABSTAIN && $_ <= $VOTE_APPROVE };
where { $_ >= $VOTE_ABSTAIN && $_ <= $VOTE_ADMIN_REJECT };

subtype EditStatus,
as Int,
Expand Down
2 changes: 2 additions & 0 deletions root/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export const EDIT_VOTE_ABSTAIN = -1;
export const EDIT_VOTE_NO = 0;
export const EDIT_VOTE_YES = 1;
export const EDIT_VOTE_APPROVE = 2;
export const EDIT_VOTE_ADMIN_APPROVE = 3;
export const EDIT_VOTE_ADMIN_REJECT = 4;

export const QUALITY_UNKNOWN = -1;
export const QUALITY_UNKNOWN_MAPPED = 1;
Expand Down
5 changes: 3 additions & 2 deletions root/edit/components/EditHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import * as React from 'react';
import RequestLogin from '../../components/RequestLogin.js';
import SubHeader from '../../components/SubHeader.js';
import VotingPeriod from '../../components/VotingPeriod.js';
import {EDIT_VOTE_APPROVE} from '../../constants.js';
import {EDIT_VOTE_ADMIN_APPROVE, EDIT_VOTE_APPROVE} from '../../constants.js';
import {CatalystContext} from '../../context.mjs';
import EditLink from '../../static/scripts/common/components/EditLink.js';
import EditorLink from '../../static/scripts/common/components/EditorLink.js';
Expand Down Expand Up @@ -62,7 +62,8 @@ component EditHeader(
? getVoteName(latestVoteForVoter.vote)
: null;
const editWasApproved = !edit.is_open && edit.votes.some(
(vote) => vote.vote === EDIT_VOTE_APPROVE,
(vote) => (vote.vote === EDIT_VOTE_APPROVE ||
vote.vote === EDIT_VOTE_ADMIN_APPROVE),
);
const showVoteTally = latestVoteForEditor || isEditEditor || !edit.is_open;

Expand Down
7 changes: 6 additions & 1 deletion root/edit/components/EditNote.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import * as React from 'react';

import {
EDIT_VOTE_ADMIN_APPROVE,
EDIT_VOTE_ADMIN_REJECT,
EDIT_VOTE_APPROVE,
EDIT_VOTE_NO,
EDIT_VOTE_YES,
Expand All @@ -20,6 +22,7 @@ import EditorLink from '../../static/scripts/common/components/EditorLink.js';
import bracketed from '../../static/scripts/common/utility/bracketed.js';
import {isAccountAdmin, isAddingNotesDisabled}
from '../../static/scripts/common/utility/privileges.js';
import {kebabCase} from '../../static/scripts/common/utility/strings.js';
import getVoteName from '../../static/scripts/edit/utility/getVoteName.js';
import formatUserDate from '../../utility/formatUserDate.js';
import parseIsoDate from '../../utility/parseIsoDate.js';
Expand All @@ -34,7 +37,7 @@ function returnVoteClass(vote: ?VoteT, isOwner: boolean) {
let className = '';

if (vote) {
className = getVoteName(vote.vote);
className = kebabCase(getVoteName(vote.vote));
}

if (isOwner) {
Expand Down Expand Up @@ -68,7 +71,9 @@ component EditNote(
));
const showVotingIcon = lastRelevantVote && (
lastRelevantVote.vote === EDIT_VOTE_APPROVE ||
lastRelevantVote.vote === EDIT_VOTE_ADMIN_APPROVE ||
lastRelevantVote.vote === EDIT_VOTE_NO ||
lastRelevantVote.vote === EDIT_VOTE_ADMIN_REJECT ||
lastRelevantVote.vote === EDIT_VOTE_YES
);

Expand Down
14 changes: 9 additions & 5 deletions root/edit/search_macros.tt
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,13 @@

<select name="args" multiple="multiple" size="5">
[%- FOR item=[ [ 2, lp('Approve', 'vote') ]
, [ 1, lp('Yes', 'vote') ]
, [ 0, lp('No', 'vote') ]
, [ -1, lp('Abstain', 'vote') ]
, [ 'no', l('No vote') ]
] %]
, [ 1, lp('Yes', 'vote') ]
, [ 0, lp('No', 'vote') ]
, [ -1, lp('Abstain', 'vote') ]
, [ 4, lp('Admin rejection', 'vote') ]
, [ 3, lp('Admin approval', 'vote') ]
, [ 'no', l('No vote') ]
] %]
<option value="[% html_escape(item.0) %]"
[%~ ' selected="selected"' IF field_contents.find_argument(item.0).defined ~%]
>[% html_escape(item.1) %]</option>
Expand All @@ -310,6 +312,8 @@
, [ 1, lp('Yes', 'vote') ]
, [ 0, lp('No', 'vote') ]
, [ -1, lp('Abstain', 'vote') ]
, [ 4, lp('Admin rejection', 'vote') ]
, [ 3, lp('Admin approval', 'vote') ]
] %]
<option value="[% html_escape(item.0) %]"
[%~ ' selected="selected"' IF field_contents.vote == item.0 ~%]
Expand Down
6 changes: 6 additions & 0 deletions root/static/scripts/edit/utility/getVoteName.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

import {
EDIT_VOTE_ABSTAIN,
EDIT_VOTE_ADMIN_APPROVE,
EDIT_VOTE_ADMIN_REJECT,
EDIT_VOTE_APPROVE,
EDIT_VOTE_NO,
EDIT_VOTE_NONE,
Expand All @@ -21,6 +23,10 @@ export default function getVoteName(
switch (vote) {
case EDIT_VOTE_ABSTAIN:
return 'Abstain';
case EDIT_VOTE_ADMIN_APPROVE:
return 'Admin approval';
case EDIT_VOTE_ADMIN_REJECT:
return 'Admin rejection';
case EDIT_VOTE_APPROVE:
return 'Approve';
case EDIT_VOTE_YES:
Expand Down
8 changes: 4 additions & 4 deletions root/static/styles/edit.less
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ table.vote-tally {
}

/* Adding voting icons to avatars */
h3.Approve, h3.No, h3.Yes {
h3.approve, h3.no, h3.yes, h3.admin-approval, h3.admin-rejection {
position: relative;

.voting-icon {
Expand All @@ -170,7 +170,7 @@ table.vote-tally {
}
}

h3.Approve {
h3.approve, h3.admin-approval {
a img {
border-color: @yes-border;
}
Expand All @@ -180,7 +180,7 @@ table.vote-tally {
}
}

h3.No {
h3.no, h3.admin-rejection {
a img {
border-color: @no-border;
}
Expand All @@ -190,7 +190,7 @@ table.vote-tally {
}
}

h3.Yes {
h3.yes {
a img {
border-color: @yes-border;
}
Expand Down
16 changes: 16 additions & 0 deletions root/statistics/Index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,22 @@ component Index(
<td>{fc('vote.abstain')}</td>
<td>{fp('vote.abstain', 'vote')}</td>
</tr>
<tr>
<th />
<th colSpan="3">
{addColonText(lp_statistics('Admin approval', 'vote'))}
</th>
<td>{fc('vote.admin_approve')}</td>
<td>{fp('vote.admin_approve', 'vote')}</td>
</tr>
<tr>
<th />
<th colSpan="3">
{addColonText(lp_statistics('Admin rejection', 'vote'))}
</th>
<td>{fc('vote.admin_reject')}</td>
<td>{fp('vote.admin_reject', 'vote')}</td>
</tr>
<tr>
<th colSpan="4">{addColonText(l_statistics('Votes'))}</th>
<td>{fc('vote')}</td>
Expand Down
10 changes: 10 additions & 0 deletions root/statistics/stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,16 @@ const stats = {
color: '#00ffff',
label: l_statistics('Abstentions'),
},
'count.vote.admin_approve': {
category: 'edit-information',
color: '#00ffff',
label: l_statistics('Admin approvals'),
},
'count.vote.admin_reject': {
category: 'edit-information',
color: '#00ffff',
label: l_statistics('Admin rejections'),
},
'count.vote.approve': {
category: 'edit-information',
color: '#00ffff',
Expand Down
4 changes: 3 additions & 1 deletion root/types/vote.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ declare type VoteOptionT =
| -1 // Abstain
| 0 // No
| 1 // Yes
| 2; // Approve
| 2 // Approve
| 3 // Admin approve
| 4; // Admin reject
/* eslint-enable @stylistic/no-multi-spaces */

// MusicBrainz::Server::Entity::Vote::TO_JSON
Expand Down

0 comments on commit f803e5f

Please sign in to comment.