MDL-12886 improved external API
[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
886d7556 33/**
34 * Return list of all web service protocol into the webservice folder
35 * @global <type> $CFG
36 * @return <type>
37 */
06e7fadc 38 public static function get_list_protocols() {
39 global $CFG;
40 $protocols = array();
41 $directorypath = $CFG->dirroot . "/webservice";
42 if( $dh = opendir($directorypath)) {
886d7556 43 while( false !== ($file = readdir($dh))) {
06e7fadc 44 if( $file == '.' || $file == '..' || $file == 'CVS') { // Skip '.' and '..'
45 continue;
46 }
47 $path = $directorypath . '/' . $file;
40f024c9 48 ///browse the subfolder
06e7fadc 49 if( is_dir($path) ) {
fbe52a39 50 if ($file != 'db') { //we don't want to browse the 'db' subfolder of webservice folder
886d7556 51 require_once($path."/lib.php");
52 $classname = $file."_server";
53 $protocols[] = new $classname;
fbe52a39 54 }
06e7fadc 55 }
40f024c9 56 ///retrieve api.php file
886d7556 57 else {
06e7fadc 58 continue;
59 }
60 }
61 closedir($dh);
62 }
63 return $protocols;
64 }
65
66 /**
67 * Temporary Authentication method to be modified/removed
68 * @global <type> $DB
69 * @param <type> $token
70 * @return <type>
71 */
72 public static function mock_check_token($token) {
886d7556 73 //fake test
fa0797ec 74 if ($token == 456) {
886d7556 75 ///retrieve the user
06e7fadc 76 global $DB;
77 $user = $DB->get_record('user', array('username'=>'wsuser', 'mnethostid'=>1));
78
79 if (empty($user)) {
80 return false;
81 }
82
83 return $user;
84 } else {
85 return false;
86 }
87 }
88
24350e06 89 /**
90 * Retrieve all external.php from Moodle (except the one of the exception list)
91 * @param <type> $
92 * @param <type> $directorypath
93 * @return boolean true if n
94 */
886d7556 95 public static function setListApiFiles( &$files, $directorypath ) {
24350e06 96 global $CFG;
97
886d7556 98 if(is_dir($directorypath)) { //check that we are browsing a folder not a file
24350e06 99
886d7556 100 if( $dh = opendir($directorypath)) {
101 while( false !== ($file = readdir($dh))) {
24350e06 102
103 if( $file == '.' || $file == '..') { // Skip '.' and '..'
104 continue;
105 }
106 $path = $directorypath . '/' . $file;
40f024c9 107 ///browse the subfolder
24350e06 108 if( is_dir($path) ) {
40f024c9 109 webservice_lib::setListApiFiles($files, $path);
24350e06 110 }
40f024c9 111 ///retrieve api.php file
24350e06 112 else if ($file == "external.php") {
886d7556 113 $files[] = $path;
114 }
24350e06 115 }
116 closedir($dh);
117
118 }
119 }
120
121 }
122
886d7556 123 public static function services_discovery() {
124 global $CFG, $DB;
125 $externalfiles = array();
126 webservice_lib::setListApiFiles($externalfiles, $CFG->dirroot);
127
9baf6825 128 $wsnotification = array();
129
886d7556 130 //retrieve all saved services
131 $services = $DB->get_records('external_services', array('custom' => 0)); //we only retrieve not custom service
132 $dbservices = array();
133 foreach ($services as $service) {
134 $dbservices[$service->name] = false; //value false will define obsolote status
135 //once we parse all services from the external files
136 }
137
138 //retrieve all saved servicefunction association including their function name,
139 //service name, function id and service id
140
9baf6825 141 $servicesfunctions = $DB->get_records_sql("SELECT fs.id as id, fs.enabled as enabled, s.name as servicename, s.id as serviceid, f.name as functionname, f.id as functionid
886d7556 142 FROM {external_services} s, {external_functions} f, {external_services_functions} fs
143 WHERE fs.externalserviceid = s.id AND fs.externalfunctionid = f.id AND s.custom = 0");
144 $dbservicesfunctions = array();
145 foreach ($servicesfunctions as $servicefunction) {
146 $dbservicesfunctions[$servicefunction->servicename][$servicefunction->functionname] = array('serviceid' => $servicefunction->serviceid,
147 'functionid' => $servicefunction->functionid,
148 'id' => $servicefunction->id,
149 'notobsolete' => false);
150 //once we parse all services from the external files
151 }
152
153 foreach ($externalfiles as $file) {
154 require($file);
155 $classpath = substr($file,strlen($CFG->dirroot)+1); //remove the dir root + / from the file path
156 $classpath = substr($classpath,0,strlen($classpath) - 13); //remove /external.php from the classpath
157 $classpath = str_replace('/','_',$classpath); //convert all / into _
158 $classname = $classpath."_external";
159 $api = new $classname();
160 if (method_exists($api, 'get_descriptions')) {
161 $descriptions = $api->get_descriptions();
162
163 //retrieve all saved function into the DB for this specific external file/component
164 $functions = $DB->get_records('external_functions', array('component' => $classpath));
165 //remove the obsolete ones
166 $dbfunctions = array();
167 foreach ($functions as $function) {
168 $dbfunctions[$function->name] = false; //value false is not important we just need the key
169 if (!array_key_exists($function->name, $descriptions)) {
170 //remove all obsolete function from the db
171 $DB->delete_records('external_functions', array('name' => $function->name, 'component' => $classpath));
172 }
173 }
174
175 foreach ($descriptions as $functionname => $functiondescription) {
886d7556 176 if (array_key_exists('service', $functiondescription) && !empty($functiondescription['service'])) { //check that the service has been set in the description
9baf6825 177 //only create the one not already saved into the database
178 if (!array_key_exists($functionname, $dbfunctions)) {
179 $newfunction = new object();
180 $newfunction->component = $classpath;
181 $newfunction->name = $functionname;
182 $DB->insert_record('external_functions', $newfunction);
183 $notifparams = new object();
184 $notifparams->functionname = $functionname;
185 $notifparams->servicename = $functiondescription['service'];
186 $wsnotification[] = get_string('wsinsertfunction','webservice', $notifparams);
187 }
188
189 //check if the service is into the database
886d7556 190 if (!array_key_exists($functiondescription['service'], $dbservices)) {
191 $newservice = new object();
192 $newservice->name = $functiondescription['service'];
9baf6825 193 $newservice->enabled = 0;
886d7556 194 $newservice->custom = 0;
195 $DB->insert_record('external_services', $newservice);
196 }
197 $dbservices[$functiondescription['service']] = true; //mark the service as not obsolete
198 //and add it if it wasn't in the list
199 }
200 else {
201 $errors = new object();
202 $errors->classname = $classname;
203 $errors->functionname = $functionname;
204 throw new moodle_exception("wsdescriptionserviceisempty",'','', $errors);
886d7556 205 }
206
886d7556 207 //check if the couple service/function is into the database
208 if (!array_key_exists($functiondescription['service'], $dbservicesfunctions) || !array_key_exists($functionname, $dbservicesfunctions[$functiondescription['service']])) {
209 $newassociation = new object();
210 $newassociation->externalserviceid = $DB->get_field('external_services','id',array('name' => $functiondescription['service']));
211 $newassociation->externalfunctionid = $DB->get_field('external_functions','id',array('name' => $functionname, 'component' => $classpath));
9baf6825 212 $newassociation->enabled = 0;
886d7556 213 $DB->insert_record('external_services_functions', $newassociation);
214 }
215 $dbservicesfunctions[$functiondescription['service']][$functionname]['notobsolete'] = true;
216 }
886d7556 217 }
218 else {
219 throw new moodle_exception("wsdoesntextendbaseclass",'','', $classname);
220 }
221 }
222
223 //remove all obsolete service (not the custom ones)
224 foreach ($dbservices as $servicename => $notobsolete) {
225 if (!$notobsolete) {
226 $DB->delete_records('external_services', array('name' => $servicename));
227 }
228 }
229
230 //remove all obsolete association (not the custom ones)
231 foreach ($dbservicesfunctions as $servicename => $servicefunctions ) {
232 foreach ($servicefunctions as $functioname => $servicefunction) {
233 if (!$servicefunction['notobsolete']) {
234 $DB->delete_records('external_services_functions', array('id' => $servicefunction['id']));
9baf6825 235 $notifparams = new object();
236 $notifparams->functionname = $functionname;
237 $notifparams->servicename = $servicename;
238 $wsnotification[] = get_string('wsdeletefunction','webservice', $notifparams);
886d7556 239 }
240 }
241 }
9baf6825 242
243 return $wsnotification;
886d7556 244 }
245
fa0797ec 246 /**
247 * Check if the Moodle site has the web service protocol enable
248 * @global object $CFG
249 * @param string $protocol
250 */
886d7556 251 function display_webservices_availability($protocol) {
fa0797ec 252 global $CFG;
253
254 $available = true;
255
256 echo get_string('webservicesenable','webservice').": ";
257 if (empty($CFG->enablewebservices)) {
258 echo "<strong style=\"color:red\">".get_string('fail','webservice')."</strong>";
259 $available = false;
260 } else {
261 echo "<strong style=\"color:green\">".get_string('ok','webservice')."</strong>";
262 }
263 echo "<br/>";
264
265 foreach(webservice_lib::get_list_protocols() as $wsprotocol) {
f7631e73 266 if (strtolower($wsprotocol->get_protocolid()) == strtolower($protocol)) {
267 echo get_string('protocolenable','webservice',array($wsprotocol->get_protocolid())).": ";
268 if ( get_config($wsprotocol-> get_protocolid(), "enable")) {
fa0797ec 269 echo "<strong style=\"color:green\">".get_string('ok','webservice')."</strong>";
270 } else {
271 echo "<strong style=\"color:red\">".get_string('fail','webservice')."</strong>";
272 $available = false;
273 }
274 echo "<br/>";
275 continue;
276 }
277 }
278
279 //check debugging
280 if ($CFG->debugdisplay) {
281 echo "<strong style=\"color:red\">".get_string('debugdisplayon','webservice')."</strong>";
282 $available = false;
283 }
284
285 return $available;
286 }
287
06e7fadc 288}
289
290/**
291 * Web Service server base class
292 */
293abstract class webservice_server {
294
886d7556 295/**
296 * Web Service Protocol name (eg. SOAP, REST, XML-RPC,...)
297 * @var String
298 */
06e7fadc 299 private $protocolname;
300
f7631e73 301 /**
302 * Web Service Protocol id (eg. soap, rest, xmlrpc...)
303 * @var String
304 */
305 private $protocolid;
306
06e7fadc 307 public function __construct() {
308 }
309
310 abstract public function run();
311
312 public function get_protocolname() {
313 return $this->protocolname;
314 }
315
f7631e73 316 public function get_protocolid() {
317 return $this->protocolid;
318 }
319
06e7fadc 320 public function set_protocolname($protocolname) {
321 $this->protocolname = $protocolname;
322 }
323
f7631e73 324 public function set_protocolid($protocolid) {
325 $this->protocolid = $protocolid;
326 }
327
06e7fadc 328 public function get_enable() {
f7631e73 329 return get_config($this->get_protocolid(), "enable");
06e7fadc 330 }
331
332 public function set_enable($enable) {
f7631e73 333 set_config("enable", $enable, $this->get_protocolid());
334 }
335
336 /**
337 * Names of the server settings
338 * @return array
339 */
340 public static function get_setting_names() {
341 return array();
342 }
343
344 public function settings_form(&$mform) {
06e7fadc 345 }
346
347}
348
349/**
350 * Temporary authentication class to be removed/modified
351 */
352class ws_authentication {
886d7556 353/**
354 *
355 * @param object|struct $params
356 * @return integer
357 */
ebcc6bd3 358 function get_token($params) {
359 $params->username = clean_param($params->username, PARAM_ALPHANUM);
360 $params->password = clean_param($params->password, PARAM_ALPHANUM);
361 if ($params->username == 'wsuser' && $params->password == 'wspassword') {
fa0797ec 362 return '456';
06e7fadc 363 } else {
364 throw new moodle_exception('wrongusernamepassword');
886d7556 365 }
06e7fadc 366 }
367}
368
24350e06 369/**
370 * Form for web service user settings (administration)
371 */
372final class wsuser_form extends moodleform {
373 protected $username;
374
375 /**
376 * Definition of the moodleform
377 */
378 public function definition() {
379 global $DB;
380 $this->username = $this->_customdata['username'];
381 $mform =& $this->_form;
40f024c9 382
24350e06 383 $mform->addElement('hidden', 'username', $this->username);
384 $param = new stdClass();
385 $param->username = $this->username;
386 $wsuser = $DB->get_record("user", array("username" => $this->username));
387
388 $mform->addElement('text', 'ipwhitelist', get_string('ipwhitelist', 'admin'), array('value'=>get_user_preferences("ipwhitelist", "", $wsuser->id),'size' => '40'));
389 $mform->addElement('static', null, '', get_string('ipwhitelistdesc','admin', $param));
390
391 $this->add_action_buttons(true, get_string('savechanges','admin'));
392 }
393}
394
f7631e73 395/**
396 * Form for web service server settings (administration)
397 */
398final class wssettings_form extends moodleform {
399 protected $settings;
400
401 /**
402 * Definition of the moodleform
403 */
404 public function definition() {
405 global $DB,$CFG;
406 $settings = $this->_customdata['settings'];
407 $mform =& $this->_form;
408
409 $mform->addElement('hidden', 'settings', $settings);
410 $param = new stdClass();
411
412 require_once($CFG->dirroot . '/webservice/'. $settings . '/lib.php');
413 $servername = $settings.'_server';
414 $server = new $servername();
415 $server->settings_form($mform);
416
417 // set the data if we have some.
418 $data = array();
419 $option_names = $server->get_setting_names();
420 foreach ($option_names as $config) {
421 $data[$config] = get_config($settings, $config);
422 }
423 $this->set_data($data);
424
425
426 $this->add_action_buttons(true, get_string('savechanges','admin'));
427 }
428}
429
9baf6825 430/**
431 * Form for web service server settings (administration)
432 */
433final class wsservicesettings_form extends moodleform {
434 protected $settings;
435
436 /**
437 * Definition of the moodleform
438 */
439 public function definition() {
440 global $DB,$CFG;
441 $serviceid = $this->_customdata['serviceid'];
442 $mform =& $this->_form;
443
444 $mform->addElement('hidden', 'serviceid', $serviceid);
445 $param = new stdClass();
446
447 // require_once($CFG->dirroot . '/webservice/'. $settings . '/lib.php');
448 // $servername = $settings.'_server';
449 // $server = new $servername();
450 // $server->settings_form($mform);
451
452 // set the data if we have some.
453 // $data = array();
454 // $option_names = $server->get_setting_names();
455 // foreach ($option_names as $config) {
456 // $data[$config] = get_config($settings, $config);
457 // }
458 // $this->set_data($data);
459 $service = $DB->get_record('external_services',array('id' => $serviceid));
460
461 $mform->addElement('text', 'servicename', get_string('servicename', 'webservice'));
462 $mform->setDefault('servicename',get_string($service->name, 'webservice'));
463 if (!empty($serviceid)) {
464 $mform->disabledIf('servicename', 'serviceid', 'eq', $serviceid);
465 }
466
467 if (empty($serviceid)) {
468 //display list of functions to select
469 }
470
471 //display list of functions associated to the service
472
473
474
475 $this->add_action_buttons(true, get_string('savechanges','admin'));
476 }
477}
478
479
06e7fadc 480?>