MDL-59495 moodlenet: move all moodlenet code into new namespace
[moodle.git] / lib / classes / hub / registration.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 registration
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 moodle_exception;
29 use moodle_url;
30 use context_system;
31 use stdClass;
33 /**
34  * Methods to use when publishing and searching courses on moodle.net
35  *
36  * @package    core
37  * @copyright  2017 Marina Glancy
38  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39  */
40 class registration {
42     /** @var Fields used in a site registration form */
43     const FORM_FIELDS = ['name', 'description', 'contactname', 'contactemail', 'contactphone', 'imageurl', 'privacy', 'street',
44         'regioncode', 'countrycode', 'geolocation', 'contactable', 'emailalert', 'language'];
46     /** @var Site privacy: not displayed */
47     const HUB_SITENOTPUBLISHED = 'notdisplayed';
49     /** @var Site privacy: public */
50     const HUB_SITENAMEPUBLISHED = 'named';
52     /** @var Site privacy: public and global */
53     const HUB_SITELINKPUBLISHED = 'linked';
55     /** @var stdClass cached site registration information */
56     protected static $registration = null;
58     /**
59      * Get site registration
60      *
61      * @param bool $confirmed
62      * @return stdClass|null
63      */
64     protected static function get_registration($confirmed = true) {
65         global $DB;
67         if (self::$registration === null) {
68             self::$registration = $DB->get_record('registration_hubs', ['huburl' => HUB_MOODLEORGHUBURL]);
69         }
71         if (self::$registration && (bool)self::$registration->confirmed == (bool)$confirmed) {
72             return self::$registration;
73         }
75         return null;
76     }
78     /**
79      * Same as get_registration except it throws exception if site not registered
80      *
81      * @return stdClass
82      * @throws \moodle_exception
83      */
84     public static function require_registration() {
85         if ($registration = self::get_registration()) {
86             return $registration;
87         }
88         if (has_capability('moodle/site:config', context_system::instance())) {
89             throw new moodle_exception('registrationwarning', 'admin', new moodle_url('/admin/registration/index.php'));
90         } else {
91             throw new moodle_exception('registrationwarningcontactadmin', 'admin');
92         }
93     }
95     /**
96      * Checks if site is registered
97      *
98      * @return bool
99      */
100     public static function is_registered() {
101         return self::get_registration() ? true : false;
102     }
104     /**
105      * Returns registration token
106      *
107      * @param int $strictness if set to MUST_EXIST and site is not registered will throw an exception
108      * @return null
109      * @throws moodle_exception
110      */
111     public static function get_token($strictness = IGNORE_MISSING) {
112         if ($strictness == MUST_EXIST) {
113             $registration = self::require_registration();
114         } else if (!$registration = self::get_registration()) {
115             return null;
116         }
117         return $registration->token;
118     }
120     /**
121      * When was the registration last updated
122      *
123      * @return int|null timestamp or null if site is not registered
124      */
125     public static function get_last_updated() {
126         if ($registration = self::get_registration()) {
127             return $registration->timemodified;
128         }
129         return null;
130     }
132     /**
133      * Calculates and prepares site information to send to moodle.net as part of registration or update
134      *
135      * @param array $defaults default values for inputs in the registration form (if site was never registered before)
136      * @return array site info
137      */
138     public static function get_site_info($defaults = []) {
139         global $CFG, $DB;
140         require_once($CFG->libdir . '/badgeslib.php');
141         require_once($CFG->dirroot . "/course/lib.php");
143         $siteinfo = array();
144         $cleanhuburl = clean_param(HUB_MOODLEORGHUBURL, PARAM_ALPHANUMEXT);
145         foreach (self::FORM_FIELDS as $field) {
146             $siteinfo[$field] = get_config('hub', 'site_'.$field.'_' . $cleanhuburl);
147             if ($siteinfo[$field] === false && array_key_exists($field, $defaults)) {
148                 $siteinfo[$field] = $defaults[$field];
149             }
150         }
152         // Statistical data.
153         $siteinfo['courses'] = $DB->count_records('course') - 1;
154         $siteinfo['users'] = $DB->count_records('user', array('deleted' => 0));
155         $siteinfo['enrolments'] = $DB->count_records('role_assignments');
156         $siteinfo['posts'] = $DB->count_records('forum_posts');
157         $siteinfo['questions'] = $DB->count_records('question');
158         $siteinfo['resources'] = $DB->count_records('resource');
159         $siteinfo['badges'] = $DB->count_records_select('badge', 'status <> ' . BADGE_STATUS_ARCHIVED);
160         $siteinfo['issuedbadges'] = $DB->count_records('badge_issued');
161         $siteinfo['participantnumberaverage'] = average_number_of_participants();
162         $siteinfo['modulenumberaverage'] = average_number_of_courses_modules();
164         // Version and url.
165         $siteinfo['moodleversion'] = $CFG->version;
166         $siteinfo['moodlerelease'] = $CFG->release;
167         $siteinfo['url'] = $CFG->wwwroot;
169         // Mobile related information.
170         $siteinfo['mobileservicesenabled'] = 0;
171         $siteinfo['mobilenotificationsenabled'] = 0;
172         $siteinfo['registereduserdevices'] = 0;
173         $siteinfo['registeredactiveuserdevices'] = 0;
174         if (!empty($CFG->enablewebservices) && !empty($CFG->enablemobilewebservice)) {
175             $siteinfo['mobileservicesenabled'] = 1;
176             $siteinfo['registereduserdevices'] = $DB->count_records('user_devices');
177             $airnotifierextpath = $CFG->dirroot . '/message/output/airnotifier/externallib.php';
178             if (file_exists($airnotifierextpath)) { // Maybe some one uninstalled the plugin.
179                 require_once($airnotifierextpath);
180                 $siteinfo['mobilenotificationsenabled'] = \message_airnotifier_external::is_system_configured();
181                 $siteinfo['registeredactiveuserdevices'] = $DB->count_records('message_airnotifier_devices', array('enable' => 1));
182             }
183         }
185         return $siteinfo;
186     }
188     /**
189      * Save registration info locally so it can be retrieved when registration needs to be updated
190      *
191      * @param stdClass $formdata data from {@link site_registration_form}
192      */
193     public static function save_site_info($formdata) {
194         $cleanhuburl = clean_param(HUB_MOODLEORGHUBURL, PARAM_ALPHANUMEXT);
195         foreach (self::FORM_FIELDS as $field) {
196             set_config('site_' . $field . '_' . $cleanhuburl, $formdata->$field, 'hub');
197         }
198     }
200     /**
201      * Updates site registration when "Update reigstration" button is clicked by admin
202      */
203     public static function update_manual() {
204         global $DB;
206         if (!$registration = self::get_registration()) {
207             return false;
208         }
210         $siteinfo = self::get_site_info();
211         try {
212             api::update_registration($siteinfo);
213         } catch (moodle_exception $e) {
214             \core\notification::add(get_string('errorregistrationupdate', 'hub', $e->getMessage()),
215                 \core\output\notification::NOTIFY_ERROR);
216             return false;
217         }
218         $DB->update_record('registration_hubs', ['id' => $registration->id, 'timemodified' => time()]);
219         \core\notification::add(get_string('siteregistrationupdated', 'hub'),
220             \core\output\notification::NOTIFY_SUCCESS);
221         self::$registration = null;
222         return true;
223     }
225     /**
226      * Updates site registration via cron
227      *
228      * @throws moodle_exception
229      */
230     public static function update_cron() {
231         global $DB;
233         if (!$registration = self::get_registration()) {
234             mtrace(get_string('registrationwarning', 'admin'));
235             return;
236         }
238         $siteinfo = self::get_site_info();
239         api::update_registration($siteinfo);
240         $DB->update_record('registration_hubs', ['id' => $registration->id, 'timemodified' => time()]);
241         mtrace(get_string('siteregistrationupdated', 'hub'));
242         self::$registration = null;
243     }
245     /**
246      * Confirms registration by moodle.net
247      *
248      * @param string $token
249      * @param string $newtoken
250      * @param string $hubname
251      * @throws moodle_exception
252      */
253     public static function confirm_registration($token, $newtoken, $hubname) {
254         global $DB;
256         $registration = self::get_registration(false);
257         if (!$registration || $registration->token !== $token) {
258             throw new moodle_exception('wrongtoken', 'hub', new moodle_url('/admin/registration/index.php'));
259         }
260         $record = ['id' => $registration->id];
261         $record['token'] = $newtoken;
262         $record['confirmed'] = 1;
263         $record['hubname'] = $hubname;
264         $record['timemodified'] = time();
265         $DB->update_record('registration_hubs', $record);
266         self::$registration = null;
267     }
269     /**
270      * Retrieve the options for site privacy form element to use in registration form
271      * @return array
272      */
273     public static function site_privacy_options() {
274         return [
275             self::HUB_SITENOTPUBLISHED => get_string('siteprivacynotpublished', 'hub'),
276             self::HUB_SITENAMEPUBLISHED => get_string('siteprivacypublished', 'hub'),
277             self::HUB_SITELINKPUBLISHED => get_string('siteprivacylinked', 'hub')
278         ];
279     }
281     /**
282      * Registers a site
283      *
284      * This method will make sure that unconfirmed registration record is created and then redirect to
285      * registration script on https://moodle.net
286      * Moodle.net will check that the site is accessible, register it and redirect back
287      * to /admin/registration/confirmregistration.php
288      *
289      * @throws \coding_exception
290      */
291     public static function register() {
292         global $DB;
294         if (self::is_registered()) {
295             // Caller of this method must make sure that site is not registered.
296             throw new \coding_exception('Site already registered');
297         }
299         $hub = self::get_registration(false);
300         if (empty($hub)) {
301             // Create a new record in 'registration_hubs'.
302             $hub = new stdClass();
303             $hub->token = get_site_identifier();
304             $hub->secret = $hub->token;
305             $hub->huburl = HUB_MOODLEORGHUBURL;
306             $hub->hubname = 'Moodle.net';
307             $hub->confirmed = 0;
308             $hub->timemodified = time();
309             $hub->id = $DB->insert_record('registration_hubs', $hub);
310             self::$registration = null;
311         }
313         $params = self::get_site_info();
314         $params['token'] = $hub->token;
316         redirect(new moodle_url(HUB_MOODLEORGHUBURL . '/local/hub/siteregistration.php', $params));
317     }
319     /**
320      * Unregister site
321      *
322      * @param bool $unpublishalladvertisedcourses
323      * @param bool $unpublishalluploadedcourses
324      * @return bool
325      */
326     public static function unregister($unpublishalladvertisedcourses, $unpublishalluploadedcourses) {
327         global $DB;
329         if (!$hub = self::get_registration()) {
330             return true;
331         }
333         // Unpublish the courses.
334         try {
335             publication::delete_all_publications($unpublishalladvertisedcourses, $unpublishalluploadedcourses);
336         } catch (moodle_exception $e) {
337             $errormessage = $e->getMessage();
338             $errormessage .= \html_writer::empty_tag('br') .
339                 get_string('errorunpublishcourses', 'hub');
341             \core\notification::add(get_string('unregistrationerror', 'hub', $errormessage),
342                 \core\output\notification::NOTIFY_ERROR);
343             return false;
344         }
346         // Course unpublish went ok, unregister the site now.
347         try {
348             api::unregister_site();
349         } catch (moodle_exception $e) {
350             \core\notification::add(get_string('unregistrationerror', 'hub', $e->getMessage()),
351                 \core\output\notification::NOTIFY_ERROR);
352             return false;
353         }
355         $DB->delete_records('registration_hubs', array('id' => $hub->id));
356         self::$registration = null;
357         return true;
358     }
360     /**
361      * Generate a new token for the site that is not registered
362      *
363      * @param string $token
364      * @throws moodle_exception
365      */
366     public static function reset_site_identifier($token) {
367         global $DB, $CFG;
369         $registration = self::get_registration(false);
370         if (!$registration || $registration->token != $token) {
371             throw new moodle_exception('wrongtoken', 'hub',
372                new moodle_url('/admin/registration/index.php'));
373         }
375         $DB->delete_records('registration_hubs', array('id' => $registration->id));
376         self::$registration = null;
378         $CFG->siteidentifier = null;
379         get_site_identifier();
380     }
382     /**
383      * Returns information about moodle.net
384      *
385      * Example of the return array:
386      * {
387      *     "courses": 384,
388      *     "description": "Moodle.net connects you with free content and courses shared by Moodle ...",
389      *     "downloadablecourses": 190,
390      *     "enrollablecourses": 194,
391      *     "hublogo": 1,
392      *     "language": "en",
393      *     "name": "Moodle.net",
394      *     "sites": 274175,
395      *     "url": "https://moodle.net",
396      *     "imgurl": moodle_url : "https://moodle.net/local/hub/webservice/download.php?filetype=hubscreenshot"
397      * }
398      *
399      * @return array|null
400      */
401     public static function get_moodlenet_info() {
402         try {
403             return api::get_hub_info();
404         } catch (moodle_exception $e) {
405             // Ignore error, we only need it for displaying information about moodle.net, if this request
406             // fails, it's not a big deal.
407             return null;
408         }
409     }