package stemmaweb::Controller::Root;
use Moose;
use namespace::autoclean;
-use Text::Tradition::Analysis qw/ run_analysis /;
+use JSON qw ();
use TryCatch;
+use XML::LibXML;
+use XML::LibXML::XPathContext;
BEGIN { extends 'Catalyst::Controller' }
my $tradition;
my $errmsg;
if( $ext eq 'xml' ) {
- # Try the different XML parsing options to see if one works.
- foreach my $type ( qw/ CollateX CTE TEI / ) {
+ my $type;
+ # Parse the XML to see which flavor it is.
+ my $parser = XML::LibXML->new();
+ my $doc;
+ try {
+ $doc = $parser->parse_file( $newopts{'file'} );
+ } catch( $err ) {
+ $errmsg = "XML file parsing error: $err";
+ }
+ if( $doc ) {
+ if( $doc->documentElement->nodeName eq 'graphml' ) {
+ $type = 'CollateX';
+ } elsif( $doc->documentElement->nodeName ne 'TEI' ) {
+ $errmsg = 'Unrecognized XML type ' . $doc->documentElement->nodeName;
+ } else {
+ my $xpc = XML::LibXML::XPathContext->new( $doc->documentElement );
+ my $venc = $xpc->findvalue( '/TEI/teiHeader/encodingDesc/variantEncoding/attribute::method' );
+ if( $venc && $venc eq 'double-end-point' ) {
+ $type = 'CTE';
+ } else {
+ $type = 'TEI';
+ }
+ }
+ }
+ # Try the relevant XML parsing option.
+ if( $type ) {
+ delete $newopts{'file'};
+ $newopts{'xmlobj'} = $doc;
try {
$tradition = Text::Tradition->new( %newopts, 'input' => $type );
} catch ( Text::Tradition::Error $e ) {
$errmsg = $e->message;
- } catch {
- $errmsg = "Unexpected parsing error";
- }
- if( $tradition ) {
- $errmsg = undef;
- last;
+ } catch ( $e ) {
+ $errmsg = "Unexpected parsing error: $e";
}
}
} elsif( $ext =~ /^(txt|csv|xls(x)?)$/ ) {
);
} catch ( Text::Tradition::Error $e ) {
$errmsg = $e->message;
- } catch {
- $errmsg = "Unexpected parsing error";
+ } catch ( $e ) {
+ $errmsg = "Unexpected parsing error: $e";
}
} else {
# Error unless we have a recognized filename extension
$tradition->name( $newname );
$changed = 1;
} catch {
- return _json_error( $c, 500, "Error setting name to $newname" );
+ return _json_error( $c, 500, "Error setting name to $newname: $@" );
}
}
}
$tradition->language( $langval );
$changed = 1;
} catch {
- return _json_error( $c, 500, "Error setting language to $langval" );
+ return _json_error( $c, 500, "Error setting language to $langval: $@" );
}
}
if( exists $params->{'owner'} ) {
# Only admins can update user / owner
my $newownerid = delete $params->{'owner'};
+ if( $tradition->has_user && !$tradition->user ) {
+ $tradition->clear_user;
+ }
unless( !$newownerid ||
( $tradition->has_user && $tradition->user->email eq $newownerid ) ) {
unless( $c->user->get_object->is_admin ) {
my $textinfo = {
textid => $textid,
name => $tradition->name,
- #language => $tradition->language,
public => $tradition->public || 0,
owner => $tradition->user ? $tradition->user->email : undef,
witnesses => [ map { $_->sigil } $tradition->witnesses ],
};
+ ## TODO Make these into callbacks in the other controllers maybe?
if( $tradition->can('language') ) {
$textinfo->{'language'} = $tradition->language;
}
- my @stemmasvg = map { $_->as_svg() } $tradition->stemmata;
- map { $_ =~ s/\n/ /mg } @stemmasvg;
+ if( $tradition->can('stemweb_jobid') ) {
+ $textinfo->{'stemweb_jobid'} = $tradition->stemweb_jobid || 0;
+ }
+ my @stemmasvg = map { _stemma_info( $_ ) } $tradition->stemmata;
$textinfo->{stemmata} = \@stemmasvg;
$c->stash->{'result'} = $textinfo;
$c->forward('View::JSON');
$c->stash->{'result'} = $collation->as_svg;
$c->forward('View::SVG');
}
+
+sub _stemma_info {
+ my( $stemma, $sid ) = @_;
+ my $ssvg = $stemma->as_svg();
+ $ssvg =~ s/\n/ /mg;
+ my $sinfo = {
+ name => $stemma->identifier,
+ directed => _json_bool( !$stemma->is_undirected ),
+ svg => $ssvg };
+ if( $sid ) {
+ $sinfo->{stemmaid} = $sid;
+ }
+ return $sinfo;
+}
+
+## TODO Separate stemma manipulation functionality into its own controller.
=head2 stemma
if( !$stemma && $tradition->stemma_count > $stemmaid ) {
$stemma = $tradition->stemma( $stemmaid );
}
- my $stemma_xml = $stemma ? $stemma->as_svg() : '';
# What was requested, XML or JSON?
my $return_view = 'SVG';
if( my $accept_header = $c->req->header('Accept') ) {
}
}
if( $return_view eq 'SVG' ) {
- $c->stash->{'result'} = $stemma_xml;
+ $c->stash->{'result'} = $stemma->as_svg();
$c->forward('View::SVG');
} else { # JSON
- $stemma_xml =~ s/\n/ /mg;
- $c->stash->{'result'} = { 'stemmaid' => $stemmaid, 'stemmasvg' => $stemma_xml };
+ $c->stash->{'result'} = { _stemma_info( $stemma, $stemmaid ) };
$c->forward('View::JSON');
}
}
$c->forward('View::JSON');
}
+=head2 stemmaroot
+
+ POST /stemmaroot/$textid/$stemmaseq, { root: <root node ID> }
+
+Orients the given stemma so that the given node is the root (archetype). Returns the
+information structure for the new stemma.
+
+=cut
+
+sub stemmaroot :Local :Args(2) {
+ my( $self, $c, $textid, $stemmaid ) = @_;
+ my $m = $c->model('Directory');
+ my $tradition = $m->tradition( $textid );
+ unless( $tradition ) {
+ return _json_error( $c, 404, "No tradition with ID $textid" );
+ }
+ my $ok = _check_permission( $c, $tradition );
+ if( $ok eq 'full' ) {
+ my $stemma = $tradition->stemma( $stemmaid );
+ try {
+ $stemma->root_graph( $c->req->param('root') );
+ $m->save( $tradition );
+ } catch( Text::Tradition::Error $e ) {
+ return _json_error( $c, 400, $e->message );
+ } catch {
+ return _json_error( $c, 500, "Error re-rooting stemma: $@" );
+ }
+ $c->stash->{'result'} = _stemma_info( $stemma );
+ $c->forward('View::JSON');
+ } else {
+ return _json_error( $c, 403,
+ 'You do not have permission to update stemmata for this tradition' );
+ }
+}
+
+=head2 download
+
+ GET /download/$textid
+
+Returns the full XML definition of the tradition and its stemmata, if any.
+
+=cut
+
+sub download :Local :Args(1) {
+ my( $self, $c, $textid ) = @_;
+ my $tradition = $c->model('Directory')->tradition( $textid );
+ unless( $tradition ) {
+ return _json_error( $c, 404, "No tradition with ID $textid" );
+ }
+ my $ok = _check_permission( $c, $tradition );
+ return unless $ok;
+ try {
+ $c->stash->{'result'} = $tradition->collation->as_graphml();
+ } catch( Text::Tradition::Error $e ) {
+ return _json_error( $c, 500, $e->message );
+ }
+ $c->forward('View::GraphML');
+}
+
####################
### Helper functions
####################
return 0;
}
+sub _json_bool {
+ return $_[0] ? JSON::true : JSON::false;
+}
+
=head2 default
Standard 404 error page