File Coverage

blib/lib/WebService/Moodle/Simple.pm
Criterion Covered Total %
statement 29 152 19.0
branch 0 18 0.0
condition 0 5 0.0
subroutine 10 25 40.0
pod 0 13 0.0
total 39 213 18.3


line stmt bran cond sub pod time code
1             package WebService::Moodle::Simple;
2              
3 3     3   47841 use 5.008_005;
  3         10  
4             our $VERSION = '0.05';
5              
6 3     3   1771 use Moo;
  3         37066  
  3         13  
7 3     3   5229 use namespace::clean;
  3         32101  
  3         12  
8 3     3   2451 use URI;
  3         18191  
  3         100  
9 3     3   1560 use HTTP::Request;
  3         41090  
  3         93  
10 3     3   2036 use LWP::UserAgent;
  3         55297  
  3         122  
11 3     3   1760 use Sys::SigAction qw( timeout_call );
  3         35178  
  3         168  
12 3     3   1855 use JSON;
  3         28251  
  3         14  
13 3     3   1913 use Ouch;
  3         5276  
  3         199  
14 3     3   45 use List::Util qw/first/;
  3         4  
  3         4981  
15              
16             my $REST_FORMAT = 'json';
17             # https://moodle.org/mod/forum/discuss.php?d=340377
18             my $STUDENT_ROLE_ID = 5;
19              
20             # the base domain like moodle.example.com
21             has domain => (
22             is => 'ro',
23             );
24              
25             has port => (
26             is => 'rw',
27             default => 80,
28             );
29              
30             has timeout => (
31             is => 'rw',
32             default => 1000,
33             );
34              
35             has target => (
36             is => 'rw',
37             );
38              
39              
40             has scheme => (
41             is => 'rw',
42             default => 'http',
43             );
44              
45             sub dns_uri {
46 0     0 0   my $self = shift;
47 0           return URI->new($self->scheme.'://'.$self->domain.':'.$self->port);
48             }
49              
50             sub rest_call {
51 0     0 0   my ($self, $dns_uri) = @_;
52              
53 0           my $res;
54              
55 0           my $timeout = $self->timeout + 1;
56              
57 0 0         if (timeout_call( $timeout,
58             sub {
59 0     0     my $req = HTTP::Request->new (GET => $dns_uri);
60 0           $req->content_type('application/json');
61              
62 0           my $lwp = LWP::UserAgent->new;
63 0           $lwp->timeout($timeout);
64 0           $res = $lwp->request( $req );
65             })) {
66 0           ouch 408, 'Timeout: no response from '.$self->domain;
67             }
68              
69 0 0         unless ($res->is_success) {
70 0           ouch $res->code, $res->message;
71             }
72              
73 0           return $res;
74             }
75              
76             sub token {
77 0     0 0   my $self = shift;
78 0           my %args = (
79             username => undef,
80             password => undef,
81             @_
82             );
83              
84 0           my $dns_uri = $self->dns_uri;
85 0           $dns_uri->path('login/token.php');
86             $dns_uri->query_form( {
87             username => $args{username},
88             password => $args{password},
89 0           service => $self->target,
90             });
91              
92 0           my $res = $self->rest_call($dns_uri);
93              
94 0           return from_json($res->content);
95             }
96              
97             sub login {
98 0     0 0   my $self = shift;
99 0           my %args = (
100             username => undef,
101             password => undef,
102             @_
103             );
104              
105 0           my $username = $args{username};
106 0           my $password = $args{password};
107 0           my $target = $self->target;
108              
109 0           my $token = $self->token(
110             username => $username,
111             password => $password,
112             );
113              
114              
115              
116 0 0         if (defined($token->{token})) {
    0          
    0          
117             return {
118             msg => "'$username' has access to the '$target' web service",
119             ok => 1,
120             token => $token->{token},
121 0           };
122             }
123             elsif ($token->{error} =~ m/No permission/) {
124             return {
125 0           msg => "'$username' has the correct password but no access to the '$target' web service",
126             ok => 1,
127             };
128             }
129             elsif ($token->{error}) {
130             # a bit of a guess as to why, but the message has changed across Moodle verions
131             return {
132 0           msg => "'$username' does not exist, or did not enter the correct password",
133             ok => 0,
134             };
135             }
136             else {
137 0           ouch 'MSE:0001', "Service '$target': ".$token->{error}, $token;
138             }
139              
140             }
141              
142             sub set_password {
143 0     0 0   my $self = shift;
144 0           my %args = (
145             username => undef,
146             password => undef,
147             token => undef,
148             @_
149             );
150              
151              
152 0           my $username = lc($args{username});
153              
154              
155             my $params = {
156             'wstoken' => $args{token},
157             'wsfunction' => "core_user_update_users",
158             'moodlewsrestformat' => $REST_FORMAT,
159             'users[0][id]' => $self->get_user(token => $args{token}, username => $username )->{id},
160             'users[0][password]' => $args{password},
161 0           };
162              
163              
164 0           my $dns_uri = $self->dns_uri;
165 0           $dns_uri->path('webservice/rest/server.php');
166 0           $dns_uri->query_form( $params );
167              
168 0           my $res = $self->rest_call($dns_uri);
169              
170 0           return $res->is_success;
171             }
172              
173             sub add_user {
174 0     0 0   my $self = shift;
175 0           my %args = (
176             firstname => undef,
177             lastname => undef,
178             email => undef,
179             username => undef,
180             token => undef,
181             password => undef,
182             @_
183             );
184              
185              
186 0           my $username = lc($args{username});
187              
188             my $params = {
189             'wstoken' => $args{token},
190             'wsfunction' => "core_user_create_users",
191             'moodlewsrestformat' => $REST_FORMAT,
192             'users[0][username]' => $username,
193             'users[0][email]' => $args{email},
194             'users[0][firstname]' => $args{firstname},
195             'users[0][lastname]' => $args{lastname},
196             'users[0][password]' => $args{password},
197 0           };
198              
199              
200 0           my $dns_uri = $self->dns_uri;
201 0           $dns_uri->path('webservice/rest/server.php');
202 0           $dns_uri->query_form( $params );
203              
204 0           my $res = $self->rest_call($dns_uri);
205              
206 0           return from_json($res->content)->[0];
207             }
208              
209             sub get_user {
210 0     0 0   my $self = shift;
211 0           my %args = (
212             username => undef,
213             token => undef,
214             @_
215             );
216              
217              
218 0           my $username = lc($args{username});
219              
220             my $params = {
221             'wstoken' => $args{token},
222 0           'wsfunction' => "core_user_get_users_by_field",
223             'moodlewsrestformat' => $REST_FORMAT,
224             'field' => 'username',
225             'values[0]' => $username,
226             };
227              
228              
229 0           my $dns_uri = $self->dns_uri;
230 0           $dns_uri->path('webservice/rest/server.php');
231 0           $dns_uri->query_form( $params );
232              
233 0           my $res = $self->rest_call($dns_uri);
234              
235 0           my $dat = from_json($res->content);
236 0 0 0       unless ($dat && ref($dat) eq 'ARRAY' && scalar(@$dat)) {
      0        
237 0           return;
238             }
239 0           return $dat->[0];
240             }
241              
242              
243             sub get_users {
244 0     0 0   my $self = shift;
245 0           my %args = (
246             token => undef,
247             @_
248             );
249              
250             my $params = {
251             'wstoken' => $args{token},
252 0           'wsfunction' => "core_user_get_users",
253             'moodlewsrestformat' => $REST_FORMAT,
254             'criteria[0][key]' => 'dummyparam',
255             'criteria[0][value]' => '',
256             };
257              
258              
259 0           my $dns_uri = $self->dns_uri;
260 0           $dns_uri->path('webservice/rest/server.php');
261 0           $dns_uri->query_form( $params );
262              
263 0           my $res = $self->rest_call($dns_uri);
264              
265 0           my $rh_res = from_json($res->content);
266 0           return $rh_res->{users};
267             }
268              
269             sub enrol_student {
270 0     0 0   my $self = shift;
271 0           my %args = (
272             username => undef,
273             course => undef,
274             token => undef,
275             @_
276             );
277              
278             my $user_id = $self->get_user(
279             token => $args{token},
280             username => $args{username}
281 0           )->{id};
282            
283              
284             my $params = {
285             'wstoken' => $args{token},
286             'wsfunction' => "enrol_manual_enrol_users",
287             'moodlewsrestformat' => $REST_FORMAT,
288             'enrolments[0][roleid]' => $STUDENT_ROLE_ID,
289             'enrolments[0][userid]' => $user_id,
290 0           'enrolments[0][courseid]' => $self->get_course_id ( token => $args{token}, short_cname => $args{course} ),
291             };
292              
293              
294 0           my $dns_uri = $self->dns_uri;
295 0           $dns_uri->path('webservice/rest/server.php');
296 0           $dns_uri->query_form( $params );
297              
298 0           my $res = $self->rest_call($dns_uri);
299 0 0         unless ($res->is_success) {
300 0           ouch $res->code, $res->message;
301             }
302 0           return 1;
303             }
304              
305             sub get_course_id {
306 0     0 0   my $self = shift;
307 0           my %args = (
308             short_cname => undef,
309             token => undef,
310             @_
311             );
312              
313 0           my $dns_uri = $self->dns_uri;
314 0           $dns_uri->path('webservice/rest/server.php');
315             $dns_uri->query_form( {
316             wstoken => $args{token},
317 0           wsfunction => 'core_course_get_courses',
318             moodlewsrestformat => $REST_FORMAT,
319             } );
320              
321 0           my $res = $self->rest_call($dns_uri);
322 0           my $ra_courses = from_json($res->content);
323 0           foreach my $rh_course (@$ra_courses) {
324 0 0         if ($rh_course->{shortname} eq $args{short_cname}) {
325 0           return $rh_course->{id};
326             }
327             }
328              
329              
330 0           ouch 'MSE-0002', 'failed to find course of name '.$args{short_cname};
331             }
332              
333             # DEPRECATED: this depends on core_role_get_all_roles which isn't in the
334             # core Moodle distribution.
335             sub get_student_role {
336 0     0 0   my $self = shift;
337 0           my %args = (
338             token => undef,
339             @_
340             );
341              
342 0           my $dns_uri = $self->dns_uri;
343 0           $dns_uri->path('webservice/rest/server.php');
344             $dns_uri->query_form( {
345             wstoken => $args{token},
346 0           wsfunction => 'core_role_get_all_roles',
347             moodlewsrestformat => $REST_FORMAT,
348             } );
349              
350 0           my $res = $self->rest_call($dns_uri);
351 0           my $ra_roles = from_json($res->content);
352              
353 0     0     my $rh_student_role = first { $_->{shortname} eq 'student' } @$ra_roles;
  0            
354 0           return $rh_student_role;
355             }
356              
357              
358             # WARNING: suspend_user depends on the 'suspended' parameter
359             # https://tracker.moodle.org/browse/MDL-31465
360              
361             sub suspend_user {
362 0     0 0   my $self = shift;
363 0           my %args = (
364             token => undef,
365             username => undef,
366             suspend => 1, # suspend unless it is 0
367             @_
368             );
369              
370 0           my $mdl_user = $self->get_user( username => $args{username}, token => $args{token});
371              
372             my $params = {
373             'wstoken' => $args{token},
374             'wsfunction' => "core_user_update_users",
375             'moodlewsrestformat' => $REST_FORMAT,
376             'users[0][id]' => $mdl_user->{id},
377 0           'users[0][suspended]' => $args{suspend} + 0,
378             };
379              
380              
381 0           my $dns_uri = $self->dns_uri;
382 0           $dns_uri->path('webservice/rest/server.php');
383 0           $dns_uri->query_form( $params );
384              
385 0           my $res = $self->rest_call($dns_uri);
386              
387 0           return $res->content;
388             }
389              
390              
391             sub raw_api {
392 0     0 0   my $self = shift;
393 0           my %args = (
394             token => undef,
395             method => undef,
396             params => {},
397             @_
398             );
399              
400             my $params = {
401             'wstoken' => $args{token},
402             'wsfunction' => $args{method},
403             'moodlewsrestformat' => $REST_FORMAT,
404 0           %{$args{params}}
  0            
405             };
406              
407              
408 0           my $dns_uri = $self->dns_uri;
409 0           $dns_uri->path('webservice/rest/server.php');
410 0           $dns_uri->query_form( $params );
411              
412 0           my $res = $self->rest_call($dns_uri);
413              
414 0 0         return if $res->content eq 'null';
415 0           return from_json($res->content);
416             }
417              
418              
419             1;
420             __END__