Skip to content

Commit

Permalink
MBS-11599: Allow adding URL relationships to genre pages
Browse files Browse the repository at this point in the history
Not implementing a full relationship editor yet since that
has no easy React implementation and mwiencek is working
on that for all entities, by which point it should be trivial
to also extend it to genres. In the meantime, being able to link
genres to Wikidata, Rate Your Music and whatnot seems like a great
first step.

We do need a "mock" relationship editor section for hidden inputs
to be attached to, though, so one is added.
  • Loading branch information
reosarevok committed May 14, 2022
1 parent 5484c0e commit 0824d5f
Show file tree
Hide file tree
Showing 17 changed files with 191 additions and 73 deletions.
3 changes: 2 additions & 1 deletion entities.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@
"edit_table": true,
"mbid": {
"indexable": true,
"multiple": false
"multiple": false,
"relatable": "overview"
},
"merging": false,
"model": "Genre",
Expand Down
2 changes: 2 additions & 0 deletions lib/MusicBrainz/Server/Controller/Genre.pm
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ use MusicBrainz::Server::Entity::Util::JSON qw( to_json_array );
with 'MusicBrainz::Server::Controller::Role::Load' => {
model => 'Genre',
entity_name => 'genre',
relationships => { cardinal => ['show', 'edit'], default => ['url'] },
};
with 'MusicBrainz::Server::Controller::Role::LoadWithRowID';
with 'MusicBrainz::Server::Controller::Role::Annotation';
with 'MusicBrainz::Server::Controller::Role::Details';
with 'MusicBrainz::Server::Controller::Role::EditListing';
with 'MusicBrainz::Server::Controller::Role::EditRelationships';

