Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MBS-13597: Add release filter to label index pages #3450

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
11 changes: 11 additions & 0 deletions lib/MusicBrainz/Server/Controller/Ajax.pm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use MusicBrainz::Server::FilterUtils qw(
create_artist_releases_form
create_artist_recordings_form
create_artist_works_form
create_label_releases_form
);

sub filter_artist_events_form : Local {
Expand Down Expand Up @@ -64,4 +65,14 @@ sub filter_artist_works_form : Local {
$c->res->content_type('application/json; charset=utf-8');
}

sub filter_label_releases_form : Local {
my ($self, $c) = @_;

my $label_id = $c->req->query_params->{label_id};
my $form = create_label_releases_form($c, $label_id);

$c->res->body(encode_json($form->TO_JSON));
$c->res->content_type('application/json; charset=utf-8');
}

1;
36 changes: 1 addition & 35 deletions lib/MusicBrainz/Server/Controller/Artist.pm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ with 'MusicBrainz::Server::Controller::Role::Annotation';
with 'MusicBrainz::Server::Controller::Role::Alias';
with 'MusicBrainz::Server::Controller::Role::Details';
with 'MusicBrainz::Server::Controller::Role::EditListing';
with 'MusicBrainz::Server::Controller::Role::Filter';
with 'MusicBrainz::Server::Controller::Role::IPI';
with 'MusicBrainz::Server::Controller::Role::ISNI';
with 'MusicBrainz::Server::Controller::Role::Rating';
Expand Down Expand Up @@ -891,41 +892,6 @@ sub edit_credit : Chained('credit') PathPart('edit') Edit {
);
}

=head2 process_filter

Utility function for dynamically loading the filter form.

=cut

sub process_filter
{
my ($self, $c, $create_form) = @_;

my %filter;
unless (exists $c->req->params->{'filter.cancel'}) {
my $cookie = $c->req->cookies->{filter};
my $has_filter_params = grep { /^filter\./ } keys %{ $c->req->params };
if ($has_filter_params || ($cookie && defined($cookie->value) && $cookie->value eq '1')) {
my $filter_form = $create_form->();
if ($c->form_submitted_and_valid($filter_form)) {
for my $name ($filter_form->filter_field_names) {
my $value = $filter_form->field($name)->value;
if ($value) {
$filter{$name} = $value;
}

}
$c->res->cookies->{filter} = { value => '1', path => '/' };
}
}
}
else {
$c->res->cookies->{filter} = { value => '', path => '/' };
}

return \%filter;
}

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2008 MetaBrainz Foundation
Expand Down
19 changes: 17 additions & 2 deletions lib/MusicBrainz/Server/Controller/Label.pm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ with 'MusicBrainz::Server::Controller::Role::Alias';
with 'MusicBrainz::Server::Controller::Role::Cleanup';
with 'MusicBrainz::Server::Controller::Role::Details';
with 'MusicBrainz::Server::Controller::Role::EditListing';
with 'MusicBrainz::Server::Controller::Role::Filter';
with 'MusicBrainz::Server::Controller::Role::IPI';
with 'MusicBrainz::Server::Controller::Role::ISNI';
with 'MusicBrainz::Server::Controller::Role::Rating';
Expand All @@ -52,8 +53,14 @@ use MusicBrainz::Server::Constants qw(
);
use MusicBrainz::Server::ControllerUtils::JSON qw( serialize_pager );
use Data::Page;
use MusicBrainz::Server::Data::Utils qw( is_special_label );
use MusicBrainz::Server::Data::Utils qw(
boolean_to_json
is_special_label
);
use MusicBrainz::Server::Entity::Util::JSON qw( to_json_array to_json_object );
use MusicBrainz::Server::FilterUtils qw(
create_label_releases_form
);
use MusicBrainz::Server::Translation qw( l );
use HTTP::Status qw( :constants );
use List::AllUtils qw( any );
Expand Down Expand Up @@ -124,8 +131,13 @@ sub show : PathPart('') Chained('load')

my $label = $c->stash->{label};

my %filter = %{ $self->process_filter($c, sub {
return create_label_releases_form($c, $label->id);
}) };
my $has_filter = %filter ? 1 : 0;

my $releases = $self->_load_paged($c, sub {
$c->model('Release')->find_by_label($label->id, shift, shift);
$c->model('Release')->find_by_label($label->id, shift, shift, filter => \%filter);
});

$c->model('ArtistCredit')->load(@$releases);
Expand Down Expand Up @@ -156,6 +168,9 @@ sub show : PathPart('') Chained('load')
}

