Differences Between: [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 18 /** 19 * REST web service implementation classes and methods. 20 * 21 * @package webservice_rest 22 * @copyright 2009 Jerome Mouneyrac 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 require_once("$CFG->dirroot/webservice/lib.php"); 27 28 /** 29 * REST service server implementation. 30 * 31 * @package webservice_rest 32 * @copyright 2009 Petr Skoda (skodak) 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class webservice_rest_server extends webservice_base_server { 36 37 /** @var string return method ('xml' or 'json') */ 38 protected $restformat; 39 40 /** 41 * Contructor 42 * 43 * @param string $authmethod authentication method of the web service (WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN, ...) 44 * @param string $restformat Format of the return values: 'xml' or 'json' 45 */ 46 public function __construct($authmethod) { 47 parent::__construct($authmethod); 48 $this->wsname = 'rest'; 49 } 50 51 /** 52 * Set the request format to. 53 */ 54 public function set_rest_format(): void { 55 // Get GET and POST parameters. 56 $methodvariables = array_merge($_GET, $_POST); 57 58 // Retrieve REST format parameter - 'xml' (default) or 'json'. 59 $restformatisset = isset($methodvariables['moodlewsrestformat']) 60 && (($methodvariables['moodlewsrestformat'] == 'xml' || $methodvariables['moodlewsrestformat'] == 'json')); 61 $this->restformat = $restformatisset ? $methodvariables['moodlewsrestformat'] : 'xml'; 62 } 63 64 /** 65 * This method parses the $_POST and $_GET superglobals and looks for 66 * the following information: 67 * 1/ user authentication - username+password or token (wsusername, wspassword and wstoken parameters) 68 * 2/ function name (wsfunction parameter) 69 * 3/ function parameters (all other parameters except those above) 70 * 4/ text format parameters 71 * 5/ return rest format xml/json 72 */ 73 protected function parse_request() { 74 75 // Retrieve and clean the POST/GET parameters from the parameters specific to the server. 76 parent::set_web_service_call_settings(); 77 78 // Get GET and POST parameters. 79 $methodvariables = array_merge($_GET, $_POST); 80 $this->set_rest_format(); 81 unset($methodvariables['moodlewsrestformat']); 82 83 if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME) { 84 $this->username = isset($methodvariables['wsusername']) ? $methodvariables['wsusername'] : null; 85 unset($methodvariables['wsusername']); 86 87 $this->password = isset($methodvariables['wspassword']) ? $methodvariables['wspassword'] : null; 88 unset($methodvariables['wspassword']); 89 90 $this->functionname = isset($methodvariables['wsfunction']) ? $methodvariables['wsfunction'] : null; 91 unset($methodvariables['wsfunction']); 92 93 $this->parameters = $methodvariables; 94 95 } else { 96 $this->token = isset($methodvariables['wstoken']) ? $methodvariables['wstoken'] : null; 97 unset($methodvariables['wstoken']); 98 99 $this->functionname = isset($methodvariables['wsfunction']) ? $methodvariables['wsfunction'] : null; 100 unset($methodvariables['wsfunction']); 101 102 $this->parameters = $methodvariables; 103 } 104 } 105 106 /** 107 * Send the result of function call to the WS client 108 * formatted as XML document. 109 */ 110 protected function send_response() { 111 112 //Check that the returned values are valid 113 try { 114 if ($this->function->returns_desc != null) { 115 $validatedvalues = external_api::clean_returnvalue($this->function->returns_desc, $this->returns); 116 } else { 117 $validatedvalues = null; 118 } 119 } catch (Exception $ex) { 120 $exception = $ex; 121 } 122 123 if (!empty($exception)) { 124 $response = $this->generate_error($exception); 125 } else { 126 //We can now convert the response to the requested REST format 127 if ($this->restformat == 'json') { 128 $response = json_encode($validatedvalues); 129 } else { 130 $response = '<?xml version="1.0" encoding="UTF-8" ?>'."\n"; 131 $response .= '<RESPONSE>'."\n"; 132 $response .= self::xmlize_result($validatedvalues, $this->function->returns_desc); 133 $response .= '</RESPONSE>'."\n"; 134 } 135 } 136 137 $this->send_headers(); 138 echo $response; 139 } 140 141 /** 142 * Send the error information to the WS client 143 * formatted as XML document. 144 * Note: the exception is never passed as null, 145 * it only matches the abstract function declaration. 146 * @param exception $ex the exception that we are sending 147 */ 148 protected function send_error($ex=null) { 149 $this->send_headers(); 150 echo $this->generate_error($ex); 151 } 152 153 /** 154 * Build the error information matching the REST returned value format (JSON or XML) 155 * @param exception $ex the exception we are converting in the server rest format 156 * @return string the error in the requested REST format 157 */ 158 protected function generate_error($ex) { 159 if ($this->restformat == 'json') { 160 $errorobject = new stdClass; 161 $errorobject->exception = get_class($ex); 162 if (isset($ex->errorcode)) { 163 $errorobject->errorcode = $ex->errorcode; 164 } 165 $errorobject->message = $ex->getMessage(); 166 if (debugging() and isset($ex->debuginfo)) { 167 $errorobject->debuginfo = $ex->debuginfo; 168 } 169 $error = json_encode($errorobject); 170 } else { 171 $error = '<?xml version="1.0" encoding="UTF-8" ?>'."\n"; 172 $error .= '<EXCEPTION class="'.get_class($ex).'">'."\n"; 173 if (isset($ex->errorcode)) { 174 $error .= '<ERRORCODE>' . htmlspecialchars($ex->errorcode, ENT_COMPAT, 'UTF-8') 175 . '</ERRORCODE>' . "\n"; 176 } 177 $error .= '<MESSAGE>'.htmlspecialchars($ex->getMessage(), ENT_COMPAT, 'UTF-8').'</MESSAGE>'."\n"; 178 if (debugging() and isset($ex->debuginfo)) { 179 $error .= '<DEBUGINFO>'.htmlspecialchars($ex->debuginfo, ENT_COMPAT, 'UTF-8').'</DEBUGINFO>'."\n"; 180 } 181 $error .= '</EXCEPTION>'."\n"; 182 } 183 return $error; 184 } 185 186 /** 187 * Internal implementation - sending of page headers. 188 */ 189 protected function send_headers() { 190 if ($this->restformat == 'json') { 191 header('Content-type: application/json'); 192 } else { 193 header('Content-Type: application/xml; charset=utf-8'); 194 header('Content-Disposition: inline; filename="response.xml"'); 195 } 196 header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0'); 197 header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT'); 198 header('Pragma: no-cache'); 199 header('Accept-Ranges: none'); 200 // Allow cross-origin requests only for Web Services. 201 // This allow to receive requests done by Web Workers or webapps in different domains. 202 header('Access-Control-Allow-Origin: *'); 203 } 204 205 /** 206 * Internal implementation - recursive function producing XML markup. 207 * 208 * @param mixed $returns the returned values 209 * @param external_description $desc 210 * @return string 211 */ 212 protected static function xmlize_result($returns, $desc) { 213 if ($desc === null) { 214 return ''; 215 216 } else if ($desc instanceof external_value) { 217 if (is_bool($returns)) { 218 // we want 1/0 instead of true/false here 219 $returns = (int)$returns; 220 } 221 if (is_null($returns)) { 222 return '<VALUE null="null"/>'."\n"; 223 } else { 224 return '<VALUE>'.htmlspecialchars($returns, ENT_COMPAT, 'UTF-8').'</VALUE>'."\n"; 225 } 226 227 } else if ($desc instanceof external_multiple_structure) { 228 $mult = '<MULTIPLE>'."\n"; 229 if (!empty($returns)) { 230 foreach ($returns as $val) { 231 $mult .= self::xmlize_result($val, $desc->content); 232 } 233 } 234 $mult .= '</MULTIPLE>'."\n"; 235 return $mult; 236 237 } else if ($desc instanceof external_single_structure) { 238 $single = '<SINGLE>'."\n"; 239 foreach ($desc->keys as $key=>$subdesc) { 240 $value = isset($returns[$key]) ? $returns[$key] : null; 241 $single .= '<KEY name="'.$key.'">'.self::xmlize_result($value, $subdesc).'</KEY>'."\n"; 242 } 243 $single .= '</SINGLE>'."\n"; 244 return $single; 245 } 246 } 247 } 248 249 250 /** 251 * REST test client class 252 * 253 * @package webservice_rest 254 * @copyright 2009 Petr Skoda (skodak) 255 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 256 */ 257 class webservice_rest_test_client implements webservice_test_client_interface { 258 /** 259 * Execute test client WS request 260 * @param string $serverurl server url (including token parameter or username/password parameters) 261 * @param string $function function name 262 * @param array $params parameters of the called function 263 * @return mixed 264 */ 265 public function simpletest($serverurl, $function, $params) { 266 return download_file_content($serverurl.'&wsfunction='.$function, null, $params); 267 } 268 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body