Commit | Line | Data |
---|---|---|
e6bdd128 | 1 | <?php |
cc93c7da | 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 | ||
a0a07014 | 17 | |
e6bdd128 | 18 | /** |
cc93c7da | 19 | * REST web service implementation classes and methods. |
06e7fadc | 20 | * |
a0a07014 JM |
21 | * @package webservice_rest |
22 | * @copyright 2009 Jerome Mouneyrac | |
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
06e7fadc | 24 | */ |
25 | ||
cc93c7da | 26 | require_once("$CFG->dirroot/webservice/lib.php"); |
06e7fadc | 27 | |
28 | /** | |
cc93c7da | 29 | * REST service server implementation. |
a0a07014 JM |
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 | |
e6bdd128 | 34 | */ |
cc93c7da | 35 | class webservice_rest_server extends webservice_base_server { |
a08b4cc8 | 36 | |
a0a07014 | 37 | /** @var string return method ('xml' or 'json') */ |
a08b4cc8 JM |
38 | protected $restformat; |
39 | ||
cc93c7da | 40 | /** |
41 | * Contructor | |
a0a07014 JM |
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' | |
cc93c7da | 45 | */ |
93ce0e82 | 46 | public function __construct($authmethod) { |
2d0acbd5 | 47 | parent::__construct($authmethod); |
cc93c7da | 48 | $this->wsname = 'rest'; |
89b8ce51 | 49 | } |
50 | ||
cc93c7da | 51 | /** |
dcd902a0 | 52 | * This method parses the $_POST and $_GET superglobals and looks for |
cc93c7da | 53 | * the following information: |
54 | * 1/ user authentication - username+password or token (wsusername, wspassword and wstoken parameters) | |
55 | * 2/ function name (wsfunction parameter) | |
56 | * 3/ function parameters (all other parameters except those above) | |
93ce0e82 JM |
57 | * 4/ text format parameters |
58 | * 5/ return rest format xml/json | |
cc93c7da | 59 | */ |
60 | protected function parse_request() { | |
dcd902a0 | 61 | |
93ce0e82 JM |
62 | // Retrieve and clean the POST/GET parameters from the parameters specific to the server. |
63 | parent::set_web_service_call_settings(); | |
64 | ||
65 | // Get GET and POST parameters. | |
66 | $methodvariables = array_merge($_GET, $_POST); | |
67 | ||
68 | // Retrieve REST format parameter - 'xml' (default) or 'json'. | |
69 | $restformatisset = isset($methodvariables['moodlewsrestformat']) | |
70 | && (($methodvariables['moodlewsrestformat'] == 'xml' || $methodvariables['moodlewsrestformat'] == 'json')); | |
71 | $this->restformat = $restformatisset ? $methodvariables['moodlewsrestformat'] : 'xml'; | |
72 | unset($methodvariables['moodlewsrestformat']); | |
dcd902a0 | 73 | |
ad8b5ba2 | 74 | if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME) { |
dcd902a0 JM |
75 | $this->username = isset($methodvariables['wsusername']) ? $methodvariables['wsusername'] : null; |
76 | unset($methodvariables['wsusername']); | |
13036898 | 77 | |
dcd902a0 JM |
78 | $this->password = isset($methodvariables['wspassword']) ? $methodvariables['wspassword'] : null; |
79 | unset($methodvariables['wspassword']); | |
7c9152bc | 80 | |
dcd902a0 JM |
81 | $this->functionname = isset($methodvariables['wsfunction']) ? $methodvariables['wsfunction'] : null; |
82 | unset($methodvariables['wsfunction']); | |
e6bdd128 | 83 | |
dcd902a0 | 84 | $this->parameters = $methodvariables; |
e6bdd128 | 85 | |
cc93c7da | 86 | } else { |
dcd902a0 JM |
87 | $this->token = isset($methodvariables['wstoken']) ? $methodvariables['wstoken'] : null; |
88 | unset($methodvariables['wstoken']); | |
01482a4a | 89 | |
dcd902a0 JM |
90 | $this->functionname = isset($methodvariables['wsfunction']) ? $methodvariables['wsfunction'] : null; |
91 | unset($methodvariables['wsfunction']); | |
8a7703ce | 92 | |
dcd902a0 | 93 | $this->parameters = $methodvariables; |
338bf5a7 | 94 | } |
89b8ce51 | 95 | } |
e6bdd128 | 96 | |
cc93c7da | 97 | /** |
98 | * Send the result of function call to the WS client | |
99 | * formatted as XML document. | |
40f024c9 | 100 | */ |
cc93c7da | 101 | protected function send_response() { |
6d4aa258 JM |
102 | |
103 | //Check that the returned values are valid | |
104 | try { | |
2ada59b7 JM |
105 | if ($this->function->returns_desc != null) { |
106 | $validatedvalues = external_api::clean_returnvalue($this->function->returns_desc, $this->returns); | |
107 | } else { | |
108 | $validatedvalues = null; | |
109 | } | |
6d4aa258 JM |
110 | } catch (Exception $ex) { |
111 | $exception = $ex; | |
112 | } | |
113 | ||
114 | if (!empty($exception)) { | |
115 | $response = $this->generate_error($exception); | |
a08b4cc8 | 116 | } else { |
6d4aa258 JM |
117 | //We can now convert the response to the requested REST format |
118 | if ($this->restformat == 'json') { | |
119 | $response = json_encode($validatedvalues); | |
120 | } else { | |
121 | $response = '<?xml version="1.0" encoding="UTF-8" ?>'."\n"; | |
122 | $response .= '<RESPONSE>'."\n"; | |
6a30d03e | 123 | $response .= self::xmlize_result($validatedvalues, $this->function->returns_desc); |
6d4aa258 JM |
124 | $response .= '</RESPONSE>'."\n"; |
125 | } | |
a08b4cc8 | 126 | } |
6d4aa258 JM |
127 | |
128 | $this->send_headers(); | |
129 | echo $response; | |
40f024c9 | 130 | } |
131 | ||
cc93c7da | 132 | /** |
133 | * Send the error information to the WS client | |
134 | * formatted as XML document. | |
6d4aa258 JM |
135 | * Note: the exception is never passed as null, |
136 | * it only matches the abstract function declaration. | |
a0a07014 | 137 | * @param exception $ex the exception that we are sending |
cc93c7da | 138 | */ |
139 | protected function send_error($ex=null) { | |
140 | $this->send_headers(); | |
6d4aa258 JM |
141 | echo $this->generate_error($ex); |
142 | } | |
143 | ||
144 | /** | |
145 | * Build the error information matching the REST returned value format (JSON or XML) | |
a0a07014 | 146 | * @param exception $ex the exception we are converting in the server rest format |
6d4aa258 JM |
147 | * @return string the error in the requested REST format |
148 | */ | |
149 | protected function generate_error($ex) { | |
a08b4cc8 | 150 | if ($this->restformat == 'json') { |
6d4aa258 JM |
151 | $errorobject = new stdClass; |
152 | $errorobject->exception = get_class($ex); | |
506df9ac | 153 | $errorobject->errorcode = $ex->errorcode; |
6d4aa258 | 154 | $errorobject->message = $ex->getMessage(); |
a08b4cc8 | 155 | if (debugging() and isset($ex->debuginfo)) { |
6d4aa258 | 156 | $errorobject->debuginfo = $ex->debuginfo; |
a08b4cc8 | 157 | } |
6d4aa258 | 158 | $error = json_encode($errorobject); |
a08b4cc8 | 159 | } else { |
6d4aa258 JM |
160 | $error = '<?xml version="1.0" encoding="UTF-8" ?>'."\n"; |
161 | $error .= '<EXCEPTION class="'.get_class($ex).'">'."\n"; | |
506df9ac JM |
162 | $error .= '<ERRORCODE>' . htmlspecialchars($ex->errorcode, ENT_COMPAT, 'UTF-8') |
163 | . '</ERRORCODE>' . "\n"; | |
d2f127e6 | 164 | $error .= '<MESSAGE>'.htmlspecialchars($ex->getMessage(), ENT_COMPAT, 'UTF-8').'</MESSAGE>'."\n"; |
a08b4cc8 | 165 | if (debugging() and isset($ex->debuginfo)) { |
d2f127e6 | 166 | $error .= '<DEBUGINFO>'.htmlspecialchars($ex->debuginfo, ENT_COMPAT, 'UTF-8').'</DEBUGINFO>'."\n"; |
a08b4cc8 | 167 | } |
6d4aa258 | 168 | $error .= '</EXCEPTION>'."\n"; |
b721783a | 169 | } |
6d4aa258 | 170 | return $error; |
b721783a | 171 | } |
e6bdd128 | 172 | |
cc93c7da | 173 | /** |
174 | * Internal implementation - sending of page headers. | |
cc93c7da | 175 | */ |
176 | protected function send_headers() { | |
a08b4cc8 JM |
177 | if ($this->restformat == 'json') { |
178 | header('Content-type: application/json'); | |
179 | } else { | |
180 | header('Content-Type: application/xml; charset=utf-8'); | |
181 | header('Content-Disposition: inline; filename="response.xml"'); | |
182 | } | |
cc93c7da | 183 | header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0'); |
184 | header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT'); | |
185 | header('Pragma: no-cache'); | |
186 | header('Accept-Ranges: none'); | |
3ca3d259 JL |
187 | // Allow cross-origin requests only for Web Services. |
188 | // This allow to receive requests done by Web Workers or webapps in different domains. | |
5e28991e | 189 | header('Access-Control-Allow-Origin: *'); |
13036898 | 190 | } |
191 | ||
cc93c7da | 192 | /** |
193 | * Internal implementation - recursive function producing XML markup. | |
a0a07014 JM |
194 | * |
195 | * @param mixed $returns the returned values | |
196 | * @param external_description $desc | |
197 | * @return string | |
cc93c7da | 198 | */ |
199 | protected static function xmlize_result($returns, $desc) { | |
200 | if ($desc === null) { | |
201 | return ''; | |
202 | ||
203 | } else if ($desc instanceof external_value) { | |
4f0c6ad1 PS |
204 | if (is_bool($returns)) { |
205 | // we want 1/0 instead of true/false here | |
206 | $returns = (int)$returns; | |
207 | } | |
f0dafb3c | 208 | if (is_null($returns)) { |
209 | return '<VALUE null="null"/>'."\n"; | |
210 | } else { | |
d2f127e6 | 211 | return '<VALUE>'.htmlspecialchars($returns, ENT_COMPAT, 'UTF-8').'</VALUE>'."\n"; |
f0dafb3c | 212 | } |
cc93c7da | 213 | |
214 | } else if ($desc instanceof external_multiple_structure) { | |
215 | $mult = '<MULTIPLE>'."\n"; | |
17ecdd29 | 216 | if (!empty($returns)) { |
217 | foreach ($returns as $val) { | |
218 | $mult .= self::xmlize_result($val, $desc->content); | |
8a7703ce | 219 | } |
cc93c7da | 220 | } |
221 | $mult .= '</MULTIPLE>'."\n"; | |
222 | return $mult; | |
223 | ||
224 | } else if ($desc instanceof external_single_structure) { | |
225 | $single = '<SINGLE>'."\n"; | |
226 | foreach ($desc->keys as $key=>$subdesc) { | |
4700c44c FM |
227 | $value = isset($returns[$key]) ? $returns[$key] : null; |
228 | $single .= '<KEY name="'.$key.'">'.self::xmlize_result($value, $subdesc).'</KEY>'."\n"; | |
cc93c7da | 229 | } |
230 | $single .= '</SINGLE>'."\n"; | |
231 | return $single; | |
13036898 | 232 | } |
13036898 | 233 | } |
e6bdd128 | 234 | } |
235 | ||
f0dafb3c | 236 | |
237 | /** | |
238 | * REST test client class | |
a0a07014 JM |
239 | * |
240 | * @package webservice_rest | |
241 | * @copyright 2009 Petr Skoda (skodak) | |
242 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
f0dafb3c | 243 | */ |
244 | class webservice_rest_test_client implements webservice_test_client_interface { | |
245 | /** | |
246 | * Execute test client WS request | |
a0a07014 JM |
247 | * @param string $serverurl server url (including token parameter or username/password parameters) |
248 | * @param string $function function name | |
249 | * @param array $params parameters of the called function | |
f0dafb3c | 250 | * @return mixed |
251 | */ | |
252 | public function simpletest($serverurl, $function, $params) { | |
253 | return download_file_content($serverurl.'&wsfunction='.$function, null, $params); | |
254 | } | |
6a30d03e | 255 | } |