roles admin: Fix notice when creating a role with no legacy type.
[moodle.git] / webservice / lib.php
CommitLineData
06e7fadc 1<?php
2/**
3 * Moodle - Modular Object-Oriented Dynamic Learning Environment
4 * http://moodle.com
5 *
6 * LICENSE
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details:
17 *
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @category Moodle
21 * @package webservice
22 * @copyright Copyright (c) 1999 onwards Martin Dougiamas http://dougiamas.com
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL License
24 */
25
24350e06 26require_once(dirname(dirname(__FILE__)) . '/lib/formslib.php');
06e7fadc 27
28/**
29 * web service library
30 */
31final class webservice_lib {
32
33 /**
34 * Return list of all web service protocol into the webservice folder
35 * @global <type> $CFG
36 * @return <type>
37 */
38 public static function get_list_protocols() {
39 global $CFG;
40 $protocols = array();
41 $directorypath = $CFG->dirroot . "/webservice";
42 if( $dh = opendir($directorypath)) {
43 while( false !== ($file = readdir($dh)))
44 {
45 if( $file == '.' || $file == '..' || $file == 'CVS') { // Skip '.' and '..'
46 continue;
47 }
48 $path = $directorypath . '/' . $file;
40f024c9 49 ///browse the subfolder
06e7fadc 50 if( is_dir($path) ) {
40f024c9 51 require_once($path."/lib.php");
52 $classname = $file."_server";
53 $protocols[] = new $classname;
06e7fadc 54 }
40f024c9 55 ///retrieve api.php file
06e7fadc 56 else {
57 continue;
58 }
59 }
60 closedir($dh);
61 }
62 return $protocols;
63 }
64
65 /**
66 * Temporary Authentication method to be modified/removed
67 * @global <type> $DB
68 * @param <type> $token
69 * @return <type>
70 */
71 public static function mock_check_token($token) {
72 //fake test
73 if ($token == 465465465468468464) {
74 ///retrieve the user
75 global $DB;
76 $user = $DB->get_record('user', array('username'=>'wsuser', 'mnethostid'=>1));
77
78 if (empty($user)) {
79 return false;
80 }
81
82 return $user;
83 } else {
84 return false;
85 }
86 }
87
24350e06 88 /**
89 * Retrieve all external.php from Moodle (except the one of the exception list)
90 * @param <type> $
91 * @param <type> $directorypath
92 * @return boolean true if n
93 */
94 public static function setListApiFiles( &$files, $directorypath )
95 {
96 global $CFG;
97
98 if(is_dir($directorypath)){ //check that we are browsing a folder not a file
99
100 if( $dh = opendir($directorypath))
101 {
102 while( false !== ($file = readdir($dh)))
103 {
104
105 if( $file == '.' || $file == '..') { // Skip '.' and '..'
106 continue;
107 }
108 $path = $directorypath . '/' . $file;
40f024c9 109 ///browse the subfolder
24350e06 110 if( is_dir($path) ) {
40f024c9 111 webservice_lib::setListApiFiles($files, $path);
24350e06 112 }
40f024c9 113 ///retrieve api.php file
24350e06 114 else if ($file == "external.php") {
115 $files[] = $path;
116 }
117 }
118 closedir($dh);
119
120 }
121 }
122
123 }
124
40f024c9 125 /**
3fba577b 126 * Generate web service description array from the phpdoc for a given class
127 * @param string $file the class file
128 * @param string $class the class name
129 * @return array description
130 *
131 *
132 -------
133 Example
134 -------
135 * Docnlock: @ subparam string $params:searches->search - the string to search
136 * $params is considered as the first element, searches the second, and search the terminal
137 * Except the terminal element, all other will be generated as an array
138 * => left element are generated as an associative array.
139 * If the following character is ':' so the right element is a key named 'multiple:element_name'
140 * If the following character is '->' so the right element will be named 'element_name'
141 * Rule: If a key is named 'multiple:xxx' other key must be 'multiple:yyy'
142
143 Docblock of mock_function
144 ---------------------------
145 @ param array|struct $params
146 @ subparam string $params:searches->search - the string to search
147 @ subparam string $params:searches->search2 optional - optional string to search
148 @ subparam string $params:searches->search3 - the string to search
149 @ subparam string $params:airport->planes:plane->company->employees:employee->name - name of a employee of a company of a plane of an airport
150 @ return array users
151 @ subreturn integer $users:user->id
152 @ subreturn integer $users:user->auth
153
154 Generated description array
155 ---------------------------
156 description["mock_function"]=>
157 array(3) {
158 ["params"]=>
159 array(2) {
160 ["multiple:searches"]=>
161 array(2) {
162 ["search"]=>
163 string(6) "string"
164 ["search3"]=>
165 string(6) "string"
166 }
167 ["multiple:airport"]=>
168 array(1) {
169 ["planes"]=>
170 array(1) {
171 ["multiple:plane"]=>
172 array(1) {
173 ["company"]=>
174 array(1) {
175 ["employees"]=>
176 array(1) {
177 ["multiple:employee"]=>
178 array(1) {
179 ["name"]=>
180 string(6) "string"
181 }
182 }
183 }
184 }
185 }
186 }
187 }
188 ["optional"]=>
189 array(1) {
190 ["multiple:searches"]=>
191 array(1) {
192 ["search2"]=>
193 string(6) "string"
194 }
195 }
196 ["return"]=>
197 array(1) {
198 ["multiple:user"]=>
199 array(13) {
200 ["id"]=>
201 string(7) "integer"
202 ["auth"]=>
203 string(7) "integer"
204 }
205 }
40f024c9 206 */
207 public static function generate_webservice_description($file, $class){
208 require_once($file);
209 require_once "Zend/Loader.php";
210 Zend_Loader::registerAutoload();
211 $reflection = Zend_Server_Reflection::reflectClass($class);
212 $description = array();
213
214 foreach($reflection->getMethods() as $method){
215
7c9152bc 216 $docBlock = $method->getDocComment();
40f024c9 217
7c9152bc 218 //retrieve the return and add it into the description if not array|object
219 preg_match_all('/@return\s+(\w+)\s+((?:\$)?\w+)/', $docBlock, $returnmatches);
40f024c9 220
7c9152bc 221 //retrieve the subparam and subreturn
222 preg_match_all('/\s*\*\s*@(subparam|subreturn)\s+(\w+)\s+(\$\w+(?::\w+|->\w+)+)((?:\s+(?:optional|required|multiple))*)/', $docBlock, $matches);
40f024c9 223
3fba577b 224 /// process every @subparam and @subreturn line of the docblock
7c9152bc 225 for($i=0;$i<sizeof($matches[1]);$i++){
3fba577b 226
227 /// identify the description type of the docblock line: is it params, optional or return (first key of a description method array)
7c9152bc 228 switch ($matches[1][$i]) {
229 case "subparam":
230 if (strpos($matches[4][$i], "optional")!==false) {
231 $descriptiontype = "optional";
232 } else {
233 $descriptiontype = "params" ;
234 }
235 break;
236 case "subreturn":
237 $descriptiontype = "return";
238 break;
239 }
40f024c9 240
3fba577b 241 /// init description[method]
7c9152bc 242 if (empty($description[$method->getName()])) {
243 $description[$method->getName()] = array();
244 }
245
3fba577b 246 /// directly set description[method][return] if the return value is a primary type
7c9152bc 247 if (strpos($returnmatches[1][0] ,"object")===false && strpos($returnmatches[1][0],"array")===false) {
248 $description[$method->getName()]['return'] = array($returnmatches[2][0] => $returnmatches[1][0]);
249 }
40f024c9 250
7c9152bc 251
3fba577b 252 ///algorythm parts
253 ///1. We compare the string to the description array
254 /// When we find a attribut that is not in the description, we retrieve all the rest of the string
255 ///2. We create the missing part of the description array, starting from the end of the rest of the string
256 ///3. We add the missing part to the description array
7c9152bc 257
3fba577b 258 ///Part 1.
7c9152bc 259
7c9152bc 260
3fba577b 261 /// extract the first part into $param (has to be $params in the case of @subparam, or anything in the case of $subreturn)
262 /// extract the second part
263 if (strpos($matches[3][$i], "->")===false || (strpos($matches[3][$i], ":")!==false && (strpos($matches[3][$i], ":") < strpos($matches[3][$i], "->")))) {
264 $separator = ":";
265 $separatorsize=1;
7c9152bc 266
3fba577b 267 } else {
268 $separator = "->";
269 $separatorsize=2;
270 }
40f024c9 271
3fba577b 272 $param = substr($matches[3][$i],1,strpos($matches[3][$i], $separator)-1); //first element/part/array
273 //for example for the line @subparam string $params:students->student->name
274 // @params is the first element/part/array of this docnlock line
275 // students is the second element/part/array
276 // ...
277 // name is the terminal element, this element will be generated as String here
40f024c9 278
40f024c9 279
3fba577b 280 $otherparam = substr($matches[3][$i],strpos($matches[3][$i], $separator)+$separatorsize); //rest of the line
281 $parsingdesc = $description[$method->getName()]; //$pasingdesc is the current position of the algorythm into the description array
282 //it is used to check if a element already exist into the description array
40f024c9 283
3fba577b 284 if (!empty($parsingdesc) && array_key_exists($descriptiontype, $parsingdesc)){
285 $parsingdesc = $parsingdesc[$descriptiontype];
286 }
287 $descriptionpath=array(); //we save in this variable the description path (e.g all keys to go deep into the description array)
288 //it will be used to know where to add a new part the description array
40f024c9 289
3fba577b 290 $creationfinished = false; //it's used to stop the algorythm when we find a new element that we can add to the descripitoin
291 unset($type);
40f024c9 292
3fba577b 293 /// try to extract the other elements and add them to the descripition id there are not already in the description
294 while(!$creationfinished && (strpos($otherparam, ":") || strpos($otherparam, "->"))) {
295 if (strpos($otherparam, "->")===false || (strpos($otherparam, ":")!==false && (strpos($otherparam, ":") < strpos($otherparam, "->")))) {
296 $type = $separator;
40f024c9 297
3fba577b 298 $separator = ":";
299 $separatorsize=1;
300 } else {
301 $type = $separator;
302 $separator = "->";
303 $separatorsize=2;
304 }
40f024c9 305
3fba577b 306 $param = substr($otherparam,0,strpos($otherparam, $separator));
40f024c9 307
3fba577b 308 $otherparam = substr($otherparam,strpos($otherparam, $separator)+$separatorsize);
40f024c9 309
40f024c9 310
3fba577b 311 if ($type==":") {
312 /// this element is not already in the description array yet and it is a non associative array
313 /// we add it (and its sub structure) to the description array
314 if (!array_key_exists('multiple:'.$param, $parsingdesc)){
40f024c9 315
3fba577b 316 $desctoadd = webservice_lib::create_end_of_descriptionline(":".$param.$separator.$otherparam, $matches[2][$i]);
40f024c9 317
3fba577b 318 if(empty($descriptionpath) ) {
319 if (empty($description[$method->getName()]) || !array_key_exists($descriptiontype, $description[$method->getName()])) {
320 $desctoadd = array($descriptiontype => $desctoadd);
40f024c9 321 }
3fba577b 322 $paramtoadd = $descriptiontype;
40f024c9 323 } else {
3fba577b 324 $paramtoadd = 'multiple:'.$param;
40f024c9 325 }
3fba577b 326 webservice_lib::add_end_of_description($paramtoadd, $desctoadd, $description[$method->getName()], $descriptionpath);
327 $creationfinished = true; // we do not want to keep going to parse this line,
328 // neither add again the terminal element of the line to the descripiton
7c9152bc 329 } else {
3fba577b 330 if(empty($descriptionpath)) {
331 $descriptionpath[] = $descriptiontype;
7c9152bc 332 }
3fba577b 333 $descriptionpath[] = 'multiple:'.$param;
334 $parsingdesc = $parsingdesc['multiple:'.$param];
7c9152bc 335 }
3fba577b 336 } else {
337 /// this element is not in the description array yet and it is a associative array
338 /// we add it (and its sub structure) to the description array
339 if (!array_key_exists($param, $parsingdesc)){
40f024c9 340
3fba577b 341 $desctoadd = webservice_lib::create_end_of_descriptionline("->".$param.$separator.$otherparam, $matches[2][$i]);
7c9152bc 342
343 if(empty($descriptionpath)) {
3fba577b 344
7c9152bc 345 if (empty($description[$method->getName()]) || !array_key_exists($descriptiontype, $description[$method->getName()])) {
346 $desctoadd = array($descriptiontype => $desctoadd);
40f024c9 347 }
7c9152bc 348 $paramtoadd = $descriptiontype;
3fba577b 349
7c9152bc 350 } else {
3fba577b 351 $paramtoadd = $param;
7c9152bc 352 }
7c9152bc 353 webservice_lib::add_end_of_description($paramtoadd, $desctoadd, $description[$method->getName()], $descriptionpath);
354
3fba577b 355 $creationfinished = true; // we do not want to keep going to parse this line,
356 // neither add again the terminal element of the line to the descripiton
7c9152bc 357 } else {
7c9152bc 358 if(empty($descriptionpath)) {
3fba577b 359 $descriptionpath[] = $descriptiontype;
360 }
361 $descriptionpath[] = $param;
362 $parsingdesc = $parsingdesc[$param];
363 }
364 }
40f024c9 365
3fba577b 366 }
367 /// Add the "terminal" element of the line to the description array
368 if (!$creationfinished) {
7c9152bc 369
3fba577b 370 if (!empty($type) && $type==":") {
371
372 $desctoadd = webservice_lib::create_end_of_descriptionline($separator.$otherparam, $matches[2][$i]);
373
374 if(empty($descriptionpath)) {
375 if (empty($description[$method->getName()]) || !array_key_exists($descriptiontype, $description[$method->getName()])) {
376 $desctoadd = array($descriptiontype => $desctoadd);
40f024c9 377 }
3fba577b 378 $paramtoadd = $descriptiontype;
379 } else {
380 $paramtoadd = 'multiple:'.$param;
381 }
382
383 webservice_lib::add_end_of_description($paramtoadd, $desctoadd, $description[$method->getName()], $descriptionpath);
384
385 } else {
386 $desctoadd = webservice_lib::create_end_of_descriptionline($separator.$otherparam, $matches[2][$i]);
387 if(empty($descriptionpath)) {
388
389 if (empty($description[$method->getName()]) || !array_key_exists($descriptiontype, $description[$method->getName()])) {
390 $desctoadd = array($descriptiontype => $desctoadd);
391 }
392 $paramtoadd = $descriptiontype;
393
394 } else {
395 $paramtoadd = $param;
40f024c9 396 }
3fba577b 397 webservice_lib::add_end_of_description($paramtoadd, $desctoadd, $description[$method->getName()], $descriptionpath);
40f024c9 398 }
399 }
3fba577b 400
40f024c9 401 }
402 }
2f4dc99d 403// echo "<pre>";
404// var_dump($description);
405// echo "</pre>";
40f024c9 406 return $description;
3fba577b 407
40f024c9 408 }
409
410 /**
3fba577b 411 * Add a description part to the descripition array
40f024c9 412 * @param <type> $param
413 * @param <type> $desctoadd
414 * @param <type> $descriptionlevel
415 * @param <type> $descriptionpath
416 * @param <type> $level
417 */
418 public static function add_end_of_description($param, $desctoadd, &$descriptionlevel, $descriptionpath, $level= 0){
419 if (sizeof($descriptionpath)==0 || sizeof($descriptionpath)==$level+1) {
420
421 if (is_array($descriptionlevel) && !empty($descriptionlevel)) {
422 foreach($desctoadd as $key=>$value) {
423 if ($key!="params" && $key!="optional" && $key!="return") { //TODO
424 $descriptionlevel[$param][$key] = $value;
425 } else {
426 $descriptionlevel[$param] = $value;
427 }
428 }
429 } else {
430 $descriptionlevel = $desctoadd;
431 }
432 } else {
433 webservice_lib::add_end_of_description($param, $desctoadd, &$descriptionlevel[$descriptionpath[$level]], $descriptionpath, $level+1);
434 }
435
436 }
437
438
439 /**
3fba577b 440 * We create a description part for the description array
441 * Structure explained in the "generate_webservice_description" dockblock
40f024c9 442 * @param <type> $stringtoadd
443 * @param <type> $type
444 * @return <type>
445 */
3fba577b 446 public static function create_end_of_descriptionline($stringtoadd, $type) {
40f024c9 447
448 if (strrpos($stringtoadd, "->")===false || (strrpos($stringtoadd, ":")!==false && (strrpos($stringtoadd, ":") > strrpos($stringtoadd, "->")))) {
449 $separator = ":";
450 $separatorsize=1;
451 } else {
452 $separator = "->";
453 $separatorsize=2;
454 }
455
456 $param = substr($stringtoadd,strrpos($stringtoadd, $separator)+$separatorsize);
457 $result = array( $param => $type);
458
459 $otherparam = substr($stringtoadd,0,strlen($stringtoadd)-strlen($param)-$separatorsize);
460
461 while(strrpos($otherparam, ":")!==false || strrpos($otherparam, "->")!==false) {
462 if (strrpos($otherparam, "->")===false || (strrpos($otherparam, ":")!==false && (strrpos($otherparam, ":") > strrpos($otherparam, "->")))) {
463 $separator = ":";
464 $separatorsize=1;
465 } else {
466 $separator = "->";
467 $separatorsize=2;
468 }
469 $param = substr($otherparam,strrpos($otherparam, $separator)+$separatorsize);
470 $otherparam = substr($otherparam,0,strrpos($otherparam, $separator));
471
472 if ($separator==":") {
473 $result = array('multiple:'.$param => $result);
474 } else {
475 $result = array($param => $result);
476 }
477
478 }
479
480 return $result;
481
482 }
483
06e7fadc 484}
485
486/**
487 * Web Service server base class
488 */
489abstract class webservice_server {
490
491 /**
492 * Web Service Protocol name (eg. SOAP, REST, XML-RPC,...)
493 * @var String
494 */
495 private $protocolname;
496
06e7fadc 497 public function __construct() {
498 }
499
500 abstract public function run();
501
502 public function get_protocolname() {
503 return $this->protocolname;
504 }
505
506 public function set_protocolname($protocolname) {
507 $this->protocolname = $protocolname;
508 }
509
510 public function get_enable() {
24350e06 511 return get_config($this->get_protocolname(), "enable");
06e7fadc 512 }
513
514 public function set_enable($enable) {
24350e06 515 set_config("enable", $enable, $this->get_protocolname());
06e7fadc 516 }
517
518}
519
520/**
521 * Temporary authentication class to be removed/modified
522 */
523class ws_authentication {
524 /**
525 *
526 * @param array|struct $params
527 * @return integer
528 */
529 function tmp_get_token($params) {
530 if ($params['username'] == 'wsuser' && $params['password'] == 'wspassword') {
531 return '465465465468468464';
532 } else {
533 throw new moodle_exception('wrongusernamepassword');
534 }
535 }
536}
537
24350e06 538/**
539 * Form for web service user settings (administration)
540 */
541final class wsuser_form extends moodleform {
542 protected $username;
543
544 /**
545 * Definition of the moodleform
546 */
547 public function definition() {
548 global $DB;
549 $this->username = $this->_customdata['username'];
550 $mform =& $this->_form;
40f024c9 551
24350e06 552 $strrequired = get_string('required');
553
554 $mform->addElement('hidden', 'username', $this->username);
555 $param = new stdClass();
556 $param->username = $this->username;
557 $wsuser = $DB->get_record("user", array("username" => $this->username));
558
559 $mform->addElement('text', 'ipwhitelist', get_string('ipwhitelist', 'admin'), array('value'=>get_user_preferences("ipwhitelist", "", $wsuser->id),'size' => '40'));
560 $mform->addElement('static', null, '', get_string('ipwhitelistdesc','admin', $param));
561
562 $this->add_action_buttons(true, get_string('savechanges','admin'));
563 }
564}
565
06e7fadc 566?>