[mb-commits] branch, nes-70, created. WIP: Show ISWCS on work pages

MusicBrainz Git Server <[email protected]> Fri, 18 Jan 2013 15:35:00 +0000
Newsgroups gmane.comp.audio.musicbrainz.cvs
Message-ID <E1TwDy8-00043J-EL@wiley>
The branch, nes-70 has been created
        at  325368b75688470f3c92556420d8a3efff6f49fe (commit)

- Log -----------------------------------------------------------------
commit 325368b75688470f3c92556420d8a3efff6f49fe
Author: Oliver Charles <[email protected]>
Date:   Fri Jan 18 11:05:17 2013 +0000

    WIP: Show ISWCS on work pages

diff --git a/lib/MusicBrainz/Server/Controller/Work.pm b/lib/MusicBrainz/Server/Controller/Work.pm
index 9a2a193..c51ef75 100644
--- a/lib/MusicBrainz/Server/Controller/Work.pm
+++ b/lib/MusicBrainz/Server/Controller/Work.pm
@@ -40,7 +40,7 @@ after 'load' => sub
 
     my $work = $c->stash->{work};
     # $c->model('Work')->load_meta($work);
-    # $c->model('ISWC')->load_for_works($work);
+    $c->model('NES::ISWC')->load_for_works($work);
     if ($c->user_exists) {
         $c->model('Work')->rating->load_user_ratings($c->user->id, $work);
     }
diff --git a/lib/MusicBrainz/Server/Data/ISWC.pm b/lib/MusicBrainz/Server/Data/ISWC.pm
index 8d9f6c5..dc20a3a 100644
--- a/lib/MusicBrainz/Server/Data/ISWC.pm
+++ b/lib/MusicBrainz/Server/Data/ISWC.pm
@@ -4,7 +4,6 @@ use namespace::autoclean;
 
 use List::MoreUtils qw( uniq );
 use MusicBrainz::Server::Data::Utils qw(
-    object_to_ids
     placeholders
     query_to_list
 );
@@ -38,56 +37,6 @@ sub _entity_class
     return 'MusicBrainz::Server::Entity::ISWC';
 }
 
-=method find_by_work
-
-    find_by_work(@work_ids : Array[Integer])
-
-Find L<MusicBrainz::Server::Entity::ISWC> objects that are linked to specific
-works. The works are searched as a disjunction, so you will get all ISWCS linked
-to any of the inputs.
-
-Returns an array of ISWC objects.
-
-=cut
-
-sub find_by_works
-{
-    my ($self, @work_ids) = @_;
-
-    my $query = "SELECT ".$self->_columns."
-                   FROM ".$self->_table."
-                  WHERE work = any(?)
-                  ORDER BY iswc";
-
-    return query_to_list($self->c->sql, sub { $self->_new_from_row($_[0]) },
-                         $query, \@work_ids);
-}
-
-=method load_for_works
-
-    load_for_works(@works : Array[Work])
-
-Load ISWCs for an array of works, and nest the ISWC objects inside each
-respective work.
-
-=cut
-
-sub load_for_works
-{
-    my ($self, @works) = @_;
-    my %id_to_works = object_to_ids (uniq grep defined, @works);
-    my @ids = keys %id_to_works;
-    return unless @ids; # nothing to do
-    my @iswcs = $self->find_by_works(@ids);
-
-    foreach my $iswc (@iswcs) {
-        foreach my $work (@{ $id_to_works{$iswc->work_id} }) {
-            $work->add_iswc($iswc);
-            $iswc->work($work);
-        }
-    }
-}
-
 =method find_by_iswc
 
     find_by_iswc($iswc : Text)
diff --git a/lib/MusicBrainz/Server/Data/NES/ISWC.pm b/lib/MusicBrainz/Server/Data/NES/ISWC.pm
new file mode 100644
index 0000000..3d35b98
--- /dev/null
+++ b/lib/MusicBrainz/Server/Data/NES/ISWC.pm
@@ -0,0 +1,28 @@
+package MusicBrainz::Server::Data::NES::ISWC;
+use Moose;
+use namespace::autoclean;
+
+with 'MusicBrainz::Server::Data::Role::NES';
+
+sub load_for_works {
+    my ($self, @works) = @_;
+    my %works_by_revision_id = object_to_revision_ids(@works);
+    my %iswc_map = $self->request('/iswc/find-by-work', {
+        revisions => [
+            map { $_->revision_id } @works
+        ]
+    });
+    for my $key (keys %iswc_map) {
+        $works_by_revision_id{$key}->iswcs([
+            map {
+                MusicBrainz::Server::Entity::ISWC->new(
+                    iswc => $_
+                )
+            } @{ $iswc_map{$key} }
+        ]);
+    }
+    return;
+}
+
+__PACKAGE__->meta->make_immutable;
+1;
diff --git a/lib/MusicBrainz/Server/Data/Utils.pm b/lib/MusicBrainz/Server/Data/Utils.pm
index f7a4442..f02b703 100644
--- a/lib/MusicBrainz/Server/Data/Utils.pm
+++ b/lib/MusicBrainz/Server/Data/Utils.pm
@@ -39,6 +39,7 @@ our @EXPORT_OK = qw(
     merge_partial_date
     model_to_type
     object_to_ids
+    object_to_revision_ids
     order_by
     partial_date_to_hash
     placeholders
@@ -341,19 +342,30 @@ sub model_to_type
     return $map{$_[0]} || undef;
 }
 
-sub object_to_ids
+sub _object_to
 {
-    my @objects = @_;
+    my ($getter, @objects) = @_;
     my %ret;
     foreach my $object (@objects)
     {
-        $ret{$object->id} = [] unless $ret{$object->id};
-        push @{ $ret{$object->id} }, $object;
+        my $key = $getter->($object);
+        $ret{$key} = [] unless $ret{$key};
+        push @{ $ret{$key} }, $object;
     }
 
     return %ret;
 }
 
+sub object_to_ids
+{
+    _object_to(sub { shift->id }, @_);
+}
+
+sub object_to_revision_ids
+{
+    _object_to(sub { shift->revision_id }, @_);
+}
+
 sub order_by
 {
     my ($order, $default, $map) = @_;

commit 524f96b02aa7c60c87dbf4f7ec31d59474e51950
Author: Oliver Charles <[email protected]>
Date:   Fri Jan 18 11:03:37 2013 +0000

    Correctly consume 'primary-for-locale' when displaying aliases

diff --git a/lib/MusicBrainz/Server/Data/NES/Work.pm b/lib/MusicBrainz/Server/Data/NES/Work.pm
index 924a3d1..14c2b86 100644
--- a/lib/MusicBrainz/Server/Data/NES/Work.pm
+++ b/lib/MusicBrainz/Server/Data/NES/Work.pm
@@ -142,7 +142,8 @@ sub get_aliases {
                 type_id => $_->{type},
                 begin_date => MusicBrainz::Server::Entity::PartialDate->new($_->{begin_date}),
                 end_date => MusicBrainz::Server::Entity::PartialDate->new($_->{end_date}),
-                ended => $_->{ended}
+                ended => $_->{ended},
+                primary_for_locale => $_->{'primary-for-locale'}
             )
         } @$response
     ]

commit 4a3bcca371e40acdc9d7b7e42ee0c3fced3da57b
Author: Oliver Charles <[email protected]>
Date:   Thu Jan 17 08:38:56 2013 +0000

    Re-enable work type/language display for tags; remove dead code

diff --git a/lib/MusicBrainz/Server/Controller/Work.pm b/lib/MusicBrainz/Server/Controller/Work.pm
index 86e656e..9a2a193 100644
--- a/lib/MusicBrainz/Server/Controller/Work.pm
+++ b/lib/MusicBrainz/Server/Controller/Work.pm
@@ -4,10 +4,7 @@ use Moose;
 BEGIN { extends 'MusicBrainz::Server::Controller'; }
 
 use MusicBrainz::Server::Constants qw(
-    $EDIT_WORK_EDIT
     $EDIT_WORK_MERGE
-    $EDIT_WORK_ADD_ISWCS
-    $EDIT_WORK_REMOVE_ISWC
 );
 use MusicBrainz::Server::Entity::Work;
 use MusicBrainz::Server::Entity::Tree::Work;
@@ -65,7 +62,7 @@ sub show : PathPart('') Chained('load')
 
 # NES - originally:
 # for my $action (qw( relationships aliases tags details )) {
