webservice MDL-17135 add disable javascript to the valid until fields
[moodle.git] / lib / externallib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Support for external API
20  *
21  * @package    moodlecore
22  * @subpackage webservice
23  * @copyright  2009 Moodle Pty Ltd (http://moodle.com)
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 /**
28  * Exception indicating user is not allowed to use external function in
29  * the current context.
30  */
31 class restricted_context_exception extends moodle_exception {
32     /**
33      * Constructor
34      */
35     function __construct() {
36         parent::__construct('restrictedcontextexception', 'error');
37     }
38 }
40 /**
41  * Base class for external api methods.
42  */
43 class external_api {
44     private static $contextrestriction;
46     /**
47      * Set context restriction for all folowing subsequent function calls.
48      * @param stdClass $contex
49      * @return void
50      */
51     public static function set_context_restriction($context) {
52         self::$contextrestriction = $context;
53     }
55     /**
56      * This method has to be called before every operation
57      * that takes a longer time to finish!
58      *
59      * @param int $seconds max expected time the next operation needs
60      * @return void
61      */
62     public static function set_timeout($seconds=360) {
63         $seconds = ($seconds < 300) ? 300 : $seconds;
64         set_time_limit($seconds);
65     }
67     /**
68      * Validates submitted function parameters, if anything is incorrect
69      * invalid_parameter_exception is thrown.
70      * This is a simple recursive method which is intended to be called from
71      * each implementation method of external API.
72      * @param external_description $description description of parameters
73      * @param mixed $params the actual parameters
74      * @return mixed params with added defaults for optional items, invalid_parameters_exception thrown if any problem found
75      */
76     public static function validate_parameters(external_description $description, $params) {
77         if ($description instanceof external_value) {
78             if (is_array($params) or is_object($params)) {
79                 throw new invalid_parameter_exception('Scalar type expected, array or object received.');
80             }
81             return validate_param($params, $description->type, $description->allownull, 'Invalid external api parameter');
83         } else if ($description instanceof external_single_structure) {
84             if (!is_array($params)) {
85                 throw new invalid_parameter_exception('Only arrays accepted.');
86             }
87             $result = array();
88             foreach ($description->keys as $key=>$subdesc) {
89                 if (!array_key_exists($key, $params)) {
90                     if ($subdesc->required) {
91                         throw new invalid_parameter_exception('Missing required key in single structure.');
92                     }
93                     if ($subdesc instanceof external_value) {
94                         $result[$key] = self::validate_parameters($subdesc, $subdesc->default);
95                     }
96                 } else {
97                     $result[$key] = self::validate_parameters($subdesc, $params[$key]);
98                 }
99                 unset($params[$key]);
100             }
101             if (!empty($params)) {
102                 throw new invalid_parameter_exception('Unexpected keys detected in parameter array.');
103             }
104             return $result;
106         } else if ($description instanceof external_multiple_structure) {
107             if (!is_array($params)) {
108                 throw new invalid_parameter_exception('Only arrays accepted.');
109             }
110             $result = array();
111             foreach ($params as $param) {
112                 $result[] = self::validate_parameters($description->content, $param);
113             }
114             return $result;
116         } else {
117             throw new invalid_parameter_exception('Invalid external api description.');
118         }
119     }
121     /**
122      * Makes sure user may execute functions in this context.
123      * @param object $context
124      * @return void
125      */
126     protected static function validate_context($context) {
127         if (empty($context)) {
128             throw new invalid_parameter_exception('Context does not exist');
129         }
130         if (empty(self::$contextrestriction)) {
131             self::$contextrestriction = get_context_instance(CONTEXT_SYSTEM);
132         }
133         $rcontext = self::$contextrestriction;
135         if ($rcontext->contextlevel == $context->contextlevel) {
136             if ($rcontex->id != $context->id) {
137                 throw new restricted_context_exception();
138             }
139         } else if ($rcontext->contextlevel > $context->contextlevel) {
140             throw new restricted_context_exception();
141         } else {
142             $parents = get_parent_contexts($context);
143             if (!in_array($rcontext->id, $parents)) {
144                 throw new restricted_context_exception();
145             }
146         }
148         if ($context->contextlevel >= CONTEXT_COURSE) {
149             //TODO: temporary bloody hack, this needs to be replaced by
150             //      proper enrolment and course visibility check
151             //      similar to require_login() (which can not be used
152             //      because it can be used only once and redirects)
153             //      oh - did I say we need to rewrite enrolments in 2.0
154             //      to solve this bloody mess?
155             //
156             //      missing: hidden courses and categories, groupmembersonly,
157             //      conditional activities, etc.
158             require_capability('moodle/course:view', $context);
159         }
160     }
163 /**
164  * Common ancestor of all parameter description classes
165  */
166 abstract class external_description {
167     /** @property string $description description of element */
168     public $desc;
169     /** @property bool $required element value required, null not alowed */
170     public $required;
172     /**
173      * Contructor
174      * @param string $desc
175      * @param bool $required
176      */
177     public function __construct($desc, $required) {
178         $this->desc = $desc;
179         $this->required = $required;
180     }
183 /**
184  * Scalar alue description class
185  */
186 class external_value extends external_description {
187     /** @property mixed $type value type PARAM_XX */
188     public $type;
189     /** @property mixed $default default value */
190     public $default;
191     /** @property bool $allownull allow null values */
192     public $allownull;
194     /**
195      * Constructor
196      * @param mixed $type
197      * @param string $desc
198      * @param bool $required
199      * @param mixed $default
200      * @param bool $allownull
201      */
202     public function __construct($type, $desc='', $required=true, $default=null, $allownull=true) {
203         parent::__construct($desc, $required);
204         $this->type      = $type;
205         $this->default   = $default;
206         $this->allownull = $allownull;
207     }
210 /**
211  * Associative array description class
212  */
213 class external_single_structure extends external_description {
214      /** @property array $keys description of array keys key=>external_description */
215     public $keys;
217     /**
218      * Constructor
219      * @param array $keys
220      * @param string $desc
221      * @param bool $required
222      */
223     public function __construct(array $keys, $desc='', $required=true) {
224         parent::__construct($desc, $required);
225         $this->keys = $keys;
226     }
229 /**
230  * Bulk array description class.
231  */
232 class external_multiple_structure extends external_description {
233      /** @property external_description $content */
234     public $content;
236     /**
237      * Constructor
238      * @param external_description $content
239      * @param string $desc
240      * @param bool $required
241      */
242     public function __construct(external_description $content, $desc='', $required=true) {
243         parent::__construct($desc, $required);
244         $this->content = $content;
245     }
248 /**
249  * Description of top level - PHP function parameters.
250  * @author skodak
251  *
252  */
253 class external_function_parameters extends external_single_structure {