From: Tara L Andrews Date: Thu, 4 Jul 2013 22:08:24 +0000 (+0200) Subject: add server-side reading merge logic for issue #9; uniform handling of buttons in... X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=scpubgit%2Fstemmaweb.git;a=commitdiff_plain;h=b001c73dc563fa9fd781375fc6916e3d93189657 add server-side reading merge logic for issue #9; uniform handling of buttons in relationship and deletion dialogs. --- diff --git a/lib/stemmaweb/Controller/Relation.pm b/lib/stemmaweb/Controller/Relation.pm index 46fb11f..bfe9d87 100644 --- a/lib/stemmaweb/Controller/Relation.pm +++ b/lib/stemmaweb/Controller/Relation.pm @@ -388,6 +388,72 @@ sub reading :Chained('text') :PathPart :Args(1) { } +=head2 merge + + POST relation/$textid/merge { data } + +Merges the requested readings, combining the witnesses of both readings into +the target reading. All non-conflicting source relationships are inherited by +the target relationship. + +=cut + +sub merge :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; + + my $main = $c->request->param('target_id'); + my $second = $c->request->param('source_id'); + # Find the common successor of these, so that we can detect other + # potentially identical readings. + my $csucc = $collation->common_successor( $main, $second ); + + # Try the merge if these are parallel readings. + if( $csucc->id eq $main || $csucc->id eq $second ) { + $errmsg = "Cannot merge readings in the same path"; + } else { + try { + $collation->merge_readings( $main, $second ); + } 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'; + } + } + + # Look for readings that are now identical. + if( $errmsg ) { + $response = { status => 'error', error => $errmsg }; + } else { + $response = { status => 'ok' }; + my @identical = $collation->identical_readings( + start => $main, end => $csucc->id ); + if( @identical ) { + $response->{'checkalign'} = [ + map { [ $_->[0]->id, $_->[1]->id ] } @identical ]; + } + $m->save( $collation ); + } + $c->stash->{'result'} = $response; + $c->forward('View::JSON'); + } +} + =head2 duplicate POST relation/$textid/duplicate { data } @@ -425,7 +491,6 @@ sub duplicate :Chained('text') :PathPart :Args(0) { foreach my $rid ( $c->request->param('readings[]') ) { my $numwits = 0; my $rdg = $collation->reading( $rid ); - $DB::single = 1; foreach my $rwit ( $rdg->witnesses( $rid ) ) { $numwits++ if exists $wits{$rwit}; } diff --git a/root/js/relationship.js b/root/js/relationship.js index 39d8f50..89ca5e8 100644 --- a/root/js/relationship.js +++ b/root/js/relationship.js @@ -837,6 +837,21 @@ function Marquee() { } +function readings_equivalent( source, target ) { + var sourcetext = readingdata[source].text; + var targettext = readingdata[target].text; + if( sourcetext === targettext ) { + return true; + } + // Lowercase and strip punctuation from both and compare again + var stlc = sourcetext.toLowerCase().replace(/[^\w\s]|_/g, ""); + var ttlc = targettext.toLowerCase().replace(/[^\w\s]|_/g, ""); + if( stlc === ttlc ) { + return true; + } + return false; +} + $(document).ready(function () { @@ -844,7 +859,8 @@ $(document).ready(function () { relation_manager = new relation_factory(); $('#update_workspace_button').data('locked', false); - + + // Set up the mouse events on the SVG enlargement $('#enlargement').mousedown(function (event) { $(this) .data('down', true) @@ -909,6 +925,11 @@ $(document).ready(function () { }); + // Set up the relationship creation dialog. This also functions as the reading + // merge dialog where appropriate. + var relation_buttonset = { + }; + if( editable ) { $( "#dialog-form" ).dialog({ autoOpen: false, @@ -916,7 +937,16 @@ $(document).ready(function () { width: 290, modal: true, buttons: { - "Ok": function( evt ) { + "Merge readings": function( evt ) { + $(evt.target).button("disable"); + $('#status').empty(); + form_values = $('#collapse_node_form').serialize(); + ncpath = getTextURL( 'merge' ); + var jqjson = $.post( ncpath, form_values, function(data) { + alert( "Did a node merge" ); + }); + }, + OK: function( evt ) { $(evt.target).button("disable"); $('#status').empty(); form_values = $('#collapse_node_form').serialize(); @@ -961,7 +991,15 @@ $(document).ready(function () { }); }, open: function() { - relation_manager.create_temporary( $('#source_node_id').val(), $('#target_node_id').val() ); + relation_manager.create_temporary( + $('#source_node_id').val(), $('#target_node_id').val() ); + var buttonset = $(this).parent().find( '.ui-dialog-buttonset' ) + if( readings_equivalent( $('#source_node_id').val(), + $('#target_node_id').val() ) ) { + buttonset.find( "button:contains('Merge readings')" ).show(); + } else { + buttonset.find( "button:contains('Merge readings')" ).hide(); + } $(".ui-widget-overlay").css("background", "none"); $("#dialog_overlay").show(); $("#dialog_overlay").height( $("#enlargement_container").height() ); @@ -994,23 +1032,23 @@ $(document).ready(function () { } ); } - var deletion_buttonset = { - cancel: function() { $( this ).dialog( "close" ); }, - global: function () { delete_relation( true ); }, - delete: function() { delete_relation( false ); } - }; - + // Set up the relationship info display and deletion dialog. $( "#delete-form" ).dialog({ autoOpen: false, height: 135, width: 250, modal: false, + buttons: { + OK: function() { $( this ).dialog( "close" ); }, + "Delete all": function () { delete_relation( true ); }, + Delete: function() { delete_relation( false ); } + }, create: function(event, ui) { // TODO What is this logic doing? // This scales the buttons in the dialog and makes it look proper // Not sure how essential it is, does anything break if it's not here? var buttonset = $(this).parent().find( '.ui-dialog-buttonset' ).css( 'width', '100%' ); - buttonset.find( "button:contains('Cancel')" ).css( 'float', 'right' ); + buttonset.find( "button:contains('OK')" ).css( 'float', 'right' ); // A: This makes sure that the pop up delete relation dialogue for a hovered over // relation auto closes if the user doesn't engage (mouseover) with it. var dialog_aria = $("div[aria-labelledby='ui-dialog-title-delete-form']"); @@ -1022,22 +1060,21 @@ $(document).ready(function () { }) }, open: function() { + // Show the appropriate buttons... + var buttonset = $(this).parent().find( '.ui-dialog-buttonset' ) + // If the user can't edit, show only the OK button if( !editable ) { - $( this ).dialog( "option", "buttons", - [{ text: "OK", click: deletion_buttonset['cancel'] }] ); + buttonset.find( "button:contains('Delete')" ).hide(); + // If the relationship scope is local, show only OK and Delete } else if( $('#delete_relation_scope').text() === 'local' ) { $( this ).dialog( "option", "width", 160 ); - $( this ).dialog( "option", "buttons", - [{ text: "Delete", click: deletion_buttonset['delete'] }, - { text: "Cancel", click: deletion_buttonset['cancel'] }] ); + buttonset.find( "button:contains('Delete')" ).show(); + buttonset.find( "button:contains('Delete all')" ).hide(); + // Otherwise, show all three } else { $( this ).dialog( "option", "width", 200 ); - $( this ).dialog( "option", "buttons", - [{ text: "Delete", click: deletion_buttonset['delete'] }, - { text: "Delete all", click: deletion_buttonset['global'] }, - { text: "Cancel", click: deletion_buttonset['cancel'] }] ); + buttonset.find( "button:contains('Delete')" ).show(); } - mouseWait = setTimeout( function() { $("#delete-form").dialog( "close" ) }, 2000 ); }, close: function() {} @@ -1055,10 +1092,10 @@ $(document).ready(function () { var form_values = $('#detach_collated_form').serialize(); ncpath = getTextURL( 'duplicate' ); var jqjson = $.post( ncpath, form_values, function(data) { - detach_node( data ); - $(evt.target).button("enable"); - $( this ).dialog( "close" ); - }); + detach_node( data ); + $(evt.target).button("enable"); + $( "#multipleselect-form" ).dialog( "close" ); + }); } }, create: function(event, ui) {