use MusicBrainz::Server::Constants qw(
$EDIT_GENRE_CREATE
Expand Down
11 changes: 10 additions & 1 deletion lib/MusicBrainz/Server/Controller/Role/EditRelationships.pm
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,23 @@ role {
my $seeded_relationships = get_seeded_relationships($c, $source_type, $source);
my @link_type_tree = $c->model('LinkType')->get_full_tree;
my @link_attribute_types = $c->model('LinkAttributeType')->get_all;
my $type_info = build_type_info(
$c,
qr/(^$source_type-|-$source_type$)/,
@link_type_tree,
);

$c->stash(
seeded_relationships => $c->json->encode(to_json_array($seeded_relationships)),
source_entity => $c->json->encode($source_entity),
attr_info => $c->json->encode(\@link_attribute_types),
type_info => $c->json->encode(build_type_info($c, qr/(^$source_type-|-$source_type$)/, @link_type_tree)),
type_info => $c->json->encode($type_info),
);

$c->stash->{component_props}{sourceEntity} = $source_entity;
$c->stash->{component_props}{attrInfo} = to_json_array(\@link_attribute_types);
$c->stash->{component_props}{typeInfo} = $type_info;

my $post_creation = delete $opts{post_creation};

$opts{post_creation} = sub {
Expand Down
1 change: 1 addition & 0 deletions lib/MusicBrainz/Server/Data/Genre.pm
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ sub can_delete { 1 }
sub delete {
my ($self, $genre_id) = @_;

$self->c->model('Relationship')->delete_entities('genre', $genre_id);
$self->annotation->delete($genre_id);
$self->delete_returning_gids($genre_id);
return;
Expand Down
1 change: 1 addition & 0 deletions lib/MusicBrainz/Server/Entity/Genre.pm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ extends 'MusicBrainz::Server::Entity::CoreEntity';
with 'MusicBrainz::Server::Entity::Role::Annotation';
with 'MusicBrainz::Server::Entity::Role::Comment';
with 'MusicBrainz::Server::Entity::Role::LastUpdate';
with 'MusicBrainz::Server::Entity::Role::Linkable';

sub entity_type { 'genre' }

Expand Down
1 change: 1 addition & 0 deletions lib/MusicBrainz/Server/Form/Genre.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use HTML::FormHandler::Moose;

extends 'MusicBrainz::Server::Form';
with 'MusicBrainz::Server::Form::Role::Edit';
with 'MusicBrainz::Server::Form::Role::Relationships';

has '+name' => ( default => 'edit-genre' );

Expand Down
23 changes: 20 additions & 3 deletions root/genre/CreateGenre.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,38 @@
import * as React from 'react';

import Layout from '../layout';
import * as manifest from '../static/manifest';
import GenreEditForm from '../static/scripts/genre/components/GenreEditForm';

import GenreEditForm from './GenreEditForm';
import type {GenreFormT} from './types';

type Props = {
+$c: CatalystContextT,
+attrInfo: LinkAttrTypeOptionsT,
+form: GenreFormT,
+sourceEntity: {entityType: 'genre'},
+typeInfo: LinkTypeOptionsT,
};

const CreateGenre = ({$c, form}: Props): React.Element<typeof Layout> => (
const CreateGenre = ({
$c,
attrInfo,
form,
sourceEntity,
typeInfo,
}: Props): React.Element<typeof Layout> => (
<Layout fullWidth title={l('Add a new genre')}>
<div id="content">
<h1>{l('Add a new genre')}</h1>
<GenreEditForm $c={$c} form={form} />
<GenreEditForm
$c={$c}
attrInfo={attrInfo}
form={form}
sourceEntity={sourceEntity}
typeInfo={typeInfo}
/>
</div>
{manifest.js('genre/components/GenreEditForm', {async: 'async'})}
</Layout>
);

Expand Down
19 changes: 17 additions & 2 deletions root/genre/EditGenre.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,43 @@

import * as React from 'react';

import GenreEditForm from './GenreEditForm';
import * as manifest from '../static/manifest';
import GenreEditForm from '../static/scripts/genre/components/GenreEditForm';

import GenreLayout from './GenreLayout';
import type {GenreFormT} from './types';

type Props = {
+$c: CatalystContextT,
+attrInfo: LinkAttrTypeOptionsT,
+entity: GenreT,
+form: GenreFormT,
+sourceEntity: GenreT,
+typeInfo: LinkTypeOptionsT,
};

const EditGenre = ({
$c,
attrInfo,
entity,
form,
sourceEntity,
typeInfo,
}: Props): React.Element<typeof GenreLayout> => (
<GenreLayout
entity={entity}
fullWidth
page="edit"
title={l('Edit genre')}
>
<GenreEditForm $c={$c} form={form} />
<GenreEditForm
$c={$c}
attrInfo={attrInfo}
form={form}
sourceEntity={sourceEntity}
typeInfo={typeInfo}
/>
{manifest.js('genre/components/GenreEditForm', {async: 'async'})}
</GenreLayout>
);

Expand Down
49 changes: 0 additions & 49 deletions root/genre/GenreEditForm.js

This file was deleted.

2 changes: 2 additions & 0 deletions root/genre/GenreIndex.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import * as React from 'react';

import Relationships from '../components/Relationships';
import Annotation from '../static/scripts/common/components/Annotation';
import TagLink from '../static/scripts/common/components/TagLink';
import * as manifest from '../static/manifest';
Expand Down Expand Up @@ -42,6 +43,7 @@ const GenreIndex = ({
entity={genre}
numberOfRevisions={numberOfRevisions}
/>
<Relationships source={genre} />
{manifest.js('genre/index', {async: 'async'})}
</GenreLayout>
);
Expand Down
3 changes: 3 additions & 0 deletions root/layout/components/sidebar/GenreSidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as React from 'react';
import {CatalystContext} from '../../../context';
import {isRelationshipEditor}
from '../../../static/scripts/common/utility/privileges';
import ExternalLinks from '../ExternalLinks';

import AnnotationLinks from './AnnotationLinks';
import EditLinks from './EditLinks';
Expand All @@ -27,6 +28,8 @@ const GenreSidebar = ({genre}: Props): React.Element<'div'> => {

return (
<div id="sidebar">
<ExternalLinks empty entity={genre} />

{isRelationshipEditor($c.user) ? (
<EditLinks entity={genre}>
<AnnotationLinks entity={genre} />
Expand Down
18 changes: 14 additions & 4 deletions root/static/scripts/edit/externalLinks.js
Original file line number Diff line number Diff line change
Expand Up @@ -1673,15 +1673,21 @@ function isMusicBrainz(url) {
type InitialOptionsT = {
errorObservable?: (boolean) => void,
mountPoint: Element,
sourceData: CoreEntityT,
sourceData: CoreEntityT | {
+entityType: CoreEntityTypeT,
+id?: void,
+relationships?: void,
},
};

type SeededUrlShape = {
+link_type_id?: string,
+text?: string,
};

MB.createExternalLinksEditor = function (options: InitialOptionsT) {
export function createExternalLinksEditor(
options: InitialOptionsT,
): React$ElementRef<typeof ExternalLinksEditor> {
const sourceData = options.sourceData;
const sourceType = sourceData.entityType;
const entityTypes = [sourceType, 'url'].sort().join('-');
Expand Down Expand Up @@ -1762,11 +1768,15 @@ MB.createExternalLinksEditor = function (options: InitialOptionsT) {
errorObservable={errorObservable}
initialLinks={initialLinks}
isNewEntity={!sourceData.id}
ref={(instance) => {
// $FlowIgnore
MB.sourceExternalLinksEditor = instance;
}}
sourceType={sourceData.entityType}
typeOptions={typeOptions}
/>,
options.mountPoint,
);
};
}

export const createExternalLinksEditor = MB.createExternalLinksEditor;
MB.createExternalLinksEditor = createExternalLinksEditor;
94 changes: 94 additions & 0 deletions root/static/scripts/genre/components/GenreEditForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* @flow
* Copyright (C) 2019 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
*/

import * as React from 'react';

import EnterEdit from '../../../../components/EnterEdit';
import EnterEditNote from '../../../../components/EnterEditNote';
import FormRowTextLong from '../../../../components/FormRowTextLong';
import type {GenreFormT} from '../../../../genre/types';
import {createExternalLinksEditor} from '../../edit/externalLinks';
import {exportTypeInfo} from '../../relationship-editor/common/viewModel';
import {prepareSubmission} from '../../relationship-editor/generic';

type Props = {
+$c: CatalystContextT,
+attrInfo: LinkAttrTypeOptionsT,
+form: GenreFormT,
+sourceEntity: GenreT | {entityType: 'genre'},
+typeInfo: LinkTypeOptionsT,
};

const GenreEditForm = ({
$c,
attrInfo,
form,
sourceEntity,
typeInfo,
}: Props): React.Element<'form'> => {
const externalLinksEditorContainerRef = React.useRef(null);

const handleSubmit = () => {
console.log('preparing submission');
prepareSubmission('edit-genre');
};

React.useEffect(() => {
exportTypeInfo(typeInfo, attrInfo);
invariant(externalLinksEditorContainerRef.current != null);
createExternalLinksEditor({
mountPoint: externalLinksEditorContainerRef.current,
sourceData: sourceEntity,
});
}, [attrInfo, sourceEntity, typeInfo]);

return (
<form
action={$c.req.uri}
className="edit-genre"
method="post"
onSubmit={handleSubmit}
>
<div className="half-width">
<fieldset>
<legend>{l('Genre details')}</legend>
<FormRowTextLong
field={form.field.name}
label={addColonText(l('Name'))}
required
uncontrolled
/>
<FormRowTextLong
field={form.field.comment}
label={addColonText(l('Disambiguation'))}
uncontrolled
/>
</fieldset>

<div data-form-name="edit-genre" id="relationship-editor" />

<fieldset>
<legend>{l('External Links')}</legend>
<div
id="external-links-editor-container"
ref={externalLinksEditorContainerRef}
/>
</fieldset>

<EnterEditNote field={form.field.edit_note} />
<EnterEdit form={form} />
</div>
</form>
);
};

export default (hydrate<Props>(
'div.genre-edit-form',
GenreEditForm,
): React.AbstractComponent<Props, void>);
2 changes: 2 additions & 0 deletions root/static/scripts/relationship-editor/common/viewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,5 @@ function editorMayEditTypes(type0, type1) {

return true;
}

export const exportTypeInfo = MB.relationshipEditor.exportTypeInfo;
Loading

0 comments on commit 0824d5f

Please sign in to comment.