my %props = (
ajaxFilterFormUrl => '' . $c->uri_for_action('/ajax/filter_label_releases_form', { label_id => $label->id }),
filterForm => to_json_object($c->stash->{filter_form}),
hasFilter => boolean_to_json($has_filter),
label => $label->TO_JSON,
numberOfRevisions => $c->stash->{number_of_revisions},
pager => serialize_pager($c->stash->{pager}),
Expand Down
51 changes: 51 additions & 0 deletions lib/MusicBrainz/Server/Controller/Role/Filter.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package MusicBrainz::Server::Controller::Role::Filter;
use Moose::Role;
use namespace::autoclean;

=head2 process_filter

Utility function for dynamically loading the filter form.

=cut

sub process_filter
{
my ($self, $c, $create_form) = @_;

my %filter;
unless (exists $c->req->params->{'filter.cancel'}) {
my $cookie = $c->req->cookies->{filter};
my $has_filter_params = grep { /^filter\./ } keys %{ $c->req->params };
if ($has_filter_params || ($cookie && defined($cookie->value) && $cookie->value eq '1')) {
my $filter_form = $create_form->();
if ($c->form_submitted_and_valid($filter_form)) {
for my $name ($filter_form->filter_field_names) {
my $value = $filter_form->field($name)->value;
if ($value) {
$filter{$name} = $value;
}

}
$c->res->cookies->{filter} = { value => '1', path => '/' };
}
}
}
else {
$c->res->cookies->{filter} = { value => '', path => '/' };
}

return \%filter;
}

no Moose::Role;
1;

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2025 MetaBrainz Foundation

This file is part of MusicBrainz, the open internet music database,
and is licensed under the GPL version 2, or (at your option) any
later version: http://www.gnu.org/licenses/gpl-2.0.txt

=cut
29 changes: 29 additions & 0 deletions lib/MusicBrainz/Server/Data/Release.pm
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,19 @@ sub find_artist_credits_by_artist
return $self->c->model('ArtistCredit')->find_by_ids($ids);
}

sub find_artist_credits_by_label
{
my ($self, $label_id) = @_;

my $query = 'SELECT DISTINCT rel.artist_credit
FROM release rel
JOIN release_label rl
ON rl.release = rel.id
WHERE rl.label = ?';
my $ids = $self->sql->select_single_column_array($query, $label_id);
return $self->c->model('ArtistCredit')->find_by_ids($ids);
}

sub find_labels_by_artist
{
my ($self, $artist_id) = @_;
Expand All @@ -253,6 +266,22 @@ sub find_labels_by_artist
return $self->c->model('Label')->get_by_ids_sorted_by_name(@$ids);
}

# This might seem stupid, but it helps filtering multi-label releases
sub find_labels_by_label
{
my ($self, $label_id) = @_;

my $query = 'SELECT DISTINCT rl.label
FROM release_label rl
WHERE rl.release IN (
SELECT DISTINCT rl2.release
FROM release_label rl2
WHERE rl2.label = ?
)';
my $ids = $self->sql->select_single_column_array($query, $label_id);
return $self->c->model('Label')->get_by_ids_sorted_by_name(@$ids);
}

