#!/usr/bin/env perl
#
# list Lessons pages, using SOAP methods added by EDIA
#
# $Id$
use strict;
use warnings;
use open qw(:std :utf8);
use SOAP::Lite;
#use SOAP::Lite +trace => [ 'all' ];
use File::Path qw(make_path);
use Data::Dumper;
$Data::Dumper::Indent = 1;
use XML::LibXML;
use XML::LibXML::XPathContext;
use Getopt::Std;
use open ':std', ':encoding(UTF-8)'; # support UTF-8 output
#--- parameter specification: ---#
#
my %opt;
getopts( 'H:P:hp:u:N?', \%opt ) or die "Cannot parse command line\n";
HELP_MESSAGE() if $opt{h} || $opt{'?'};
my $rx = do
{
my $regex = @ARGV ? join( ' ', @ARGV ) : '';
qr($regex)
or die "invalid argument, not a regular expression: $regex\n";
};
my $user = $opt{u} // $ENV{SAKAI_USER} // 'admin';
my $password = $opt{p} // $ENV{SAKAI_PASSWORD} // 'minda';
my $host = $opt{H} // $ENV{SAKAI_HOST} // 'sakai2.win.tue.nl';
my $port = $opt{P} // $ENV{SAKAI_PORT} // ( $opt{N} ? 80 : 443 );
my $http = $opt{N} ? 'http' : ( $ENV{SAKAI_PROTOCOL} // 'https' );
my $server = "$http://$host:$port/";
my $ns_uri = 'http://webservices.sakaiproject.org/';
#--- Usage ---#
#
sub HELP_MESSAGE
{
print STDERR <uri($ns_uri)->proxy($proxy)->on_action( sub { return '' } )
or die "No SOAP login object for service $proxy\n";
}
sub method
{
SOAP::Data->name( "ns1:" . $_[0] )->attr( { 'xmlns:ns1' => $ns_uri } );
}
sub soap_data_name
{
#warn sprintf("SOAP parameter: %s => %s\n", @_);
SOAP::Data->name(@_)->type('string') # assessmentIds are strings!
}
sub param
{
# feed the arguments pairwise to SOAP::Data->name()
map { soap_data_name( $_[$_], $_[ $_ + 1 ] ) }
grep { !( $_ % 2 ) } ( 0 .. $#_ );
}
sub call
{
my ( $service, $method, @params ) = @_;
$service->call( method($method), param(@params) )
or die sprintf( "failed call %s(%s)\n", $method, join( ', ', @params ) );
}
#--- for deconstructing output: ---#
#
my $xml_parser = new XML::LibXML(
line_numbers => 1,
load_ext_dtd => 0, # disables validation, says the documentation
no_blanks => 1,
clean_namespaces => 1,
no_network => 1,
pedantic_parser => 1
) or die "cannot create the XML parser\n";
my $xpath_context = new XML::LibXML::XPathContext;
sub xmldoc
{
if ( my $doc = eval { $xml_parser->parse_string( $_[0] ) } )
{
$doc->getDocumentElement;
}
else
{
warn "cannot parse SOAP result: $@\n";
undef # ! EDIA's SOAP methods can return an empty document
}
}
sub xpath
{
my ( $xpr, $node ) = @_;
if ( !defined $node )
{
return ( wantarray ? () : undef );
}
ref($node) and $node->can('findnodes')
or die "bug: xpath() called with wrong argument(s)\n";
my @nodes = $xpath_context->findnodes(@_)->get_nodelist;
wantarray() ? @nodes : $nodes[0];
}
#sub assessment_title
#{
# my ($xmldoc) = @_;
#
# if ( $format eq 'QTI_1_2' )
# {
# my @assmt = xpath( '//assessment', $xmldoc )
# // die "cannot find assessment, malformed QTI?\n";
# my $title = $assmt[0]->getAttribute('title')
# // die "cannot find assessment title, malformed QTI?\n";
# $title;
# }
# else
# {
# die "sorry, cannot determine assessment title for format $format\n";
# }
#}
#--- here we go ---#
#
my $login_service = service('login');
#my $login = $login_service->loginToServer(id=>$user, pw=>$password)
my $login = call(
$login_service,
'loginToServer',
'id' => $user,
'pw' => $password
);
my ( $session, $server_direct ) = split( /,/, $login->result )
or die "Cannot log in as $user to site $server\n";
#warn "session is: $session\n";
my $sites =
call( service('sakai'), 'getAllSitesForCurrentUser', 'sessionid' => $session );
#print $sites->result;
# returns - texttext
my %siteid2title;
foreach my $site ( xpath( '//list/item', xmldoc( $sites->result ) ) )
{
my ($id) = xpath( 'siteId', $site )->textContent;
my ($title) = xpath( 'siteTitle', $site )->textContent;
if ( $title =~ /$rx/ )
{
$siteid2title{$id} = $title;
}
}
my @siteids =
sort { $siteid2title{$a} cmp $siteid2title{$b} } keys %siteid2title;
my $lessons_service = service('lessons');
sub site2pages
# given a site id,
# returns the list of its pages
{
my ($site_id) = @_;
my $pages = call(
$lessons_service,
'getPagesForSite',
'sessionId' => $session,
'siteId' => $site_id
) or die "cannot call lessons.getPagesForSite()\n";
print $pages->result, "\n";
# example output for a page with 1 text item and 1 embedded video:
#
# 45d49a9a1-ab5d-4855-9f15-08e68f12abc4Lessons40f4ce4d-8932-469e-94bf-3a812c7410f7false1651The first item is a text item.
# ]]>1772 <iframe width="560" height="315" src="https://www.youtube.com/embed/SfWwmJZOwZg" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>1admin
my @content_attrs = qw(id type sequence html);
#my @items;
warn "in site [$siteid2title{$site_id}],\n";
my @pages = xpath( '/pages/page', xmldoc( $pages->result ) );
warn " there are no Lessons pages\n" if !@pages;
foreach my $page ( xpath( '/pages/page', xmldoc( $pages->result ) ) )
{
my $pageId = xpath( 'pageId', $page )->textContent;
warn " Lessons page $pageId has\n";
my @items = xpath( 'contents/content', $page );
warn " no items\n" if !@items;
foreach my $content (@items)
{
my @values = map { xpath( $_, $content )->textContent } @content_attrs;
my @direct_attrs =
map { $content_attrs[$_] . ': ' . $values[$_] } 0 .. $#content_attrs;
my @attributes = xpath( 'attributes/*', $content );
# there may be 0 of these
my @attr_attrs =
map { 'attrs/' . $_->nodeName . ': ' . $_->textContent } @attributes;
my $values = join( ', ', @direct_attrs, @attr_attrs );
warn " an item [$values]\n";
}
}
# @items;
}
foreach my $id (@siteids)
{
site2pages($id);
}
#my @assmts = site2assessments($site_id);
#warn sprintf( "%d assessments in site %s\n", scalar(@assmts), $sitename );
#warn join( ' ', 'with ids:', map { $_->{id} } @assmts ), "\n";