From: Tara L Andrews Date: Fri, 7 Jun 2013 10:31:17 +0000 (+0200) Subject: first in-progress attempt at adding node detachment X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=5539cba31aece1048961e1c0845c0996390cf3b4;p=scpubgit%2Fstemmaweb.git first in-progress attempt at adding node detachment --- diff --git a/lib/stemmaweb/Controller/Relation.pm b/lib/stemmaweb/Controller/Relation.pm index 0b5d1bb..71f80ac 100644 --- a/lib/stemmaweb/Controller/Relation.pm +++ b/lib/stemmaweb/Controller/Relation.pm @@ -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; @@ -280,11 +280,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 +329,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 +387,58 @@ sub reading :Chained('text') :PathPart :Args(1) { } +=head2 duplicate + + POST relation/$textid/duplicate/$id/ { witnesses } + +Duplicates the given reading, detaching the witnesses specified in the list to use +the new reading instead of the old. The 'witnesses' param should be a JSON array. + +=cut + +sub duplicate :Chained('text') :PathPart :Args(1) { + my( $self, $c, $reading_id ) = @_; + my $tradition = delete $c->stash->{'tradition'}; + my $collation = $tradition->collation; + my $rdg = $collation->reading( $reading_id ); + 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 = {}; + if( $c->request->param('witnesses') ) { + my $witlist = from_json( $c->request->param('witnesses') ); + my $newrdg; + 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 ) { + $response = { reading => $newrdg->id, witnesses => $witlist }; + } + } else { + $c->response->status( '403' ); + $errmsg = "At least one witness must be specified for a duplication"; + } + $c->stash->{'result'} = $errmsg ? { 'error' => $errmsg } : $response; + } + $c->forward('View::JSON'); +} + + + sub _check_permission { my( $c, $tradition ) = @_; my $user = $c->user_exists ? $c->user->get_object : undef; diff --git a/root/css/jquery.multiselect.css b/root/css/jquery.multiselect.css new file mode 100644 index 0000000..898786a --- /dev/null +++ b/root/css/jquery.multiselect.css @@ -0,0 +1,23 @@ +.ui-multiselect { padding:2px 0 2px 4px; text-align:left } +.ui-multiselect span.ui-icon { float:right } +.ui-multiselect-single .ui-multiselect-checkboxes input { position:absolute !important; top: auto !important; left:-9999px; } +.ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important } + +.ui-multiselect-header { margin-bottom:3px; padding:3px 0 3px 4px } +.ui-multiselect-header ul { font-size:0.9em } +.ui-multiselect-header ul li { float:left; padding:0 10px 0 0 } +.ui-multiselect-header a { text-decoration:none } +.ui-multiselect-header a:hover { text-decoration:underline } +.ui-multiselect-header span.ui-icon { float:left } +.ui-multiselect-header li.ui-multiselect-close { float:right; text-align:right; padding-right:0 } + +.ui-multiselect-menu { display:none; padding:3px; position:absolute; z-index:10000; text-align: left } +.ui-multiselect-checkboxes { position:relative /* fixes bug in IE6/7 */; overflow-y:scroll } +.ui-multiselect-checkboxes label { cursor:default; display:block; border:1px solid transparent; padding:3px 1px } +.ui-multiselect-checkboxes label input { position:relative; top:1px } +.ui-multiselect-checkboxes li { clear:both; font-size:0.9em; padding-right:3px } +.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label { text-align:center; font-weight:bold; border-bottom:1px solid } +.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label a { display:block; padding:3px; margin:1px 0; text-decoration:none } + +/* remove label borders in IE6 because IE6 does not support transparency */ +* html .ui-multiselect-checkboxes label { border:none } diff --git a/root/js/jquery.multiselect.min.js b/root/js/jquery.multiselect.min.js new file mode 100644 index 0000000..e924350 --- /dev/null +++ b/root/js/jquery.multiselect.min.js @@ -0,0 +1,20 @@ +/* + * jQuery MultiSelect UI Widget 1.13 + * Copyright (c) 2012 Eric Hynds + * + * http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/ + * + * Depends: + * - jQuery 1.4.2+ + * - jQuery UI 1.8 widget factory + * + * Optional: + * - jQuery UI effects + * - jQuery UI position utility + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ +(function(d){var k=0;d.widget("ech.multiselect",{options:{header:!0,height:175,minWidth:225,classes:"",checkAllText:"Check all",uncheckAllText:"Uncheck all",noneSelectedText:"Select options",selectedText:"# selected",selectedList:0,show:null,hide:null,autoOpen:!1,multiple:!0,position:{}},_create:function(){var a=this.element.hide(),b=this.options;this.speed=d.fx.speeds._default;this._isOpen=!1;a=(this.button=d('')).addClass("ui-multiselect ui-widget ui-state-default ui-corner-all").addClass(b.classes).attr({title:a.attr("title"),"aria-haspopup":!0,tabIndex:a.attr("tabIndex")}).insertAfter(a);(this.buttonlabel=d("")).html(b.noneSelectedText).appendTo(a);var a=(this.menu=d("
")).addClass("ui-multiselect-menu ui-widget ui-widget-content ui-corner-all").addClass(b.classes).appendTo(document.body),c=(this.header=d("
")).addClass("ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix").appendTo(a);(this.headerLinkContainer=d("
    ")).addClass("ui-helper-reset").html(function(){return!0===b.header?'
  • '+b.checkAllText+'
  • '+b.uncheckAllText+"
  • ":"string"===typeof b.header?"
  • "+b.header+"
  • ":""}).append('
  • ').appendTo(c);(this.checkboxContainer=d("
      ")).addClass("ui-multiselect-checkboxes ui-helper-reset").appendTo(a);this._bindEvents();this.refresh(!0);b.multiple||a.addClass("ui-multiselect-single")},_init:function(){!1===this.options.header&&this.header.hide();this.options.multiple||this.headerLinkContainer.find(".ui-multiselect-all, .ui-multiselect-none").hide();this.options.autoOpen&&this.open();this.element.is(":disabled")&&this.disable()},refresh:function(a){var b=this.element,c=this.options,f=this.menu,h=this.checkboxContainer,g=[],e="",i=b.attr("id")||k++;b.find("option").each(function(b){d(this);var a=this.parentNode,f=this.innerHTML,h=this.title,k=this.value,b="ui-multiselect-"+(this.id||i+"-option-"+b),l=this.disabled,n=this.selected,m=["ui-corner-all"],o=(l?"ui-multiselect-disabled ":" ")+this.className,j;"OPTGROUP"===a.tagName&&(j=a.getAttribute("label"),-1===d.inArray(j,g)&&(e+='
    • '+j+"
    • ",g.push(j)));l&&m.push("ui-state-disabled");n&&!c.multiple&&m.push("ui-state-active");e+='
    • ';e+='
    • "});h.html(e);this.labels=f.find("label");this.inputs=this.labels.children("input");this._setButtonWidth();this._setMenuWidth();this.button[0].defaultValue=this.update();a||this._trigger("refresh")},update:function(){var a=this.options,b=this.inputs,c=b.filter(":checked"),f=c.length,a=0===f?a.noneSelectedText:d.isFunction(a.selectedText)?a.selectedText.call(this,f,b.length,c.get()):/\d/.test(a.selectedList)&&0').attr( + 'value', wit ).text( wit ) ); + }); + } // Now do the morphological properties. morphology_form( reading_info['lexemes'] ); // and then open the dialog. @@ -752,6 +760,11 @@ $(document).ready(function () { // function for reading form dialog should go here; // just hide the element for now if we don't have morphology if( can_morphologize ) { + if( editable ) { + $('#reading_decollate_witnesses').multiselect(); + } else { + $('#decollation').hide(); + } $('#reading-form').dialog({ autoOpen: false, // height: 400, @@ -807,6 +820,8 @@ $(document).ready(function () { }, open: function() { $(".ui-widget-overlay").css("background", "none"); + $('#reading_decollate_witnesses').multiselect("refresh"); + $('#reading_decollate_witnesses').multiselect("uncheckAll"); $("#dialog_overlay").show(); $('#reading_status').empty(); $("#dialog_overlay").height( $("#enlargement_container").height() ); diff --git a/root/src/header.tt b/root/src/header.tt index 8f87602..9264603 100644 --- a/root/src/header.tt +++ b/root/src/header.tt @@ -4,6 +4,7 @@ + [% IF applicationstyle -%] @@ -12,6 +13,7 @@ + [% IF applicationjs -%] diff --git a/root/src/relate.tt b/root/src/relate.tt index e2affca..a3fe4af 100644 --- a/root/src/relate.tt +++ b/root/src/relate.tt @@ -98,6 +98,15 @@ $(document).ready(function () {


+ +
+ + + +