Merge branch 'MDL-66118-master-byebyemoodlenet' of git://github.com/mudrd8mz/moodle
[moodle.git] / lib / classes / hub / api.php
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/>.
17 /**
18  * Class communication
19  *
20  * @package    core
21  * @copyright  2017 Marina Glancy
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 namespace core\hub;
26 defined('MOODLE_INTERNAL') || die();
28 use webservice_xmlrpc_client;
29 use moodle_exception;
30 use curl;
31 use stdClass;
32 use coding_exception;
33 use moodle_url;
35 /**
36  * Provides methods to communicate with the hub (sites directory) web services.
37  *
38  * @package    core
39  * @copyright  2017 Marina Glancy
40  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41  */
42 class api {
44     /** @var File type: Course screenshot */
45     const HUB_SCREENSHOT_FILE_TYPE = 'screenshot';
47     /** @var File type: Hub screenshot */
48     const HUB_HUBSCREENSHOT_FILE_TYPE = 'hubscreenshot';
50     /** @var File type: Backup */
51     const HUB_BACKUP_FILE_TYPE = 'backup';
53     /**
54      * Calls a remote function exposed via web services on the hub.
55      *
56      * @param string $function name of WS function
57      * @param array $data parameters of WS function
58      * @param bool $allowpublic allow request without registration on the hub
59      * @return mixed depends on the function
60      * @throws moodle_exception
61      */
62     protected static function call($function, array $data = [], $allowpublic = false) {
64         $token = registration::get_token() ?: 'publichub';
65         if (!$allowpublic && $token === 'publichub') {
66             // This will throw an exception.
67             registration::require_registration();
68         }
70         return self::call_rest($token, $function, $data);
71     }
73     /**
74      * Performs a REST request to the hub site (using the GET method).
75      *
76      * @param string $token
77      * @param string $function
78      * @param array $data
79      * @return mixed
80      * @throws moodle_exception
81      */
82     protected static function call_rest($token, $function, array $data) {
83         $params = [
84                 'wstoken' => $token,
85                 'wsfunction' => $function,
86                 'moodlewsrestformat' => 'json'
87             ] + $data;
89         $curl = new curl();
90         $serverurl = HUB_MOODLEORGHUBURL . "/local/hub/webservice/webservices.php";
91         $query = http_build_query($params, '', '&');
92         $curloutput = @json_decode($curl->post($serverurl, $query), true);
93         $info = $curl->get_info();
94         if ($curl->get_errno()) {
95             // Connection error.
96             throw new moodle_exception('errorconnect', 'hub', '', $curl->error);
97         } else if (isset($curloutput['exception'])) {
98             // Exception occurred on the remote side.
99             self::process_curl_exception($token, $curloutput);
100         } else if ($info['http_code'] != 200) {
101             throw new moodle_exception('errorconnect', 'hub', '', $info['http_code']);
102         } else {
103             return $curloutput;
104         }
105     }
107     /**
108      * Analyses exception received from the hub server.
109      *
110      * @param string $token token used for CURL request
111      * @param array $curloutput output from CURL request
112      * @throws moodle_exception
113      */
114     protected static function process_curl_exception($token, $curloutput) {
115         if (!isset($curloutput['exception'])) {
116             return;
117         }
118         if ($token === registration::get_token()) {
119             // Check if registration token was rejected or there are other problems with registration.
120             if (($curloutput['exception'] === 'moodle_exception' && $curloutput['errorcode'] === 'invalidtoken')
121                     || $curloutput['exception'] === 'registration_exception') {
122                 // Force admin to repeat site registration process.
123                 registration::reset_token();
124                 throw new moodle_exception('errorwstokenreset', 'hub', '', $curloutput['message']);
125             }
126         }
127         throw new moodle_exception('errorws', 'hub', '', $curloutput['message']);
128     }
130     /**
131      * Update site registration on the hub.
132      *
133      * @param array $siteinfo
134      * @throws moodle_exception
135      */
136     public static function update_registration(array $siteinfo) {
137         $params = array('siteinfo' => $siteinfo, 'validateurl' => 1);
138         self::call('hub_update_site_info', $params);
139     }
141     /**
142      * Returns information about the hub.
143      *
144      * Example of the return array:
145      * {
146      *     "courses": 384,
147      *     "description": "Official Moodle sites directory.",
148      *     "downloadablecourses": 0,
149      *     "enrollablecourses": 0,
150      *     "hublogo": 1,
151      *     "language": "en",
152      *     "name": "moodle",
153      *     "sites": 274175,
154      *     "url": "https://stats.moodle.org",
155      *     "imgurl": "https://stats.moodle.org/local/hub/webservice/download.php?filetype=hubscreenshot"
156      * }
157      *
158      * @return array
159      * @throws moodle_exception
160      */
161     public static function get_hub_info() {
162         $info = self::call('hub_get_info', [], true);
163         $info['imgurl'] = new moodle_url(HUB_MOODLEORGHUBURL . '/local/hub/webservice/download.php',
164             ['filetype' => self::HUB_HUBSCREENSHOT_FILE_TYPE]);
165         return $info;
166     }
168     /**
169      * Calls WS function hub_get_courses
170      *
171      * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
172      *
173      * Parameter $options may have any of these fields:
174      * [
175      *     'ids' => new external_multiple_structure(new external_value(PARAM_INTEGER, 'id of a course in the hub course
176      *          directory'), 'ids of course', VALUE_OPTIONAL),
177      *     'sitecourseids' => new external_multiple_structure(new external_value(PARAM_INTEGER, 'id of a course in the
178      *          site'), 'ids of course in the site', VALUE_OPTIONAL),
179      *     'coverage' => new external_value(PARAM_TEXT, 'coverage', VALUE_OPTIONAL),
180      *     'licenceshortname' => new external_value(PARAM_ALPHANUMEXT, 'licence short name', VALUE_OPTIONAL),
181      *     'subject' => new external_value(PARAM_ALPHANUM, 'subject', VALUE_OPTIONAL),
182      *     'audience' => new external_value(PARAM_ALPHA, 'audience', VALUE_OPTIONAL),
183      *     'educationallevel' => new external_value(PARAM_ALPHA, 'educational level', VALUE_OPTIONAL),
184      *     'language' => new external_value(PARAM_ALPHANUMEXT, 'language', VALUE_OPTIONAL),
185      *     'orderby' => new external_value(PARAM_ALPHA, 'orderby method: newest, eldest, publisher, fullname,
186      *          ratingaverage', VALUE_OPTIONAL),
187      *     'givememore' => new external_value(PARAM_INT, 'next range of result - range size being set by the hub
188      *          server ', VALUE_OPTIONAL),
189      *     'allsitecourses' => new external_value(PARAM_INTEGER,
190      *          'if 1 return all not visible and visible courses whose siteid is the site
191      *          matching token. Only courses of this site are returned.
192      *          givememore parameter is ignored if this param = 1.
193      *          In case of public token access, this param option is ignored', VALUE_DEFAULT, 0),
194      * ]
195      *
196      * Each course in the returned array of courses will have fields:
197      * [
198      *     'id' => new external_value(PARAM_INTEGER, 'id'),
199      *     'fullname' => new external_value(PARAM_TEXT, 'course name'),
200      *     'shortname' => new external_value(PARAM_TEXT, 'course short name'),
201      *     'description' => new external_value(PARAM_TEXT, 'course description'),
202      *     'language' => new external_value(PARAM_ALPHANUMEXT, 'course language'),
203      *     'publishername' => new external_value(PARAM_TEXT, 'publisher name'),
204      *     'publisheremail' => new external_value(PARAM_EMAIL, 'publisher email', VALUE_OPTIONAL),
205      *     'privacy' => new external_value(PARAM_INT, 'privacy: published or not', VALUE_OPTIONAL),
206      *     'sitecourseid' => new external_value(PARAM_INT, 'course id on the site', VALUE_OPTIONAL),
207      *     'contributornames' => new external_value(PARAM_TEXT, 'contributor names', VALUE_OPTIONAL),
208      *     'coverage' => new external_value(PARAM_TEXT, 'coverage', VALUE_OPTIONAL),
209      *     'creatorname' => new external_value(PARAM_TEXT, 'creator name'),
210      *     'licenceshortname' => new external_value(PARAM_ALPHANUMEXT, 'licence short name'),
211      *     'subject' => new external_value(PARAM_ALPHANUM, 'subject'),
212      *     'audience' => new external_value(PARAM_ALPHA, 'audience'),
213      *     'educationallevel' => new external_value(PARAM_ALPHA, 'educational level'),
214      *     'creatornotes' => new external_value(PARAM_RAW, 'creator notes'),
215      *     'creatornotesformat' => new external_value(PARAM_INTEGER, 'notes format'),
216      *     'demourl' => new external_value(PARAM_URL, 'demo URL', VALUE_OPTIONAL),
217      *     'courseurl' => new external_value(PARAM_URL, 'course URL', VALUE_OPTIONAL),
218      *     'backupsize' => new external_value(PARAM_INT, 'course backup size in bytes', VALUE_OPTIONAL),
219      *     'enrollable' => new external_value(PARAM_BOOL, 'is the course enrollable'),
220      *     'screenshots' => new external_value(PARAM_INT, 'total number of screenshots'),
221      *     'timemodified' => new external_value(PARAM_INT, 'time of last modification - timestamp'),
222      *     'contents' => new external_multiple_structure(new external_single_structure(
223      *         array(
224      *             'moduletype' => new external_value(PARAM_ALPHA, 'the type of module (activity/block)'),
225      *             'modulename' => new external_value(PARAM_TEXT, 'the name of the module (forum, resource etc)'),
226      *             'contentcount' => new external_value(PARAM_INT, 'how many time the module is used in the course'),
227      *         )), 'contents', VALUE_OPTIONAL),
228      *     'rating' => new external_single_structure (
229      *         array(
230      *              'aggregate' =>  new external_value(PARAM_FLOAT, 'Rating average', VALUE_OPTIONAL),
231      *              'scaleid' => new external_value(PARAM_INT, 'Rating scale'),
232      *              'count' => new external_value(PARAM_INT, 'Rating count'),
233      *         ), 'rating', VALUE_OPTIONAL),
234      *     'comments' => new external_multiple_structure(new external_single_structure (
235      *          array(
236      *              'comment' => new external_value(PARAM_TEXT, 'the comment'),
237      *              'commentator' => new external_value(PARAM_TEXT, 'the name of commentator'),
238      *              'date' => new external_value(PARAM_INT, 'date of the comment'),
239      *         )), 'contents', VALUE_OPTIONAL),
240      *     'outcomes' => new external_multiple_structure(new external_single_structure(
241      *          array(
242      *              'fullname' => new external_value(PARAM_TEXT, 'the outcome fullname')
243      *          )), 'outcomes', VALUE_OPTIONAL)
244      * ]
245      *
246      * Additional fields for each course:
247      *      'screenshotbaseurl' (moodle_url) URL of the first screenshot, only set if $course['screenshots']>0
248      *      'commenturl' (moodle_url) URL for comments
249      *
250      * @param string $search search string
251      * @param bool $downloadable return downloadable courses
252      * @param bool $enrollable return enrollable courses
253      * @param array|\stdClass $options other options from the list of allowed options:
254      *              'ids', 'sitecourseids', 'coverage', 'licenceshortname', 'subject', 'audience',
255      *              'educationallevel', 'language', 'orderby', 'givememore', 'allsitecourses'
256      * @return array of two elements: [$courses, $coursetotal]
257      * @throws \coding_exception
258      * @throws moodle_exception
259      */
260     public static function get_courses($search, $downloadable, $enrollable, $options) {
261         debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
262         return [[], 0];
263     }
265     /**
266      * Unregister the site
267      *
268      * @throws moodle_exception
269      */
270     public static function unregister_site() {
271         global $CFG;
272         self::call('hub_unregister_site', ['url' => [$CFG->wwwroot]]);
273     }
275     /**
276      * Unpublish courses
277      *
278      * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
279      *
280      * @param int[]|int $courseids
281      * @throws moodle_exception
282      */
283     public static function unregister_courses($courseids) {
284         debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
285     }
287     /**
288      * Publish one course
289      *
290      * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
291      *
292      * Expected contents of $courseinfo:
293      * [
294      *     'sitecourseid' => new external_value(PARAM_INT, 'the id of the course on the publishing site'),
295      *     'fullname' => new external_value(PARAM_TEXT, 'course name'),
296      *     'shortname' => new external_value(PARAM_TEXT, 'course short name'),
297      *     'description' => new external_value(PARAM_TEXT, 'course description'),
298      *     'language' => new external_value(PARAM_ALPHANUMEXT, 'course language'),
299      *     'publishername' => new external_value(PARAM_TEXT, 'publisher name'),
300      *     'publisheremail' => new external_value(PARAM_EMAIL, 'publisher email'),
301      *     'contributornames' => new external_value(PARAM_TEXT, 'contributor names'),
302      *     'coverage' => new external_value(PARAM_TEXT, 'coverage'),
303      *     'creatorname' => new external_value(PARAM_TEXT, 'creator name'),
304      *     'licenceshortname' => new external_value(PARAM_ALPHANUMEXT, 'licence short name'),
305      *     'subject' => new external_value(PARAM_ALPHANUM, 'subject'),
306      *     'audience' => new external_value(PARAM_ALPHA, 'audience'),
307      *     'educationallevel' => new external_value(PARAM_ALPHA, 'educational level'),
308      *     'creatornotes' => new external_value(PARAM_RAW, 'creator notes'),
309      *     'creatornotesformat' => new external_value(PARAM_INTEGER, 'notes format'),
310      *     'demourl' => new external_value(PARAM_URL, 'demo URL', VALUE_OPTIONAL),
311      *     'courseurl' => new external_value(PARAM_URL, 'course URL', VALUE_OPTIONAL),
312      *     'enrollable' => new external_value(PARAM_BOOL, 'is the course enrollable', VALUE_DEFAULT, 0),
313      *     'screenshots' => new external_value(PARAM_INT, 'the number of screenhots', VALUE_OPTIONAL),
314      *     'deletescreenshots' => new external_value(PARAM_INT, 'ask to delete all the existing screenshot files
315      *          (it does not reset the screenshot number)', VALUE_DEFAULT, 0),
316      *     'contents' => new external_multiple_structure(new external_single_structure(
317      *          array(
318      *              'moduletype' => new external_value(PARAM_ALPHA, 'the type of module (activity/block)'),
319      *              'modulename' => new external_value(PARAM_TEXT, 'the name of the module (forum, resource etc)'),
320      *              'contentcount' => new external_value(PARAM_INT, 'how many time the module is used in the course'),
321      *          )), 'contents', VALUE_OPTIONAL),
322      *     'outcomes' => new external_multiple_structure(new external_single_structure(
323      *         array(
324      *              'fullname' => new external_value(PARAM_TEXT, 'the outcome fullname')
325      *          )), 'outcomes', VALUE_OPTIONAL)
326      * ]
327      *
328      * @param array|\stdClass $courseinfo
329      * @return int id of the published course on the hub
330      * @throws moodle_exception if the communication with the hub failed or the course could not be published
331      */
332     public static function register_course($courseinfo) {
333         debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
334         throw new moodle_exception('errorcoursewronglypublished', 'hub');
335     }
337     /**
338      * Uploads a screenshot for the published course
339      *
340      * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
341      *
342      * @param int $hubcourseid id of the published course on the hub, it must be published from this site
343      * @param \stored_file $file
344      * @param int $screenshotnumber ordinal number of the screenshot
345      */
346     public static function add_screenshot($hubcourseid, \stored_file $file, $screenshotnumber) {
347         debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
348     }
350     /**
351      * Downloads course backup
352      *
353      * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
354      *
355      * @param int $hubcourseid id of the course on the hub
356      * @param string $path local path (in tempdir) to save the downloaded backup to.
357      */
358     public static function download_course_backup($hubcourseid, $path) {
359         debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
360     }
362     /**
363      * Uploads a course backup
364      *
365      * @deprecated since Moodle 3.8. Moodle.net has been sunsetted making this function useless.
366      *
367      * @param int $hubcourseid id of the published course on the hub, it must be published from this site
368      * @param \stored_file $backupfile
369      */
370     public static function upload_course_backup($hubcourseid, \stored_file $backupfile) {
371         debugging("This function has been deprecated as part of the Moodle.net sunsetting process.");
372     }