-for my $action (qw( aliases details )) {
+for my $action (qw( aliases tags details )) {
     after $action => sub {
         my ($self, $c) = @_;
         my $work = $c->stash->{work};
@@ -93,25 +90,6 @@ sub work_tree {
     );
 }
 
-# with 'MusicBrainz::Server::Controller::Role::Edit' => {
-#                 my @current_iswcs = $c->model('ISWC')->find_by_works($work->id);
-#                 my %current_iswcs = map { $_->iswc => 1 } @current_iswcs;
-#                 my @submitted = @{ $form->field('iswcs')->value };
-#                 my %submitted = map { $_ => 1 } @submitted;
-
-#                 my @added = grep { !exists($current_iswcs{$_}) } @submitted;
-#                 my @removed = grep { !exists($submitted{$_->iswc}) } @current_iswcs;
-
-#                 $self->_add_iswcs($c, $form, $work, @added) if @added;
-#                 $self->_remove_iswcs($c, $form, $work, @removed) if @removed;
-
-#                 if ((@added || @removed) && $c->stash->{makes_no_changes}) {
-#                     $c->stash( makes_no_changes => 0 );
-#                     $c->response->redirect(
-#                         $c->uri_for_action($self->action_for('show'), [ $work->gid ]));
-#                 }
-# };
-
 with 'MusicBrainz::Server::Controller::Role::Merge' => {
     edit_type => $EDIT_WORK_MERGE,
     confirmation_template => 'work/merge_confirm.tt',
@@ -156,37 +134,6 @@ sub create : Local Edit {
     );
 }
 
-sub _add_iswcs {
-    my ($self, $c, $form, $work, @iswcs) = @_;
-
-    $c->model('MB')->with_transaction(sub {
-        $self->_insert_edit(
-            $c, $form,
-            edit_type => $EDIT_WORK_ADD_ISWCS,
-            iswcs => [ map {
-                iswc => $_,
-                work => {
-                    id => $work->id,
-                    name => $work->name
-                }
-            }, @iswcs ]
-        );
-    });
-}
-
-sub _remove_iswcs {
-    my ($self, $c, $form, $work, @iswcs) = @_;
-
-    $c->model('MB')->with_transaction(sub {
-        $self->_insert_edit(
-            $c, $form,
-            edit_type => $EDIT_WORK_REMOVE_ISWC,
-            iswc => $_,
-            work => $work
-        );
-    }) for @iswcs;
-}
-
 1;
 
 =head1 COPYRIGHT
diff --git a/lib/MusicBrainz/Server/Data/Work.pm b/lib/MusicBrainz/Server/Data/Work.pm
index 606a59d..b9495a6 100644
--- a/lib/MusicBrainz/Server/Data/Work.pm
+++ b/lib/MusicBrainz/Server/Data/Work.pm
@@ -122,24 +122,6 @@ sub load
     load_subobjects($self, 'work', @objs);
 }
 
-sub insert
-{
-    my ($self, @works) = @_;
-    my %names = $self->find_or_insert_names(map { $_->{name} } @works);
-    my $class = $self->_entity_class;
-    my @created;
-    for my $work (@works)
-    {
-        my $row = $self->_hash_to_row($work, \%names);
-        $row->{gid} = $work->{gid} || generate_gid();
-        push @created, $class->new(
-            id => $self->sql->insert_row('work', $row, 'id'),
-            gid => $row->{gid}
-        );
-    }
-    return @works > 1 ? @created : $created[0];
-}
-
 sub update
 {
     my ($self, $work_id, $update) = @_;

commit c9153d25cf38b65f96107e82da2b2e8b63cc2260
Author: Oliver Charles <[email protected]>
Date:   Tue Jan 15 17:53:15 2013 +0000

    Added support for creating aliases

diff --git a/lib/MusicBrainz/Server/Controller/Role/Alias.pm b/lib/MusicBrainz/Server/Controller/Role/Alias.pm
index 5662c53..d144d4a 100644
--- a/lib/MusicBrainz/Server/Controller/Role/Alias.pm
+++ b/lib/MusicBrainz/Server/Controller/Role/Alias.pm
@@ -9,6 +9,9 @@ use MusicBrainz::Server::Constants qw(
     $EDIT_WORK_ADD_ALIAS $EDIT_WORK_DELETE_ALIAS $EDIT_WORK_EDIT_ALIAS
 );
 
+use MusicBrainz::Server::Entity::Alias;
+use MusicBrainz::Server::NES::Controller::Utils qw( create_update );
+
 my %model_to_edit_type = (
     add => {
         Artist => $EDIT_ARTIST_ADD_ALIAS,
@@ -30,7 +33,7 @@ my %model_to_edit_type = (
 my %model_to_search_hint_type_id = (
     Artist => 3,
     Label => 2,
-    Work => 2
+    'NES::Work' => 2
 );
 
 sub alias_type_model {
@@ -67,25 +70,30 @@ sub alias : Chained('load') PathPart('alias') CaptureArgs(1)
 sub add_alias : Chained('load') PathPart('add-alias') RequireAuth Edit
 {
     my ($self, $c) = @_;
-    my $type = $self->{entity_name};
-    my $entity = $c->stash->{ $type };
-    my $alias_model = $c->model( $self->{model} )->alias;
-    $self->edit_action($c,
-        form => 'Alias',
-        form_args => {
-            parent_id => $entity->id,
-            alias_model => $alias_model,
-            search_hint_type_id => $model_to_search_hint_type_id{ $self->{model} }
-        },
-        type => $model_to_edit_type{add}->{ $self->{model} },
-        edit_args => {
-            entity => $entity
-        },
-        item => {
-            name => $entity->name,
-            id => $entity->id
+    my $entity = $c->stash->{entity};
+    my $model_name = $self->{model};
+
+    create_update(
+        $self, $c,
+        build_form => sub {
+            $c->form(
+                form => 'Alias',
+                search_hint_type_id => $model_to_search_hint_type_id{ $model_name },
+                type_model => alias_type_model($model_name),
+                init_object => { revision_id => $entity->revision_id }
+            );
         },
-        on_creation => sub { $self->_redir_to_aliases($c) }
+        build_tree => sub {
+            my ($values, $revision) = @_;
+
+            my $aliases = $c->model($model_name)->get_aliases($revision);
+            $self->{tree_entity}->new(
+                aliases => [
+                    @$aliases,
+                    MusicBrainz::Server::Entity::Alias->new($values)
+                ]
+            );
+        }
     );
 }
 
diff --git a/lib/MusicBrainz/Server/Controller/Work.pm b/lib/MusicBrainz/Server/Controller/Work.pm
index 2e8eef8..86e656e 100644
--- a/lib/MusicBrainz/Server/Controller/Work.pm
+++ b/lib/MusicBrainz/Server/Controller/Work.pm
@@ -9,7 +9,14 @@ use MusicBrainz::Server::Constants qw(
     $EDIT_WORK_ADD_ISWCS
     $EDIT_WORK_REMOVE_ISWC
 );
+use MusicBrainz::Server::Entity::Work;
+use MusicBrainz::Server::Entity::Tree::Work;
 use MusicBrainz::Server::Translation qw( l );
+use MusicBrainz::Server::NES::Controller::Utils qw( create_edit create_update );
+
+__PACKAGE__->config(
+    tree_entity => 'MusicBrainz::Server::Entity::Tree::Work',
+);
 
 with 'MusicBrainz::Server::Controller::Role::Load' => {
     model       => 'NES::Work',
@@ -67,68 +74,22 @@ for my $action (qw( aliases details )) {
     };
 }
 
-sub create_edit {
-    my ($self, $c, %opts) = @_;
-
-    my $form = do {
-        my %args = (
-            ctx => $c,
-        );
-
-        $args{init_object} = $opts{subject}
-            if defined $opts{subject};
-
-        $c->form(form => $opts{form}, %args);
-    };
-
-    if ($c->form_posted && $form->submitted_and_valid($c->req->body_params)) {
-        my $work = do {
-            my $edit = $c->model('NES::Edit')->open;
-            $opts{on_post}->($form->values, $edit);
-        };
-
-        $c->response->redirect(
-            $c->uri_for_action($self->action_for('show'), [ $work->gid ]));
-    }
-    elsif (!$c->form_posted && %{ $c->req->query_params }) {
-        $form->process( params => $c->req->query_params );
-        $form->clear_errors;
-    }
-}
-
 sub edit : Chained('load') {
     my ($self, $c) = @_;
 
-    $self->create_edit(
-        $c,
+    create_update(
+        $self, $c,
         form => 'Work::Edit',
         subject => $c->stash->{work},
-        on_post => sub {
-            my ($values, $edit) = @_;
-
-            my $original_work = $c->model('NES::Work')->get_revision(
-                $values->{revision_id});
-
-            $c->model('NES::Work')->update(
-                $edit, $c->user, $values->{revision_id},
-                work_tree($values)
-            );
-
-            return $original_work;
-        }
+        build_tree => \&work_tree
     );
 }
 
 sub work_tree {
     my $values = shift;
-    return (
-        {
-            type => $values->{type_id},
-            language => $values->{language_id},
-            name => $values->{name},
-            comment => $values->{comment}
-        },
-        $values->{iswcs} // []
+    return MusicBrainz::Server::Entity::Tree::Work->new(
+        work => MusicBrainz::Server::Entity::Work->new($values),
+        iswcs => $values->{iswcs} // []
     );
 }
 
@@ -181,8 +142,8 @@ after 'merge' => sub
 sub create : Local Edit {
     my ($self, $c) = @_;
 
-    $self->create_edit(
-        $c,
+    create_edit(
+        $self, $c,
         form => 'Work',
         on_post => sub {
             my ($values, $edit) = @_;
@@ -191,13 +152,6 @@ sub create : Local Edit {
                 $edit, $c->user,
                 work_tree($values)
             );
-
-            # NES:
-            # my $privs = $c->user->privileges;
-            # if ($c->user->is_auto_editor &&
-            #     $form->field('as_auto_editor') &&
-            #     !$form->field('as_auto_editor')->value) {
-            # }
         }
     );
 }
diff --git a/lib/MusicBrainz/Server/Data/NES/Work.pm b/lib/MusicBrainz/Server/Data/NES/Work.pm
index 100fd4a..924a3d1 100644
--- a/lib/MusicBrainz/Server/Data/NES/Work.pm
+++ b/lib/MusicBrainz/Server/Data/NES/Work.pm
@@ -1,41 +1,98 @@
 package MusicBrainz::Server::Data::NES::Work;
 use Moose;
 
+use MusicBrainz::Server::Data::Utils qw( partial_date_to_hash );
 use MusicBrainz::Server::Entity::Work;
+use MusicBrainz::Server::WebService::Serializer::JSON::2::Utils qw( boolean );
 
 with 'MusicBrainz::Server::Data::Role::NES';
 
 sub create {
-    my ($self, $edit, $editor, $work, $iswcs) = @_;
+    my ($self, $edit, $editor, $tree) = @_;
+
+    $tree->aliases([]) unless $tree->aliases_set;
 
     my $response = $self->request('/work/create', {
         edit => $edit->id,
         editor => $editor->id,
-        _work_tree($work, $iswcs)
+        _work_tree($tree)
     });
 
     return $self->get_revision($response->{ref});
 }
 
 sub update {
-    my ($self, $edit, $editor, $base_revision, $work, $iswcs) = @_;
+    my ($self, $edit, $editor, $base_revision, $tree) = @_;
+
+    die 'Need a base revision' unless $base_revision;
+
+    my $final_tree = do {
+        if( $tree->work_set && $tree->aliases_set && $tree->iswcs_set ) {
+            $tree
+        }
+        else {
+            my $original_tree = $self->view_tree($base_revision);
+
+            $original_tree->work($tree->work)
+                if ($tree->work_set);
+
+            $original_tree->aliases($tree->aliases)
+                if ($tree->aliases_set);
+
+            $original_tree->iswcs($tree->iswcs)
+                if ($tree->iswcs_set);
+
+            $original_tree;
+        }
+    };
 
     my $response = $self->request('/work/update', {
         edit => $edit->id,
         editor => $editor->id,
-        revision => $base_revision,
-        _work_tree($work, $iswcs)
+        revision => $base_revision->revision_id,
+        _work_tree($final_tree)
     });
 
     return undef;
 }
 
+sub view_tree {
+    my ($self, $revision) = @_;
+
+    return MusicBrainz::Server::Entity::Tree::Work->new(
+        work => $revision,
+        iswcs => $self->get_iswcs($revision),
+        aliases => $self->get_aliases($revision)
+    );
+}
+
 sub _work_tree {
-    my ($work, $iswcs) = @_;
+    my $tree = shift;
+
     return (
-        work => $work,
+        work => do {
+            my $work = $tree->work;
+            {
+                 type => $work->type_id,
+                 language => $work->language_id,
+                 name => $work->name,
+                 comment => $work->comment
+            }
+        },
         iswcs => [
-            map +{ iswc => $_ }, @$iswcs
+            map +{ iswc => $_ }, @{ $tree->iswcs }
+        ],
+        aliases => [
+            map +{
+                name => $_->name,
+                'sort-name' => $_->sort_name,
+                'begin-date' => partial_date_to_hash($_->begin_date),
+                'end-date' => partial_date_to_hash($_->end_date),
+                ended => $_->ended,
+                'primary-for-locale' => boolean($_->primary_for_locale),
+                type => $_->type_id,
+                locale => $_->locale
+            }, @{ $tree->aliases }
         ]
     );
 }
@@ -91,5 +148,11 @@ sub get_aliases {
     ]
 }
 
+sub get_iswcs {
+    my ($self, $revision) = @_;
+    warn "Unimplemented";
+    return [];
+}
+
 __PACKAGE__->meta->make_immutable;
 1;
diff --git a/lib/MusicBrainz/Server/Entity/Alias.pm b/lib/MusicBrainz/Server/Entity/Alias.pm
index 36467df..1d29dec 100644
--- a/lib/MusicBrainz/Server/Entity/Alias.pm
+++ b/lib/MusicBrainz/Server/Entity/Alias.pm
@@ -18,12 +18,12 @@ has 'sort_name' => (
 
 has 'locale' => (
     is  => 'rw',
-    isa => 'Str',
+    isa => 'Maybe[Str]',
 );
 
 has 'type_id' => (
     is => 'rw',
-    isa => 'Int',
+    isa => 'Maybe[Int]',
 );
 
 has 'type' => (
diff --git a/lib/MusicBrainz/Server/Entity/Tree/Work.pm b/lib/MusicBrainz/Server/Entity/Tree/Work.pm
new file mode 100644
index 0000000..acf63f0
--- /dev/null
+++ b/lib/MusicBrainz/Server/Entity/Tree/Work.pm
@@ -0,0 +1,19 @@
+package MusicBrainz::Server::Entity::Tree::Work;
+use Moose;
+
+has work => (
+    is => 'rw',
+    predicate => 'work_set',
+);
+
+has aliases => (
+    is => 'rw',
+    predicate => 'aliases_set',
+);
+
+has iswcs => (
+    is => 'rw',
+    predicate => 'iswcs_set',
+);
+
+1;
diff --git a/lib/MusicBrainz/Server/Form/Alias.pm b/lib/MusicBrainz/Server/Form/Alias.pm
index c918b56..16ffbfc 100644
--- a/lib/MusicBrainz/Server/Form/Alias.pm
+++ b/lib/MusicBrainz/Server/Form/Alias.pm
@@ -43,14 +43,8 @@ has 'id' => (
     is  => 'rw',
 );
 
-has 'parent_id' => (
-    isa => 'Int',
-    is  => 'ro',
-    required => 1,
-);
-
-has 'alias_model' => (
-    isa => 'MusicBrainz::Server::Data::Alias',
+has 'type_model' => (
+    isa => 'MusicBrainz::Server::Data::AliasType',
     is  => 'ro',
     required => 1
 );
@@ -61,6 +55,10 @@ has search_hint_type_id => (
     required => 1
 );
 
+has_field revision_id => (
+    type => 'Integer',
+);
+
 sub edit_field_names {
     qw( name locale sort_name period.begin_date period.end_date
         type_id primary_for_locale )
@@ -94,7 +92,7 @@ sub options_locale {
 
 sub options_type_id {
     my $self = shift;
-    $self->_select_all($self->alias_model->parent->alias_type);
+    $self->_select_all($self->type_model);
 }
 
 sub validate_primary_for_locale {
@@ -115,13 +113,13 @@ after validate => sub {
         $sort_name_field->validate_field;
     }
 
-    if ($self->alias_model->exists({ name => $self->field('name')->value,
-                                     locale => $self->field('locale')->value,
-                                     type_id => $self->field('type_id')->value,
-                                     not_id => $self->init_object ? $self->init_object->{id} : undef,
-                                 })) {
-        $self->field('name')->add_error('This alias already exists');
-    }
+    # if ($self->alias_model->exists({ name => $self->field('name')->value,
+    #                                  locale => $self->field('locale')->value,
+    #                                  type_id => $self->field('type_id')->value,
+    #                                  not_id => $self->init_object ? $self->init_object->{id} : undef,
+    #                              })) {
+    #     $self->field('name')->add_error('This alias already exists');
+    # }
 };
 
 1;
diff --git a/lib/MusicBrainz/Server/NES/Controller/Utils.pm b/lib/MusicBrainz/Server/NES/Controller/Utils.pm
new file mode 100644
index 0000000..fe16de7
--- /dev/null
+++ b/lib/MusicBrainz/Server/NES/Controller/Utils.pm
@@ -0,0 +1,72 @@
+package MusicBrainz::Server::NES::Controller::Utils;
+use strict;
+use warnings;
+
+use Sub::Exporter -setup => {
+    exports => [qw( create_edit create_update )]
+};
+
+sub create_edit {
+    my ($controller, $c, %opts) = @_;
+
+    my $form = do {
+        if (my $build_form = $opts{build_form}) {
+            $build_form->()
+        }
+        else {
+            my $form = do {
+                my %args = (
+                    ctx => $c,
+                );
+
+                $args{init_object} = $opts{subject}
+                    if defined $opts{subject};
+
+                $c->form(form => $opts{form}, %args);
+            }
+        }
+    };
+
+    if ($c->form_posted && $form->submitted_and_valid($c->req->body_params)) {
+        my $work = do {
+            my $edit = $c->model('NES::Edit')->open;
+            $opts{on_post}->($form->values, $edit);
+        };
+
+        # NES:
+        # my $privs = $c->user->privileges;
+        # if ($c->user->is_auto_editor &&
+        #     $form->field('as_auto_editor') &&
+        #     !$form->field('as_auto_editor')->value) {
+        # }
+
+        $c->response->redirect(
+            $c->uri_for_action($controller->action_for('show'), [ $work->gid ]));
+    }
+    elsif (!$c->form_posted && %{ $c->req->query_params }) {
+        $form->process( params => $c->req->query_params );
+        $form->clear_errors;
+    }
+}
+
+sub create_update {
+    my ($controller, $c, %opts) = @_;
+    create_edit(
+        $controller, $c,
+        %opts,
+        on_post => sub {
+            my ($values, $edit) = @_;
+            my $revision = $c->model('NES::Work')->get_revision(
+                $values->{revision_id});
+
+            $c->model( $controller->{model} )->update(
+                $edit, $c->user, $revision,
+                $opts{build_tree}->($values, $revision)
+            );
+
+            return $revision;
+        }
+    );
+}
+
+1;
diff --git a/root/alias/edit_form.tt b/root/alias/edit_form.tt
index b95c158..d27091d 100644
--- a/root/alias/edit_form.tt
+++ b/root/alias/edit_form.tt
@@ -20,6 +20,7 @@
         [% form_row_checkbox(r, 'primary_for_locale', l('This is the primary alias for this locale')) %]
       </span>
       [% form_row_select(r, 'type_id', l('Type:')) %]
+      [% r.hidden(form.field('revision_id')) %]
     </fieldset>
 
     <fieldset>

commit 43ab95ad7dee3d6b6a73790b2e3ce378f2e8daa4
Author: Oliver Charles <[email protected]>
Date:   Tue Jan 15 16:26:18 2013 +0000

    Basic support for viewing work aliases

diff --git a/lib/MusicBrainz/Server/Controller/Role/Alias.pm b/lib/MusicBrainz/Server/Controller/Role/Alias.pm
index 2df415f..5662c53 100644
--- a/lib/MusicBrainz/Server/Controller/Role/Alias.pm
+++ b/lib/MusicBrainz/Server/Controller/Role/Alias.pm
@@ -33,14 +33,24 @@ my %model_to_search_hint_type_id = (
     Work => 2
 );
 
+sub alias_type_model {
+    my ($c, $parent) = @_;
+    my %type_model = (
+        'NES::Work' => 'Work'
+    );
+    return $c->model($type_model{$parent})->alias_type;
+}
+
 sub aliases : Chained('load') PathPart('aliases')
 {
     my ($self, $c) = @_;
 
     my $entity = $c->stash->{$self->{entity_name}};
-    my $m = $c->model($self->{model});
-    my $aliases = $m->alias->find_by_entity_id($entity->id);
-    $m->alias_type->load(@$aliases);
+    my $m = $self->{model};
+
+    my $aliases = $c->model($m)->get_aliases($entity);
+    alias_type_model($c, $m)->load(@$aliases);
+
     $c->stash(
         aliases => $aliases,
     );
diff --git a/lib/MusicBrainz/Server/Data/NES/Work.pm b/lib/MusicBrainz/Server/Data/NES/Work.pm
index 4d59a76..100fd4a 100644
--- a/lib/MusicBrainz/Server/Data/NES/Work.pm
+++ b/lib/MusicBrainz/Server/Data/NES/Work.pm
@@ -71,5 +71,25 @@ sub tags {
     $self->c->model('Work')->tags;
 }
 
+sub get_aliases {
+    my ($self, $work) = @_;
+    my $response = $self->request('/work/view-aliases', {
+        revision => $work->revision_id
+    });
+    return [
+        map {
+            MusicBrainz::Server::Entity::Alias->new(
+                name => $_->{name},
+                sort_name => $_->{'sort-name'},
+                locale => $_->{locale},
+                type_id => $_->{type},
+                begin_date => MusicBrainz::Server::Entity::PartialDate->new($_->{begin_date}),
+                end_date => MusicBrainz::Server::Entity::PartialDate->new($_->{end_date}),
+                ended => $_->{ended}
+            )
+        } @$response
+    ]
+}
+
 __PACKAGE__->meta->make_immutable;
 1;
diff --git a/lib/MusicBrainz/Server/Entity/Work.pm b/lib/MusicBrainz/Server/Entity/Work.pm
index 86b1dfd..fcafc9c 100644
--- a/lib/MusicBrainz/Server/Entity/Work.pm
+++ b/lib/MusicBrainz/Server/Entity/Work.pm
@@ -15,8 +15,8 @@ use MooseX::Types::Moose qw( ArrayRef Object Str );
 
 has 'type_id' => (
     is => 'rw',
-    isa => 'Int'
-    );
+    isa => 'Maybe[Int]'
+);
 
 has 'type' => (
     is => 'rw',
@@ -37,8 +37,8 @@ sub l_type_name
 
 has 'language_id' => (
     is => 'rw',
-    isa => 'Int'
-    );
+    isa => 'Maybe[Int]'
+);
 
 has 'language' => (
     is => 'rw',

commit 782d852cf868694897018618d8a0bc21bf0015a3
Author: Oliver Charles <[email protected]>
Date:   Tue Jan 15 15:52:21 2013 +0000

    Allow editing works

diff --git a/lib/MusicBrainz/Server/Controller/Work.pm b/lib/MusicBrainz/Server/Controller/Work.pm
index ae10e40..2e8eef8 100644
--- a/lib/MusicBrainz/Server/Controller/Work.pm
+++ b/lib/MusicBrainz/Server/Controller/Work.pm
@@ -67,36 +67,89 @@ for my $action (qw( aliases details )) {
     };
 }
 
-with 'MusicBrainz::Server::Controller::Role::Edit' => {
-    form           => 'Work',
-    edit_type      => $EDIT_WORK_EDIT,
-    edit_arguments => sub {
-        my ($self, $c, $work) = @_;
-
-        return (
-            post_creation => sub {
-                my ($edit, $form) = @_;
-
-                my @current_iswcs = $c->model('ISWC')->find_by_works($work->id);
-                my %current_iswcs = map { $_->iswc => 1 } @current_iswcs;
-                my @submitted = @{ $form->field('iswcs')->value };
-                my %submitted = map { $_ => 1 } @submitted;
-
-                my @added = grep { !exists($current_iswcs{$_}) } @submitted;
-                my @removed = grep { !exists($submitted{$_->iswc}) } @current_iswcs;
-
-                $self->_add_iswcs($c, $form, $work, @added) if @added;
-                $self->_remove_iswcs($c, $form, $work, @removed) if @removed;
-
-                if ((@added || @removed) && $c->stash->{makes_no_changes}) {
-                    $c->stash( makes_no_changes => 0 );
-                    $c->response->redirect(
-                        $c->uri_for_action($self->action_for('show'), [ $work->gid ]));
-                }
-            }
+sub create_edit {
+    my ($self, $c, %opts) = @_;
+
+    my $form = do {
+        my %args = (
+            ctx => $c,
         );
+
+        $args{init_object} = $opts{subject}
+            if defined $opts{subject};
+
+        $c->form(form => $opts{form}, %args);
+    };
+
+    if ($c->form_posted && $form->submitted_and_valid($c->req->body_params)) {
+        my $work = do {
+            my $edit = $c->model('NES::Edit')->open;
+            $opts{on_post}->($form->values, $edit);
+        };
+
+        $c->response->redirect(
+            $c->uri_for_action($self->action_for('show'), [ $work->gid ]));
     }
-};
+    elsif (!$c->form_posted && %{ $c->req->query_params }) {
+        $form->process( params => $c->req->query_params );
+        $form->clear_errors;
+    }
+}
+
+sub edit : Chained('load') {
+    my ($self, $c) = @_;
+
+    $self->create_edit(
+        $c,
+        form => 'Work::Edit',
+        subject => $c->stash->{work},
+        on_post => sub {
+            my ($values, $edit) = @_;
+
+            my $original_work = $c->model('NES::Work')->get_revision(
+                $values->{revision_id});
+
+            $c->model('NES::Work')->update(
+                $edit, $c->user, $values->{revision_id},
+                work_tree($values)
+            );
+
+            return $original_work;
+        }
+    );
+}
+
+sub work_tree {
+    my $values = shift;
+    return (
+        {
+            type => $values->{type_id},
+            language => $values->{language_id},
+            name => $values->{name},
+            comment => $values->{comment}
+        },
+        $values->{iswcs} // []
+    );
+}
+
+# with 'MusicBrainz::Server::Controller::Role::Edit' => {
+#                 my @current_iswcs = $c->model('ISWC')->find_by_works($work->id);
+#                 my %current_iswcs = map { $_->iswc => 1 } @current_iswcs;
+#                 my @submitted = @{ $form->field('iswcs')->value };
+#                 my %submitted = map { $_ => 1 } @submitted;
+
+#                 my @added = grep { !exists($current_iswcs{$_}) } @submitted;
+#                 my @removed = grep { !exists($submitted{$_->iswc}) } @current_iswcs;
+
+#                 $self->_add_iswcs($c, $form, $work, @added) if @added;
+#                 $self->_remove_iswcs($c, $form, $work, @removed) if @removed;
+
+#                 if ((@added || @removed) && $c->stash->{makes_no_changes}) {
+#                     $c->stash( makes_no_changes => 0 );
+#                     $c->response->redirect(
+#                         $c->uri_for_action($self->action_for('show'), [ $work->gid ]));
+#                 }
+# };
 
 with 'MusicBrainz::Server::Controller::Role::Merge' => {
     edit_type => $EDIT_WORK_MERGE,
@@ -128,36 +181,25 @@ after 'merge' => sub
 sub create : Local Edit {
     my ($self, $c) = @_;
 
-    my $form = $c->form( form => 'Work', ctx => $c );
-    if ($c->form_posted && $form->submitted_and_valid($c->req->body_params)) {
-        my $edit = $c->model('NES::Edit')->open;
-
-        my $values = $form->values;
-        my $work_revision = $c->model('NES::Work')->create(
-            $edit, $c->user,
-            {
-                type => $values->{type_id},
-                language => $values->{language_id},
-                name => $values->{name},
-                comment => $values->{comment}
-            },
-            $values->{iswcs} // []
-        );
-
-        # NES:
-        # my $privs = $c->user->privileges;
-        # if ($c->user->is_auto_editor &&
-        #     $form->field('as_auto_editor') &&
-        #     !$form->field('as_auto_editor')->value) {
-        # }
-
-        $c->response->redirect(
-            $c->uri_for_action($self->action_for('show'), [ $work_revision->gid ]));
-    }
-    elsif (!$c->form_posted && %{ $c->req->query_params }) {
-        $form->process( params => $c->req->query_params );
-        $form->clear_errors;
-    }
+    $self->create_edit(
+        $c,
+        form => 'Work',
+        on_post => sub {
+            my ($values, $edit) = @_;
+
+            return $c->model('NES::Work')->create(
+                $edit, $c->user,
+                work_tree($values)
+            );
+
+            # NES:
+            # my $privs = $c->user->privileges;
+            # if ($c->user->is_auto_editor &&
+            #     $form->field('as_auto_editor') &&
+            #     !$form->field('as_auto_editor')->value) {
+            # }
+        }
+    );
 }
 
 sub _add_iswcs {
diff --git a/lib/MusicBrainz/Server/Data/NES/Work.pm b/lib/MusicBrainz/Server/Data/NES/Work.pm
index e970a4f..4d59a76 100644
--- a/lib/MusicBrainz/Server/Data/NES/Work.pm
+++ b/lib/MusicBrainz/Server/Data/NES/Work.pm
@@ -11,13 +11,33 @@ sub create {
     my $response = $self->request('/work/create', {
         edit => $edit->id,
         editor => $editor->id,
+        _work_tree($work, $iswcs)
+    });
+
+    return $self->get_revision($response->{ref});
+}
+
+sub update {
+    my ($self, $edit, $editor, $base_revision, $work, $iswcs) = @_;
+
+    my $response = $self->request('/work/update', {
+        edit => $edit->id,
+        editor => $editor->id,
+        revision => $base_revision,
+        _work_tree($work, $iswcs)
+    });
+
+    return undef;
+}
+
+sub _work_tree {
+    my ($work, $iswcs) = @_;
+    return (
         work => $work,
         iswcs => [
             map +{ iswc => $_ }, @$iswcs
         ]
-    });
-
-    return $self->get_revision($response->{ref});
+    );
 }
 
 sub get_revision {
@@ -41,7 +61,8 @@ sub _new_from_response {
         type_id => $data{type},
         language_id => $data{language},
 
-        gid => $response->{mbid}
+        gid => $response->{mbid},
+        revision_id => $response->{revision}
     );
 }
 
diff --git a/lib/MusicBrainz/Server/Data/Role/NES.pm b/lib/MusicBrainz/Server/Data/Role/NES.pm
index e1cfacd..1d19ec9 100644
--- a/lib/MusicBrainz/Server/Data/Role/NES.pm
+++ b/lib/MusicBrainz/Server/Data/Role/NES.pm
@@ -3,23 +3,32 @@ use Moose::Role;
 
 with 'MusicBrainz::Server::Data::Role::Context';
 
+use Data::Dumper::Concise qw( Dumper );
 use Devel::Dwarn;
 use Encode;
 use JSON;
 use Try::Tiny;
+use Time::HiRes qw( gettimeofday tv_interval );
 
 sub request {
     my ($self, $path, $body) = @_;
 
     my $uri = DBDefs->DATA_ACCESS_SERVICE.$path;
-    my $content = to_json ($body, { pretty => 1, canonical => 1 });
+    my $content = to_json ($body, { canonical => 1 });
 
+    printf STDERR "> Request: $uri\n";
+    printf STDERR Dumper($body), "\n";
+
+    my $t0 = [ gettimeofday ];
     my $response = $self->c->lwp->post($uri, Content => encode('utf8', $content));
+    my $t = tv_interval($t0);
 
     return try {
-        my $response = decode_json($response->content);
-        use Devel::Dwarn; Dwarn $response;
-        return $response;
+        printf STDERR "Response in ${t}s\n";
+        printf STDERR $response->content;
+        printf STDERR "\n\n";
+
+        return decode_json($response->content);
     }
     catch {
         die 'Failed to decode response: ' . $response->content;
diff --git a/lib/MusicBrainz/Server/Entity/CoreEntity.pm b/lib/MusicBrainz/Server/Entity/CoreEntity.pm
index dbef7f6..23b6f27 100644
--- a/lib/MusicBrainz/Server/Entity/CoreEntity.pm
+++ b/lib/MusicBrainz/Server/Entity/CoreEntity.pm
@@ -2,9 +2,13 @@ package MusicBrainz::Server::Entity::CoreEntity;
 
 use Moose;
 
-extends 'MusicBrainz::Server::Entity';
 with 'MusicBrainz::Server::Entity::Role::Editable';
 
+has 'revision_id' => (
+    isa => 'Int',
+    is => 'ro',
+);
+
 has 'gid' => (
     is => 'rw',
     isa => 'Str'
diff --git a/lib/MusicBrainz/Server/Form/Work/Edit.pm b/lib/MusicBrainz/Server/Form/Work/Edit.pm
new file mode 100644
index 0000000..ae4085d
--- /dev/null
+++ b/lib/MusicBrainz/Server/Form/Work/Edit.pm
@@ -0,0 +1,12 @@
+package MusicBrainz::Server::Form::Work::Edit;
+use HTML::FormHandler::Moose;
+
+extends 'MusicBrainz::Server::Form::Work';
+
+has_field 'revision_id' => (
+    type => 'Integer',
+    required => 1
+);
+
+__PACKAGE__->meta->make_immutable;
+1;
diff --git a/root/work/edit_form.tt b/root/work/edit_form.tt
index 446f126..bf23628 100644
--- a/root/work/edit_form.tt
+++ b/root/work/edit_form.tt
@@ -9,6 +9,8 @@
       [%- form_row_select(r, 'type_id', l('Type:')) -%]
       [%- form_row_select(r, 'language_id', l('Lyrics Language:')) -%]
       [%- form_row_text_list(r, 'iswcs', l('ISWCs:'), l('ISWC')) -%]
+
+      [% r.hidden(form.field('revision_id')) IF work.revision_id %]
     </fieldset>
 
     [%- INCLUDE 'forms/edit-note.tt' -%]

commit 390bf9d8c36865f4b3a077606f1a6ae26a5aeda9
Author: Oliver Charles <[email protected]>
Date:   Tue Jan 15 14:23:31 2013 +0000

    Fix rating works via the sidebar

diff --git a/lib/MusicBrainz/Server/Controller/Role/Rating.pm b/lib/MusicBrainz/Server/Controller/Role/Rating.pm
index a62cb0f..fa36f4b 100644
--- a/lib/MusicBrainz/Server/Controller/Role/Rating.pm
+++ b/lib/MusicBrainz/Server/Controller/Role/Rating.pm
@@ -8,7 +8,7 @@ sub ratings : Chained('load') PathPart('ratings')
     my ($self, $c) = @_;
 
     my $entity = $c->stash->{$self->{entity_name}};
-    my @ratings = $c->model($self->{model})->rating->find_by_entity_id($entity->id);
+    my @ratings = $c->model($self->{model})->rating->find_by_entity_id($entity->gid);
     $c->model('Editor')->load(@ratings);
     $c->model('Editor')->load_preferences(map { $_->editor } @ratings);
 
diff --git a/root/components/rating-macros.tt b/root/components/rating-macros.tt
index 154774a..5455f58 100644
--- a/root/components/rating-macros.tt
+++ b/root/components/rating-macros.tt
@@ -9,7 +9,7 @@ END -%]
 [%- MACRO rating_rate_url(entity, rating) BLOCK;
     c.uri_for_action("/rating/rate", {
         entity_type => entity_type(entity),
-        entity_id   => entity.id,
+        entity_id   => entity.gid,
         rating      => rating * 20,
     });
 END -%]

commit ab7a61997a56f575ee4e71654adbf75e88b6a935
Author: Oliver Charles <[email protected]>
Date:   Tue Jan 15 14:14:51 2013 +0000

    Fix tagging works via the sidebar

diff --git a/lib/MusicBrainz/Server/Controller/Role/Tag.pm b/lib/MusicBrainz/Server/Controller/Role/Tag.pm
index afeb136..4695ad1 100644
--- a/lib/MusicBrainz/Server/Controller/Role/Tag.pm
+++ b/lib/MusicBrainz/Server/Controller/Role/Tag.pm
@@ -14,9 +14,9 @@ after 'load' => sub
 
     my $entity = $c->stash->{$self->{entity_name}};
     my $tags_model = $c->model($self->{model})->tags;
-    my @tags = $tags_model->find_top_tags($entity->id, $TOP_TAGS_COUNT);
-    my $count = $tags_model->find_tag_count($entity->id);
-    my @user_tags = $tags_model->find_user_tags($c->user->id, $entity->id)
+    my @tags = $tags_model->find_top_tags($entity->gid, $TOP_TAGS_COUNT);
+    my $count = $tags_model->find_tag_count($entity->gid);
+    my @user_tags = $tags_model->find_user_tags($c->user->id, $entity->gid)
         if $c->user_exists;
 
     $c->stash(
@@ -71,11 +71,11 @@ sub tag_async : Chained('load') PathPart('ajax/tag') DenyWhenReadonly
 
     my $entity = $c->stash->{$self->{entity_name}};
     my $tags_model = $c->model($self->{model})->tags;
-    $tags_model->update($c->user->id, $entity->id, $c->req->params->{tags});
+    $tags_model->update($c->user->id, $entity->gid, $c->req->params->{tags});
 
-    my @user_tags = $tags_model->find_user_tags($c->user->id, $entity->id);
-    my @tags = $c->model($self->{model})->tags->find_top_tags($entity->id, $TOP_TAGS_COUNT);
-    my $count = $tags_model->find_tag_count($entity->id);
+    my @user_tags = $tags_model->find_user_tags($c->user->id, $entity->gid);
+    my @tags = $c->model($self->{model})->tags->find_top_tags($entity->gid, $TOP_TAGS_COUNT);
+    my $count = $tags_model->find_tag_count($entity->gid);
 
     my $response = {
         tags => [
diff --git a/lib/MusicBrainz/Server/Data/EntityTag.pm b/lib/MusicBrainz/Server/Data/EntityTag.pm
index d980035..d65d7e3 100644
--- a/lib/MusicBrainz/Server/Data/EntityTag.pm
+++ b/lib/MusicBrainz/Server/Data/EntityTag.pm
@@ -243,8 +243,9 @@ sub update
     @new_tags = $self->parse_tags($input);
 
     Sql::run_in_transaction(sub {
-        # Lock the entity being tagged to prevent concurrency issues
-        $self->parent->get_by_id_locked($entity_id);
+        # NES
+        # # Lock the entity being tagged to prevent concurrency issues
+        # $self->parent->get_by_id_locked($entity_id);
 
         # Load the existing raw tag ids for this entity
         my %old_tag_info;

commit caf9de70f841325776798e42fe427eb818a1aeb6
Author: Oliver Charles <[email protected]>
Date:   Tue Jan 15 13:29:11 2013 +0000

    Re-enable Role::Tag

diff --git a/lib/MusicBrainz/Server/Controller/Work.pm b/lib/MusicBrainz/Server/Controller/Work.pm
index a650c81..ae10e40 100644
--- a/lib/MusicBrainz/Server/Controller/Work.pm
+++ b/lib/MusicBrainz/Server/Controller/Work.pm
@@ -15,12 +15,14 @@ with 'MusicBrainz::Server::Controller::Role::Load' => {
     model       => 'NES::Work',
     entity_name => 'work',
 };
-# with 'MusicBrainz::Server::Controller::Role::Annotation';
+
 with 'MusicBrainz::Server::Controller::Role::Alias';
 with 'MusicBrainz::Server::Controller::Role::Details';
-# with 'MusicBrainz::Server::Controller::Role::Relationship';
-with 'MusicBrainz::Server::Controller::Role::Rating';
 with 'MusicBrainz::Server::Controller::Role::EditListing';
+with 'MusicBrainz::Server::Controller::Role::Rating';
+with 'MusicBrainz::Server::Controller::Role::Tag';
+# with 'MusicBrainz::Server::Controller::Role::Annotation';
+# with 'MusicBrainz::Server::Controller::Role::Relationship';
 # with 'MusicBrainz::Server::Controller::Role::Cleanup';
 # with 'MusicBrainz::Server::Controller::Role::WikipediaExtract';
 
@@ -28,12 +30,6 @@ use aliased 'MusicBrainz::Server::Entity::ArtistCredit';
 
 sub base : Chained('/') PathPart('work') CaptureArgs(0) { }
 
-################################################################################
-# with 'MusicBrainz::Server::Controller::Role::Tag';
-sub tag_async : Chained('load') { }
-sub tags : Chained('load') { }
-################################################################################
-
 after 'load' => sub
 {
     my ($self, $c) = @_;
diff --git a/lib/MusicBrainz/Server/Data/NES/Work.pm b/lib/MusicBrainz/Server/Data/NES/Work.pm
index c3964c4..e970a4f 100644
--- a/lib/MusicBrainz/Server/Data/NES/Work.pm
+++ b/lib/MusicBrainz/Server/Data/NES/Work.pm
@@ -45,5 +45,10 @@ sub _new_from_response {
     );
 }
 
+sub tags {
+    my $self = shift;
+    $self->c->model('Work')->tags;
+}
+
 __PACKAGE__->meta->make_immutable;
 1;

commit 5c2a0683f00734e9bab07145be9837fc0b823ea9
Author: Oliver Charles <[email protected]>
Date:   Tue Jan 15 13:18:26 2013 +0000

    Re-enable Role::Rating

diff --git a/lib/MusicBrainz/Server/Controller/Work.pm b/lib/MusicBrainz/Server/Controller/Work.pm
index e9f6e91..a650c81 100644
--- a/lib/MusicBrainz/Server/Controller/Work.pm
+++ b/lib/MusicBrainz/Server/Controller/Work.pm
@@ -19,7 +19,7 @@ with 'MusicBrainz::Server::Controller::Role::Load' => {
 with 'MusicBrainz::Server::Controller::Role::Alias';
 with 'MusicBrainz::Server::Controller::Role::Details';
 # with 'MusicBrainz::Server::Controller::Role::Relationship';
-# with 'MusicBrainz::Server::Controller::Role::Rating';
+with 'MusicBrainz::Server::Controller::Role::Rating';
 with 'MusicBrainz::Server::Controller::Role::EditListing';
 # with 'MusicBrainz::Server::Controller::Role::Cleanup';
 # with 'MusicBrainz::Server::Controller::Role::WikipediaExtract';

commit 880ac5612760bd18a52c763265143fd0fc6eaaaf
Author: Oliver Charles <[email protected]>
Date:   Tue Jan 15 13:14:33 2013 +0000

    Re-enable the loading of user ratings for work pages

diff --git a/lib/MusicBrainz/Server/Controller/Work.pm b/lib/MusicBrainz/Server/Controller/Work.pm
index d9c44e2..e9f6e91 100644
--- a/lib/MusicBrainz/Server/Controller/Work.pm
+++ b/lib/MusicBrainz/Server/Controller/Work.pm
@@ -41,9 +41,9 @@ after 'load' => sub
     my $work = $c->stash->{work};
     # $c->model('Work')->load_meta($work);
     # $c->model('ISWC')->load_for_works($work);
-    # if ($c->user_exists) {
-    #     $c->model('Work')->rating->load_user_ratings($c->user->id, $work);
-    # }
+    if ($c->user_exists) {
+        $c->model('Work')->rating->load_user_ratings($c->user->id, $work);
+    }
 };
 
 sub show : PathPart('') Chained('load')
diff --git a/lib/MusicBrainz/Server/Data/Rating.pm b/lib/MusicBrainz/Server/Data/Rating.pm
index a35b41f..c53ce7b 100644
--- a/lib/MusicBrainz/Server/Data/Rating.pm
+++ b/lib/MusicBrainz/Server/Data/Rating.pm
@@ -82,7 +82,7 @@ sub load_user_ratings
 {
     my ($self, $user_id, @objs) = @_;
 
-    my %id_to_obj = map { $_->id => $_ } @objs;
+    my %id_to_obj = map { $_->gid => $_ } @objs;
     my @ids = keys %id_to_obj;
     return unless @ids;
 

commit 7cd2e4f98f2d39eb58d8863299b0dda2cf33a21b
Author: Oliver Charles <[email protected]>
Date:   Tue Jan 15 13:00:43 2013 +0000

    Make /work/show action work for just the `work` table

diff --git a/lib/MusicBrainz/Server/Controller/Work.pm b/lib/MusicBrainz/Server/Controller/Work.pm
index 9086bd9..d9c44e2 100644
--- a/lib/MusicBrainz/Server/Controller/Work.pm
+++ b/lib/MusicBrainz/Server/Controller/Work.pm
@@ -12,33 +12,38 @@ use MusicBrainz::Server::Constants qw(
 use MusicBrainz::Server::Translation qw( l );
 
 with 'MusicBrainz::Server::Controller::Role::Load' => {
-    model       => 'Work',
+    model       => 'NES::Work',
     entity_name => 'work',
 };
-with 'MusicBrainz::Server::Controller::Role::Annotation';
+# with 'MusicBrainz::Server::Controller::Role::Annotation';
 with 'MusicBrainz::Server::Controller::Role::Alias';
 with 'MusicBrainz::Server::Controller::Role::Details';
-with 'MusicBrainz::Server::Controller::Role::Relationship';
-with 'MusicBrainz::Server::Controller::Role::Rating';
-with 'MusicBrainz::Server::Controller::Role::Tag';
+# with 'MusicBrainz::Server::Controller::Role::Relationship';
+# with 'MusicBrainz::Server::Controller::Role::Rating';
 with 'MusicBrainz::Server::Controller::Role::EditListing';
-with 'MusicBrainz::Server::Controller::Role::Cleanup';
-with 'MusicBrainz::Server::Controller::Role::WikipediaExtract';
+# with 'MusicBrainz::Server::Controller::Role::Cleanup';
+# with 'MusicBrainz::Server::Controller::Role::WikipediaExtract';
 
 use aliased 'MusicBrainz::Server::Entity::ArtistCredit';
 
 sub base : Chained('/') PathPart('work') CaptureArgs(0) { }
 
+################################################################################
+# with 'MusicBrainz::Server::Controller::Role::Tag';
+sub tag_async : Chained('load') { }
+sub tags : Chained('load') { }
+################################################################################
+
 after 'load' => sub
 {
     my ($self, $c) = @_;
 
     my $work = $c->stash->{work};
-    $c->model('Work')->load_meta($work);
-    $c->model('ISWC')->load_for_works($work);
-    if ($c->user_exists) {
-        $c->model('Work')->rating->load_user_ratings($c->user->id, $work);
-    }
+    # $c->model('Work')->load_meta($work);
+    # $c->model('ISWC')->load_for_works($work);
+    # if ($c->user_exists) {
+    #     $c->model('Work')->rating->load_user_ratings($c->user->id, $work);
+    # }
 };
 
 sub show : PathPart('') Chained('load')
@@ -49,13 +54,15 @@ sub show : PathPart('') Chained('load')
     $c->model('WorkType')->load($work);
     $c->model('Language')->load($work);
 
-    # need to call relationships for overview page
-    $self->relationships($c);
+    # Need to call relationships for overview page
+    # $self->relationships($c); NES
 
     $c->stash->{template} = 'work/index.tt';
 }
 
-for my $action (qw( relationships aliases tags details )) {
+# NES - originally:
+# for my $action (qw( relationships aliases tags details )) {
+for my $action (qw( aliases details )) {
     after $action => sub {
         my ($self, $c) = @_;
         my $work = $c->stash->{work};
diff --git a/lib/MusicBrainz/Server/Data/NES/Work.pm b/lib/MusicBrainz/Server/Data/NES/Work.pm
index 57570bc..c3964c4 100644
--- a/lib/MusicBrainz/Server/Data/NES/Work.pm
+++ b/lib/MusicBrainz/Server/Data/NES/Work.pm
@@ -1,7 +1,7 @@
 package MusicBrainz::Server::Data::NES::Work;
 use Moose;
 
-use MusicBrainz::Server::Entity::NES::Work;
+use MusicBrainz::Server::Entity::Work;
 
 with 'MusicBrainz::Server::Data::Role::NES';
 
@@ -22,11 +22,25 @@ sub create {
 
 sub get_revision {
     my ($self, $revision_id) = @_;
+    return _new_from_response(
+        $self->request('/work/view-revision', { revision => $revision_id }));
+}
+
+sub get_by_gid {
+    my ($self, $gid) = @_;
+    return _new_from_response(
+        $self->request('/work/find-latest', { mbid => $gid }))
+}
 
-    my $response = $self->request('/work/view-revision', { revision => $revision_id });
+sub _new_from_response {
+    my ($response) = @_;
+    my %data = %{ $response->{data} };
+    return MusicBrainz::Server::Entity::Work->new(
+        name => $data{name},
+        comment => $data{comment},
+        type_id => $data{type},
+        language_id => $data{language},
 
-    return MusicBrainz::Server::Entity::NES::Work->new(
-        name => $response->{data}{name},
         gid => $response->{mbid}
     );
 }
diff --git a/lib/MusicBrainz/Server/Entity/NES/Work.pm b/lib/MusicBrainz/Server/Entity/NES/Work.pm
deleted file mode 100644
index 685ad57..0000000
--- a/lib/MusicBrainz/Server/Entity/NES/Work.pm
+++ /dev/null
@@ -1,12 +0,0 @@
-package MusicBrainz::Server::Entity::NES::Work;
-use Moo;
-
-has gid => (
-    is => 'ro'
-);
-
-has name => (
-    is => 'ro'
-);
-
-1;

commit 7995b95a768488e984f6262d703c3aa5662cf284
Author: Oliver Charles <[email protected]>
Date:   Tue Jan 15 12:15:59 2013 +0000

    /work/create: Correctly send ISWCs to data-service

diff --git a/lib/MusicBrainz/Server/Controller/Work.pm b/lib/MusicBrainz/Server/Controller/Work.pm
index dae5a79..9086bd9 100644
--- a/lib/MusicBrainz/Server/Controller/Work.pm
+++ b/lib/MusicBrainz/Server/Controller/Work.pm
@@ -122,16 +122,6 @@ after 'merge' => sub
     $c->model('ISWC')->load_for_works(@{ $c->stash->{to_merge} });
 };
 
-# with 'MusicBrainz::Server::Controller::Role::Create' => {
-#             post_creation => sub {
-#                 my ($edit, $form) = @_;
-#                 my $work = $c->model('Work')->get_by_id($edit->entity_id);
-#                 my @iswcs = @{ $form->field('iswcs')->value };
-#                 $self->_add_iswcs($c, $form, $work, @iswcs) if scalar @iswcs;
-#             }
-# };
-
-
 sub create : Local Edit {
     my ($self, $c) = @_;
 
@@ -147,9 +137,11 @@ sub create : Local Edit {
                 language => $values->{language_id},
                 name => $values->{name},
                 comment => $values->{comment}
-            }
+            },
+            $values->{iswcs} // []
         );
 
+        # NES:
         # my $privs = $c->user->privileges;
         # if ($c->user->is_auto_editor &&
         #     $form->field('as_auto_editor') &&
diff --git a/lib/MusicBrainz/Server/Data/NES/Work.pm b/lib/MusicBrainz/Server/Data/NES/Work.pm
new file mode 100644
index 0000000..57570bc
--- /dev/null
+++ b/lib/MusicBrainz/Server/Data/NES/Work.pm
@@ -0,0 +1,35 @@
+package MusicBrainz::Server::Data::NES::Work;
+use Moose;
+
+use MusicBrainz::Server::Entity::NES::Work;
+
+with 'MusicBrainz::Server::Data::Role::NES';
+
+sub create {
+    my ($self, $edit, $editor, $work, $iswcs) = @_;
+
+    my $response = $self->request('/work/create', {
+        edit => $edit->id,
+        editor => $editor->id,
+        work => $work,
+        iswcs => [
+            map +{ iswc => $_ }, @$iswcs
+        ]
+    });
+
+    return $self->get_revision($response->{ref});
+}
+
+sub get_revision {
+    my ($self, $revision_id) = @_;
+
+    my $response = $self->request('/work/view-revision', { revision => $revision_id });
+
+    return MusicBrainz::Server::Entity::NES::Work->new(
+        name => $response->{data}{name},
+        gid => $response->{mbid}
+    );
+}
+
+__PACKAGE__->meta->make_immutable;
+1;
diff --git a/lib/MusicBrainz/Server/Entity/NES/Edit.pm b/lib/MusicBrainz/Server/Entity/NES/Edit.pm
new file mode 100644
index 0000000..c2192e9
--- /dev/null
+++ b/lib/MusicBrainz/Server/Entity/NES/Edit.pm
@@ -0,0 +1,6 @@
+package MusicBrainz::Server::Entity::NES::Edit;
+use Moo;
+
+has id => ( is => 'ro' );
+
+1;
diff --git a/lib/MusicBrainz/Server/Entity/NES/Work.pm b/lib/MusicBrainz/Server/Entity/NES/Work.pm
new file mode 100644
index 0000000..685ad57
--- /dev/null
+++ b/lib/MusicBrainz/Server/Entity/NES/Work.pm
@@ -0,0 +1,12 @@
+package MusicBrainz::Server::Entity::NES::Work;
+use Moo;
+
+has gid => (
+    is => 'ro'
+);
+
+has name => (
+    is => 'ro'
+);
+
+1;

commit e49cf6ca8b667a855c70154e41f68d49c3588227
Author: Oliver Charles <[email protected]>
Date:   Mon Jan 14 17:52:48 2013 +0000

    Add support for creating works

diff --git a/lib/MusicBrainz/Server/Controller/Work.pm b/lib/MusicBrainz/Server/Controller/Work.pm
index 5862028..dae5a79 100644
--- a/lib/MusicBrainz/Server/Controller/Work.pm
+++ b/lib/MusicBrainz/Server/Controller/Work.pm
@@ -4,7 +4,6 @@ use Moose;
 BEGIN { extends 'MusicBrainz::Server::Controller'; }
 
 use MusicBrainz::Server::Constants qw(
-    $EDIT_WORK_CREATE
     $EDIT_WORK_EDIT
     $EDIT_WORK_MERGE
     $EDIT_WORK_ADD_ISWCS
@@ -123,22 +122,48 @@ after 'merge' => sub
     $c->model('ISWC')->load_for_works(@{ $c->stash->{to_merge} });
 };
 
-with 'MusicBrainz::Server::Controller::Role::Create' => {
-    form      => 'Work',
-    edit_type => $EDIT_WORK_CREATE,
-    edit_arguments => sub {
-        my ($self, $c) = @_;
+# with 'MusicBrainz::Server::Controller::Role::Create' => {
+#             post_creation => sub {
+#                 my ($edit, $form) = @_;
+#                 my $work = $c->model('Work')->get_by_id($edit->entity_id);
+#                 my @iswcs = @{ $form->field('iswcs')->value };
+#                 $self->_add_iswcs($c, $form, $work, @iswcs) if scalar @iswcs;
+#             }
+# };
 
-        return (
-            post_creation => sub {
-                my ($edit, $form) = @_;
-                my $work = $c->model('Work')->get_by_id($edit->entity_id);
-                my @iswcs = @{ $form->field('iswcs')->value };
-                $self->_add_iswcs($c, $form, $work, @iswcs) if scalar @iswcs;
+
+sub create : Local Edit {
+    my ($self, $c) = @_;
+
+    my $form = $c->form( form => 'Work', ctx => $c );
+    if ($c->form_posted && $form->submitted_and_valid($c->req->body_params)) {
+        my $edit = $c->model('NES::Edit')->open;
+
+        my $values = $form->values;
+        my $work_revision = $c->model('NES::Work')->create(
+            $edit, $c->user,
+            {
+                type => $values->{type_id},
+                language => $values->{language_id},
+                name => $values->{name},
+                comment => $values->{comment}
             }
         );
+
+        # my $privs = $c->user->privileges;
+        # if ($c->user->is_auto_editor &&
+        #     $form->field('as_auto_editor') &&
+        #     !$form->field('as_auto_editor')->value) {
+        # }
+
+        $c->response->redirect(
+            $c->uri_for_action($self->action_for('show'), [ $work_revision->gid ]));
     }
-};
+    elsif (!$c->form_posted && %{ $c->req->query_params }) {
+        $form->process( params => $c->req->query_params );
+        $form->clear_errors;
+    }
+}
 
 sub _add_iswcs {
     my ($self, $c, $form, $work, @iswcs) = @_;
diff --git a/lib/MusicBrainz/Server/Data/NES/Artist.pm b/lib/MusicBrainz/Server/Data/NES/Artist.pm
index 6173230..a1f76cf 100644
--- a/lib/MusicBrainz/Server/Data/NES/Artist.pm
+++ b/lib/MusicBrainz/Server/Data/NES/Artist.pm
@@ -5,9 +5,9 @@ use Moose;
 use namespace::autoclean;
 
 use MusicBrainz::Server::Entity::Artist;
-use MusicBrainz::Server::Data::NES::Utils qw( request );
 
 extends 'MusicBrainz::Server::Data::NES::CoreEntity';
+with 'MusicBrainz::Server::Data::Role::NES';
 
 sub _entity_class
 {
@@ -37,14 +37,14 @@ sub create
 {
     my ($self, $editor_id, @artists) = @_;
 
-    my $response = request ('/edit/open', {});
+    my $response = $self->request ('/edit/open', {});
     my $edit_id = $response->{ref};
 
     my @created;
     for my $artist (@artists)
     {
 
-        my $response = request ('/artist/create', {
+        my $response = $self->request ('/artist/create', {
             editor => 1, edit => $edit_id, artist => $artist });
 
         push @created, $response->{ref};
@@ -57,7 +57,7 @@ sub get_by_revision
 {
     my ($self, $revision_id) = @_;
 
-    my $response = request ('/artist/view-revision', { revision => $revision_id });
+    my $response = $self->request ('/artist/view-revision', { revision => $revision_id });
 
     return $self->new_from_response ($response);
 }
diff --git a/lib/MusicBrainz/Server/Data/NES/Edit.pm b/lib/MusicBrainz/Server/Data/NES/Edit.pm
new file mode 100644
index 0000000..c7b1996
--- /dev/null
+++ b/lib/MusicBrainz/Server/Data/NES/Edit.pm
@@ -0,0 +1,16 @@
+package MusicBrainz::Server::Data::NES::Edit;
+use Moose;
+
+use MusicBrainz::Server::Entity::NES::Edit;
+
+with 'MusicBrainz::Server::Data::Role::NES';
+
+sub open {
+    my $self = shift;
+    return MusicBrainz::Server::Entity::NES::Edit->new(
+        id => $self->request('/edit/open', {})->{ref}
+    );
+}
+
+__PACKAGE__->meta->make_immutable;
+1;
diff --git a/lib/MusicBrainz/Server/Data/NES/Editor.pm b/lib/MusicBrainz/Server/Data/NES/Editor.pm
index 0e448c7..0aa0173 100644
--- a/lib/MusicBrainz/Server/Data/NES/Editor.pm
+++ b/lib/MusicBrainz/Server/Data/NES/Editor.pm
@@ -3,7 +3,6 @@ use Moose;
 use namespace::autoclean;
 
 use MusicBrainz::Server::Entity::Editor;
-use MusicBrainz::Server::Data::NES::Utils qw( request );
 
 extends 'MusicBrainz::Server::Data::NES::CoreEntity';
 
@@ -21,7 +20,7 @@ sub register
 {
     my ($self, $editor) = @_;
 
-    my $response = request ('/editor/register', $editor);
+    my $response = $self->request ('/editor/register', $editor);
 
     return $self->new_from_response ($response);
 }
diff --git a/lib/MusicBrainz/Server/Data/NES/Utils.pm b/lib/MusicBrainz/Server/Data/NES/Utils.pm
index c99cc3f..1b8c5aa 100644
--- a/lib/MusicBrainz/Server/Data/NES/Utils.pm
+++ b/lib/MusicBrainz/Server/Data/NES/Utils.pm
@@ -18,19 +18,6 @@ sub model
     return $model_class->new ();
 }
 
-sub request
-{
-    my ($path, $body) = @_;
-
-    my $ua = LWP::UserAgent->new;
-
-    my $uri = DBDefs->DATA_ACCESS_SERVICE.$path;
-    my $content = to_json ($body, { pretty => 1, canonical => 1 });
-
-    my $response = $ua->post ($uri, Content => encode ('utf8', $content));
-
-    return decode_json ($response->content);
-}
 
 1;
 
diff --git a/lib/MusicBrainz/Server/Data/Role/NES.pm b/lib/MusicBrainz/Server/Data/Role/NES.pm
new file mode 100644
index 0000000..e1cfacd
--- /dev/null
+++ b/lib/MusicBrainz/Server/Data/Role/NES.pm
@@ -0,0 +1,29 @@
+package MusicBrainz::Server::Data::Role::NES;
+use Moose::Role;
+
+with 'MusicBrainz::Server::Data::Role::Context';
+
+use Devel::Dwarn;
+use Encode;
+use JSON;
+use Try::Tiny;
+
+sub request {
+    my ($self, $path, $body) = @_;
+
+    my $uri = DBDefs->DATA_ACCESS_SERVICE.$path;
+    my $content = to_json ($body, { pretty => 1, canonical => 1 });
+
+    my $response = $self->c->lwp->post($uri, Content => encode('utf8', $content));
+
+    return try {
+        my $response = decode_json($response->content);
+        use Devel::Dwarn; Dwarn $response;
+        return $response;
+    }
+    catch {
+        die 'Failed to decode response: ' . $response->content;
+    }
+}
+
+1;

commit 1325f9847705187adb813bd3c07b15bf38deb6dd
Author: warp <[email protected]>
Date:   Tue Dec 18 18:30:05 2012 +0100

    First parts of Data::NES, a perl wrapper around musicbrainz-data-service.

diff --git a/lib/DBDefs/Default.pm b/lib/DBDefs/Default.pm
index f7d7f43..f02a859 100644
--- a/lib/DBDefs/Default.pm
+++ b/lib/DBDefs/Default.pm
@@ -73,6 +73,8 @@ sub WEB_SERVER                { "www.musicbrainz.example.com" }
 sub LUCENE_SERVER             { "search.musicbrainz.org" }
 sub WEB_SERVER_USED_IN_EMAIL  { my $self = shift; $self->WEB_SERVER }
 
+sub DATA_ACCESS_SERVICE       { "http://localhost:8000" }
+
 ################################################################################
 # Mail Settings
 ################################################################################
diff --git a/lib/MusicBrainz/Server/Data/NES/Artist.pm b/lib/MusicBrainz/Server/Data/NES/Artist.pm
new file mode 100644
index 0000000..6173230
--- /dev/null
+++ b/lib/MusicBrainz/Server/Data/NES/Artist.pm
@@ -0,0 +1,87 @@
+package MusicBrainz::Server::Data::NES::Artist;
+
+use utf8;
+use Moose;
+use namespace::autoclean;
+
+use MusicBrainz::Server::Entity::Artist;
+use MusicBrainz::Server::Data::NES::Utils qw( request );
+
+extends 'MusicBrainz::Server::Data::NES::CoreEntity';
+
+sub _entity_class
+{
+    return 'MusicBrainz::Server::Entity::Artist';
+}
+
+sub _field_mapping
+{
+    my $self = shift;
+
+    my $pd = $self->c->model ('NES::PartialDate');
+
+    return {
+        'mbid' => 'gid',
+        'country' => 'country_id',
+        'type' => 'type_id',
+        'gender' => 'gender_id',
+        'sort-name' => 'sort_name',
+        'begin-date' => undef,
+        'end-date' => undef,
+        'begin-date' => sub { return $pd->new_from_service (@_); },
+        'end-date' => sub { return $pd->new_from_service (@_); },
+    };
+}
+
+sub create
+{
+    my ($self, $editor_id, @artists) = @_;
+
+    my $response = request ('/edit/open', {});
+    my $edit_id = $response->{ref};
+
+    my @created;
+    for my $artist (@artists)
+    {
+
+        my $response = request ('/artist/create', {
+            editor => 1, edit => $edit_id, artist => $artist });
+
+        push @created, $response->{ref};
+    }
+
+    return @artists > 1 ? @created : $created[0];
+}
+
+sub get_by_revision
+{
+    my ($self, $revision_id) = @_;
+
+    my $response = request ('/artist/view-revision', { revision => $revision_id });
+
+    return $self->new_from_response ($response);
+}
+
+__PACKAGE__->meta->make_immutable;
+no Moose;
+1;
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 MetaBrainz Foundation
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+=cut
diff --git a/lib/MusicBrainz/Server/Data/NES/CoreEntity.pm b/lib/MusicBrainz/Server/Data/NES/CoreEntity.pm
new file mode 100644
index 0000000..3833c04
--- /dev/null
+++ b/lib/MusicBrainz/Server/Data/NES/CoreEntity.pm
@@ -0,0 +1,30 @@
+package MusicBrainz::Server::Data::NES::CoreEntity;
+use Moose;
+
+extends 'MusicBrainz::Server::Data::NES::Entity';
+
+
+__PACKAGE__->meta->make_immutable;
+no Moose;
+1;
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 MetaBrainz Foundation
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+=cut
+
diff --git a/lib/MusicBrainz/Server/Data/NES/Editor.pm b/lib/MusicBrainz/Server/Data/NES/Editor.pm
new file mode 100644
index 0000000..0e448c7
--- /dev/null
+++ b/lib/MusicBrainz/Server/Data/NES/Editor.pm
@@ -0,0 +1,51 @@
+package MusicBrainz::Server::Data::NES::Editor;
+use Moose;
+use namespace::autoclean;
+
+use MusicBrainz::Server::Entity::Editor;
+use MusicBrainz::Server::Data::NES::Utils qw( request );
+
+extends 'MusicBrainz::Server::Data::NES::CoreEntity';
+
+sub _entity_class
+{
+    return 'MusicBrainz::Server::Entity::Editor';
+}
+
+sub _field_mapping
+{
+    return { 'ref' => 'id' };
+}
+
+sub register
+{
+    my ($self, $editor) = @_;
+
+    my $response = request ('/editor/register', $editor);
+
+    return $self->new_from_response ($response);
+}
+
+__PACKAGE__->meta->make_immutable;
+no Moose;
+1;
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 MetaBrainz Foundation
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+=cut
diff --git a/lib/MusicBrainz/Server/Data/NES/Entity.pm b/lib/MusicBrainz/Server/Data/NES/Entity.pm
new file mode 100644
index 0000000..2c96b07
--- /dev/null
+++ b/lib/MusicBrainz/Server/Data/NES/Entity.pm
@@ -0,0 +1,83 @@
+package MusicBrainz::Server::Data::NES::Entity;
+use Moose;
+
+with 'MusicBrainz::Server::Data::Role::Context';
+
+sub _entity_class
+{
+    die("Not implemented");
+}
+
+sub _field_mapping
+{
+    return {};
+}
+
+
+sub new_from_service
+{
+    my ($self, $response, $field) = @_;
+
+    return unless $response;
+
+    my %data = %{ $response->{$field} };
+    my %info;
+    my %mapping = %{$self->_field_mapping};
+
+    foreach my $key (keys %data)
+    {
+        my $attrib = $mapping{$key} // $key;
+        my $val;
+        if (ref($attrib) eq 'CODE') {
+            $val = $attrib->(\%data, $key);
+        }
+        elsif (defined $data{$key}) {
+            $val = $data{$key};
+        }
+        $info{$attrib} = $val if defined $val;
+    }
+
+    my $entity_class = $self->_entity_class(\%data);
+    Class::MOP::load_class($entity_class);
+
+    return $entity_class->new(%info);
+}
+
+sub new_from_response
+{
+    my ($self, $response) = @_;
+
+    return unless $response;
+
+    for my $key (grep { $_ ne 'data' } keys %$response)
+    {
+        $response->{data}->{$key} = $response->{$key};
+    }
+
+    return $self->new_from_service ($response, 'data');
+}
+
+
+__PACKAGE__->meta->make_immutable;
+no Moose;
+1;
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 MetaBrainz Foundation
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+=cut
diff --git a/lib/MusicBrainz/Server/Data/NES/PartialDate.pm b/lib/MusicBrainz/Server/Data/NES/PartialDate.pm
new file mode 100644
index 0000000..bc3b7a0
--- /dev/null
+++ b/lib/MusicBrainz/Server/Data/NES/PartialDate.pm
@@ -0,0 +1,33 @@
+package MusicBrainz::Server::Data::NES::PartialDate;
+use Moose;
+
+extends 'MusicBrainz::Server::Data::NES::Entity';
+
+sub _entity_class
+{
+    return 'MusicBrainz::Server::Entity::PartialDate';
+}
+
+__PACKAGE__->meta->make_immutable;
+no Moose;
+1;
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 MetaBrainz Foundation
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+=cut
diff --git a/lib/MusicBrainz/Server/Data/NES/Utils.pm b/lib/MusicBrainz/Server/Data/NES/Utils.pm
new file mode 100644
index 0000000..c99cc3f
--- /dev/null
+++ b/lib/MusicBrainz/Server/Data/NES/Utils.pm
@@ -0,0 +1,55 @@
+package MusicBrainz::Server::Data::NES::Utils;
+use Moose;
+
+use DBDefs;
+use Encode;
+use JSON;
+use LWP::UserAgent;
+
+use Sub::Exporter -setup => {
+    exports => [qw( request )]
+};
+
+sub model
+{
+    my $model_class = shift;
+
+    Class::MOP::load_class("MusicBrainz::Server::Data::NES::$model_class");
+    return $model_class->new ();
+}
+
+sub request
+{
+    my ($path, $body) = @_;
+
+    my $ua = LWP::UserAgent->new;
+
+    my $uri = DBDefs->DATA_ACCESS_SERVICE.$path;
+    my $content = to_json ($body, { pretty => 1, canonical => 1 });
+
+    my $response = $ua->post ($uri, Content => encode ('utf8', $content));
+
+    return decode_json ($response->content);
+}
+
+1;
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 MetaBrainz Foundation
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+=cut
diff --git a/t/lib/t/MusicBrainz/Server/Data/NES/Artist.pm b/t/lib/t/MusicBrainz/Server/Data/NES/Artist.pm
new file mode 100644
index 0000000..3098b6b
--- /dev/null
+++ b/t/lib/t/MusicBrainz/Server/Data/NES/Artist.pm
@@ -0,0 +1,64 @@
+package t::MusicBrainz::Server::Data::NES::Artist;
+
+use utf8;
+use Test::Routine;
+use Test::More;
+use MusicBrainz::Server::Data::NES::Artist;
+
+with 't::Context';
+
+test 'Create artist' => sub {
+    my $test = shift;
+
+    my $editor = $test->c->model ('NES::Editor')->register (
+        { 'name' => 'Bulbasaur', 'password' => 'pokemons' });
+
+    my $artist_create = {
+        'name' => '倖田 來未',
+        'sort-name' => 'Koda Kumi',
+        # 'country' => 1,
+        # 'gender' => 1,
+        'ended' => 0,
+        'begin-date' => {
+            'year' => 1982,
+            'month' => 11,
+            'day' => 13,
+        }
+        # 'type' => 1
+    };
+
+    my $inserted = $test->c->model ('NES::Artist')->create (
+        $editor->id, $artist_create);
+
+    ok ($inserted > 0, "Artist has a positive id");
+
+    my $artist = $test->c->model ('NES::Artist')->get_by_revision ($inserted);
+
+    is ($artist->name, "\x{5016}\x{7530} \x{4f86}\x{672a}", "Artist name consists of expected unicode code points");
+    is ($artist->sort_name, "Koda Kumi", "Artist sort-name correct");
+    is ($artist->begin_date->format, "1982-11-13");
+    is ($artist->end_date->format, "");
+    ok (!$artist->ended, "Artist is still alive");
+};
+
+1;
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 MetaBrainz Foundation
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+=cut
diff --git a/t/lib/t/MusicBrainz/Server/Data/NES/Editor.pm b/t/lib/t/MusicBrainz/Server/Data/NES/Editor.pm
new file mode 100644
index 0000000..c8e4894
--- /dev/null
+++ b/t/lib/t/MusicBrainz/Server/Data/NES/Editor.pm
@@ -0,0 +1,45 @@
+package t::MusicBrainz::Server::Data::NES::Editor;
+
+use utf8;
+use Test::Routine;
+use Test::More;
+use MusicBrainz::Server::Data::NES::Editor;
+
+with 't::Context';
+
+test 'Register editor' => sub {
+    my $test = shift;
+
+    my $editor_form = {
+        'name' => '박 상수',
+        'password' => 'IchGofEnckucFibrajFecepNooHyfsUv',
+    };
+
+    my $editor = $test->c->model ('NES::Editor')->register ($editor_form);
+
+    isa_ok ($editor, 'MusicBrainz::Server::Entity::Editor');
+    is ($editor->name, '박 상수', "Editor has expected name");
+    ok ($editor->id > 0, "Editor has a positive id");
+};
+
+1;
+
+=head1 COPYRIGHT
+
+Copyright (C) 2012 MetaBrainz Foundation
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+=cut

-----------------------------------------------------------------------


hooks/post-receive
-- 
mb_server

_______________________________________________
MusicBrainz-commits mailing list
[email protected]
http://lists.musicbrainz.org/mailman/listinfo/musicbrainz-commits