key duplication result on new reading rather than old
[scpubgit/stemmaweb.git] / lib / stemmaweb / Controller / Relation.pm
index 0b5d1bb..7dd0c73 100644 (file)
@@ -1,5 +1,5 @@
 package stemmaweb::Controller::Relation;
-use JSON qw/ to_json /;
+use JSON qw/ to_json from_json /;
 use Moose;
 use Module::Load;
 use namespace::autoclean;
@@ -236,9 +236,10 @@ sub relationships :Chained('text') :PathPart :Args(0) {
                } elsif( $c->request->method eq 'DELETE' ) {
                        my $node = $c->request->param('source_id');
                        my $target = $c->request->param('target_id');
-               
+                       my $scopewide = $c->request->param('scopewide') 
+                               && $c->request->param('scopewide') eq 'true';
                        try {
-                               my @vectors = $collation->del_relationship( $node, $target );
+                               my @vectors = $collation->del_relationship( $node, $target, $scopewide );
                                $m->save( $tradition );
                                $c->stash->{'result'} = \@vectors;
                        } catch( Text::Tradition::Error $e ) {
@@ -280,11 +281,9 @@ sub _reading_struct {
                my $t = $_[0]->type;
                return $t eq 'spelling' || $t eq 'orthographic';
        };
-       my @variants;
-       foreach my $sr ( $reading->related_readings( $sameword ) ) {
-               push( @variants, $sr->text );
-       }
-       $struct->{'variants'} = \@variants;
+       # Now add the list data
+       $struct->{'variants'} = [ map { $_->text } $reading->related_readings( $sameword ) ];
+       $struct->{'witnesses'} = [ $reading->witnesses ];
        return $struct;
 }
 
@@ -331,7 +330,7 @@ sub reading :Chained('text') :PathPart :Args(1) {
                if( $c->stash->{'permission'} ne 'full' ) {
                        $c->response->status( '403' );
                        $c->stash->{'result'} = { 
-                               'error' => 'You do not have permission to view this tradition.' };
+                               'error' => 'You do not have permission to modify this tradition.' };
                        $c->detach('View::JSON');
                        return;
                }
@@ -389,6 +388,116 @@ sub reading :Chained('text') :PathPart :Args(1) {
 
 }
 
+=head2 duplicate
+
+ POST relation/$textid/duplicate { data }
+Duplicates the requested readings, detaching the witnesses specified in
+the list to use the new reading(s) instead of the old. The data to be
+passed should be a JSON structure:
+
+ { readings: rid1,rid2,rid3,...
+   witnesses: [ wit1, ... ] }
+
+=cut
+
+sub duplicate :Chained('text') :PathPart :Args(0) {
+       my( $self, $c ) = @_;
+       my $tradition = delete $c->stash->{'tradition'};
+       my $collation = $tradition->collation;
+       my $m = $c->model('Directory');
+       if( $c->request->method eq 'POST' ) {
+               if( $c->stash->{'permission'} ne 'full' ) {
+                       $c->response->status( '403' );
+                       $c->stash->{'result'} = { 
+                               'error' => 'You do not have permission to modify this tradition.' };
+                       $c->detach('View::JSON');
+                       return;
+               }
+               my $errmsg;
+               my $response = {};
+               # Sort out which readings need to be duplicated from the set given, and
+               # ensure that all the given wits bear each relevant reading.
+               
+               my %wits = ();
+               map { $wits{$_} = 1 } $c->request->param('witnesses[]');
+               my %rdgranks = ();
+               foreach my $rid ( $c->request->param('readings[]') ) {
+                       my $numwits = 0;
+                       my $rdg = $collation->reading( $rid );
+                       foreach my $rwit ( $rdg->witnesses( $rid ) ) {
+                               $numwits++ if exists $wits{$rwit};
+                       }
+                       if( $numwits > 0 && $numwits < keys( %wits ) ) {
+                               $errmsg = "Reading $rid contains some but not all of the specified witnesses.";
+                               last;
+                       } elsif( exists $rdgranks{ $rdg->rank } ) {
+                               $errmsg = "More than one reading would be detached along with $rid at rank " . $rdg->rank;
+                               last;
+                       } else {
+                               $rdgranks{ $rdg->rank } = $rid;
+                       }
+               }
+               
+               # Now check that the readings make a single sequence.
+               unless( $errmsg ) {
+                       my $prior;
+                       foreach my $rank ( sort { $a <=> $b } keys %rdgranks ) {
+                               my $rid = $rdgranks{$rank};
+                               if( $prior ) {
+                                       # Check that there is only one path between $prior and $rdg.
+                                       foreach my $wit ( keys %wits ) {
+                                               unless( $collation->prior_reading( $rid, $wit ) eq $prior ) {
+                                                       $errmsg = "Diverging witness paths from $prior to $rid at $wit";
+                                                       last;
+                                               }
+                                       }
+                               }
+                               $prior = $rid;
+                       }
+               }
+               
+               # Abort if we've run into a problem.
+               if( $errmsg ) {
+                       $c->stash->{'result'} = { 'error' => $errmsg };
+                       $c->response->status( '403' );
+                       $c->forward('View::JSON');
+                       return;
+               }
+               
+               # Otherwise, do the dirty work.
+               my @witlist = keys %wits;
+               foreach my $rank ( sort { $a <=> $b } keys %rdgranks ) {
+                       my $newrdg;
+                       my $reading_id = $rdgranks{$rank};
+                       try {
+                               $newrdg = $collation->duplicate_reading( $reading_id, @witlist );
+                       } catch( Text::Tradition::Error $e ) {
+                               $c->response->status( '403' );
+                               $errmsg = $e->message;
+                       } catch {
+                               # Something else went wrong, probably a Moose error
+                               $c->response->status( '403' );
+                               $errmsg = 'Something went wrong with the request';      
+                       }
+                       if( $newrdg ) {
+                               my $data = _reading_struct( $newrdg );
+                               $data->{'orig_rdg'} = $reading_id;
+                               $response->{"$newrdg"} = $data;
+                       }
+               } 
+               if( $errmsg ) {
+                       $c->stash->{'result'} = { 'error' => $errmsg };
+               } else {
+                       $m->save( $collation );
+                       $c->stash->{'result'} = $response;
+               }
+       }
+       $c->forward('View::JSON');
+}
+
+
+
 sub _check_permission {
        my( $c, $tradition ) = @_;
     my $user = $c->user_exists ? $c->user->get_object : undef;