sub find_by_area {
my ($self, $area_id, $limit, $offset) = @_;
my $query = 'SELECT ' . $self->_columns . '
Expand Down
29 changes: 24 additions & 5 deletions lib/MusicBrainz/Server/FilterUtils.pm
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ our @EXPORT_OK = qw(
create_artist_releases_form
create_artist_recordings_form
create_artist_works_form
create_label_releases_form
);

sub create_artist_events_form {
Expand Down Expand Up @@ -53,7 +54,7 @@ sub create_artist_releases_form {
$c->model('Release')->find_labels_by_artist($artist_id);
$form_args{statuses} = [$c->model('ReleaseStatus')->get_all];

return $c->form(filter_form => 'Filter::Release', %form_args);
return $c->form(filter_form => 'Filter::ReleaseForArtist', %form_args);
}

sub create_artist_recordings_form {
Expand All @@ -80,6 +81,22 @@ sub create_artist_works_form {
return $c->form(filter_form => 'Filter::Work', %form_args);
}

sub create_label_releases_form {
my ($c, $label_id) = @_;

my %form_args = (entity_type => 'release');

$form_args{artist_credits} =
$c->model('Release')->find_artist_credits_by_label($label_id);
preserve_selected_artist_credit($c, $form_args{artist_credits});
$form_args{countries} = [$c->model('CountryArea')->get_all];
$form_args{labels} =
$c->model('Release')->find_labels_by_label($label_id);
$form_args{statuses} = [$c->model('ReleaseStatus')->get_all];

return $c->form(filter_form => 'Filter::ReleaseForLabel', %form_args);
}

sub preserve_selected_artist_credit {
my ($c, $artist_credits) = @_;

Expand All @@ -88,10 +105,12 @@ sub preserve_selected_artist_credit {
# to preserve the intent of the filter (showing no results until the AC
# is possibly used again).
my $selected_artist_credit_id = $c->req->query_params->{'filter.artist_credit_id'};
unless (any { $_->id == $selected_artist_credit_id } @$artist_credits) {
my $artist_credit = $c->model('ArtistCredit')->get_by_id($selected_artist_credit_id);
if ($artist_credit) {
unshift @$artist_credits, $artist_credit;
if ($selected_artist_credit_id) {
unless (any { $_->id == $selected_artist_credit_id } @$artist_credits) {
my $artist_credit = $c->model('ArtistCredit')->get_by_id($selected_artist_credit_id);
if ($artist_credit) {
unshift @$artist_credits, $artist_credit;
}
}
}
}
Expand Down
10 changes: 0 additions & 10 deletions lib/MusicBrainz/Server/Form/Filter/Release.pm
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,6 @@ sub options_country_id {
];
}

sub options_label_id {
my ($self, $field) = @_;
return [
{ value => '-1', label => lp('[none]', 'release label') },
map +{ value => $_->id, label => $_->name },
@{ $self->labels },
];
}

sub options_status_id {
my ($self, $field) = @_;
return [
Expand All @@ -105,7 +96,6 @@ around TO_JSON => sub {
my $json = $self->$orig;
$json->{options_artist_credit_id} = $self->options_artist_credit_id;
$json->{options_country_id} = $self->options_country_id;
$json->{options_label_id} = $self->options_label_id;
$json->{options_status_id} = $self->options_status_id;
return $json;
};
Expand Down
27 changes: 27 additions & 0 deletions lib/MusicBrainz/Server/Form/Filter/ReleaseForArtist.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package MusicBrainz::Server::Form::Filter::ReleaseForArtist;
use strict;
use warnings;

use HTML::FormHandler::Moose;
use MusicBrainz::Server::Translation qw( lp );

extends 'MusicBrainz::Server::Form::Filter::Release';

sub options_label_id {
my ($self, $field) = @_;
return [
{ value => '-1', label => lp('[none]', 'release label') },
map +{ value => $_->id, label => $_->name },
@{ $self->labels },
];
}

around TO_JSON => sub {
my ($orig, $self) = @_;

my $json = $self->$orig;
$json->{options_label_id} = $self->options_label_id;
return $json;
};

1;
26 changes: 26 additions & 0 deletions lib/MusicBrainz/Server/Form/Filter/ReleaseForLabel.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package MusicBrainz::Server::Form::Filter::ReleaseForLabel;
use strict;
use warnings;

use HTML::FormHandler::Moose;
use MusicBrainz::Server::Translation qw( lp );

extends 'MusicBrainz::Server::Form::Filter::Release';

sub options_label_id {
my ($self, $field) = @_;
return [
map +{ value => $_->id, label => $_->name },
@{ $self->labels },
];
}

around TO_JSON => sub {
my ($orig, $self) = @_;

my $json = $self->$orig;
$json->{options_label_id} = $self->options_label_id;
return $json;
};

1;
18 changes: 17 additions & 1 deletion root/label/LabelIndex.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import manifest from '../static/manifest.mjs';
import Annotation from '../static/scripts/common/components/Annotation.js';
import DescriptiveLink
from '../static/scripts/common/components/DescriptiveLink.js';
import Filter from '../static/scripts/common/components/Filter.js';
import {type ReleaseFilterT}
from '../static/scripts/common/components/FilterForm.js';
import WikipediaExtract
from '../static/scripts/common/components/WikipediaExtract.js';
import commaOnlyList from '../static/scripts/common/i18n/commaOnlyList.js';
Expand All @@ -28,7 +31,10 @@ import {returnToCurrentPage} from '../utility/returnUri.js';
import LabelLayout from './LabelLayout.js';

component LabelIndex(
ajaxFilterFormUrl: string,
eligibleForCleanup: boolean,
filterForm: ?ReleaseFilterT,
hasFilter: boolean,
label: LabelT,
numberOfRevisions: number,
pager: PagerT,
Expand Down Expand Up @@ -68,6 +74,12 @@ component LabelIndex(
entity={label}
/>
<h2 className="releases">{l('Releases')}</h2>

<Filter
ajaxFormUrl={ajaxFilterFormUrl}
initialFilterForm={filterForm}
/>

{releases?.length ? (
<form
action={'/release/merge_queue?' + returnToCurrentPage($c)}
Expand All @@ -87,7 +99,11 @@ component LabelIndex(
) : null}
</form>
) : (
<p>{l('This label does not have any releases.')}</p>
<p>
{hasFilter
? l('No releases found that match this search.')
: l('This label does not have any releases.')}
</p>
)}
{manifest('label/index', {async: 'async'})}
</LabelLayout>
Expand Down
Loading