#!/usr/bin/env perl # # list Sakai courses (sites) # # $Id$ # based on ./content-lists-test.pl use strict; use warnings; # uses ubuntu package libsoap-lite-perl use SOAP::Lite; #use SOAP::Lite +trace => [ all ]; use Data::Dumper; $Data::Dumper::Indent = 1; # uses ubuntu package libxml-libxml-perl use XML::LibXML; use XML::LibXML::XPathContext; use Getopt::Std; use open ':std', ':encoding(UTF-8)'; # support UTF-8 output #--- parameter specification: ---# # # // means if empty then use # # Usage: (from TU/e, e.g. from sakai2.win.tue.nl) # ./sakai-list-courses -u -p # # or set SAKAI_USER and SAKAI_PASSWORD # # NOTE: to keep passwords out of bash history, precede the command with a space # (see also the HISTCONTROL environment variable, which can be set to # ignorespace or ignoreboth, i.e. also ignores duplicate commands) my %opt; getopts( 'H:P:p:u:Nf:', \%opt ) or die "Cannot parse command line\n"; my $user = $opt{u} // $ENV{SAKAI_USER} // 'admin'; my $password = $opt{p} // $ENV{SAKAI_PASSWORD} // 'minda'; my $host = $opt{H} // $ENV{SAKAI_HOST} // 'future.update.eitdigital.eu'; my $port = $opt{P} // $ENV{SAKAI_PORT} // ( $opt{N} ? 80 : 443 ); my $http = $opt{N} ? 'http' : ( $ENV{SAKAI_PROTOCOL} // 'https' ); my $format = $opt{f} // $ENV{QTI_FORMAT} // 'QTI_1_2'; my @formats = qw(QTI_1_2 QTI_2_0 Markup); grep { $format eq $_ } @formats or die join( ' ', 'format must be one of:', @formats ), "\n"; my $server = "$http://$host:$port/"; my $ns_uri = 'http://webservices.sakaiproject.org/'; #--- call input construction (SOAP related functions): ---# # sub service { my $proxy = $server . 'sakai-ws/soap/' . $_[0]; SOAP::Lite->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, jon( ', ', @params ) ); } #--- for deconstructing output (XML parsing and searching functions): ---# # 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 { my $doc = eval { $xml_parser->parse_string( $_[0] ) } or die "cannot parse SOAP result: $@\n"; $doc->getDocumentElement; } sub xpath { ref( $_[1] ) and $_[1]->can('findnodes') or die "bug: xpath() called with wrong argument(s)\n"; my @nodes = $xpath_context->findnodes(@_)->get_nodelist; wantarray() ? @nodes : $nodes[0]; } #--- here we go (initialisation) ---# # my $login_service = service('login'); #my $login = $login_service->loginToServer(id=>$user, pw=>$password) my $login = call( $login_service, 'loginToServer', 'id' => $user, 'pw' => $password ) or die "Cannot log in as $user to site $server\n"; my ( $session, $server_direct ) = split( /,/, $login->result // '' ) or die "Cannot log in as $user to site $server\n"; warn "session is: $session\n"; my $sakai_service = service('sakai'); # --- perform soap calls: --- # # to get an overview of the services type: (on a host that has access, like localhost) # w3m https://sakai2.win.tue.nl/sakai-ws/ # w3m https://future.update.eitdigital.eu/sakai-ws/ # w3m https://update.eitdigital.eu/sakai-ws/ # SOAP CALL: get current user name my $user_response = call( $sakai_service, 'getUserDisplayNameForCurrentUser', 'sessionid' => $session ) or die "Cannot get display name for $user\n"; # --- show output current user: --- # warn "the current user is ", $user_response->result, "\n"; # to get site types: use ContentHosting/getAllSitesCollectionSize (or for every site) # #my $site_info = call( $sakai_service, 'getAllSitesCollectionSize', 'sessionid' => $session) # or die("could not find site info"); # #warn "site info is ", $site_info->result, "\n"; # SOAP CALL: get sites for current user my $sites = call( $sakai_service, 'getAllSitesForCurrentUser', 'sessionid' => $session ); # or die #print $sites->result; # returns texttext # SOAP call: get the type of a given site sub get_site_type # NOTE: we've added getSiteType to Sakai ourselves! { my ($id) = @_; my $response = call( $sakai_service, 'getSiteType', 'sessionid' => $session, 'siteid' => $id ) or die "cannot get type of site $id\n"; my $type = $response->result; } # --- print for each site : id, title, type (? on unpatched Sakais) --- # foreach my $site ( xpath( '//list/item', xmldoc( $sites->result ) ) ) { my ($id) = xpath( 'siteId', $site )->textContent; my ($title) = xpath( 'siteTitle', $site )->textContent; my $type = get_site_type($id) // '?'; my $hyphenated = index( $id, "-" ) != -1 ? "hyphenated" : "unhyphenated"; warn "$hyphenated $type site $id has title $title\n"; }