/** * Updates sites data (from moodle.org) into {hub_sites_directory} in moodle.net (hub.moodle.org) * @return object */ public static function sync_into_sitesregister($sites) { global $DB; // Ensure the current user is allowed to run this function $context = context_system::instance(); self::validate_context($context); require_capability('local/hub:viewinfo', $context); $returnable = new stdClass(); try { $params = self::validate_parameters(self::sync_into_sitesregister_parameters(), array('newdatasince' => $sites)); } catch (invalid_parameter_exception $ex) { // record and send back later - but try individual records (with individual stricter validation) $returnable->exception = $ex->debuginfo; } //do our own additional validation (to circumvent core and proceed with using/replacing nulls.) $nullablefields = array('hubid', 'url', 'name', 'description', 'moodleversion', 'moodlerelease', 'serverstring', 'host', 'ip', 'language', 'secret', 'countrycode', 'deleted', 'publicationmax', 'regioncode', 'street', 'geolocation', 'contactname', 'contactemail', 'contactphone', 'imageurl', 'privacy', 'confirmed', 'redirectto', 'latitude', 'longitude'); $nullable_map = array('name' => ' ', 'deleted' => 0, 'publicationmax' => null); //what they should be in mdl_hub_site_directory $hub = new local_hub(); $syncerecs = array(); foreach ($params['newdatasince'] as $registrysite) { try { $syncrec = new stdClass(); //used in catch so we'll init here. $registrysite = (object) $registrysite; $syncrec->id = $registrysite->id; $syncrec->hubid = null; $objvars = get_object_vars($registrysite); //convert back to null and run extra validation. foreach ($objvars as $prop => $val) { if ($registrysite->otpnull === $val) { //got a null marker. if (array_key_exists($prop, $nullable_map)) { $registrysite->{$prop} = $nullable_map[$prop]; } else { if (in_array($prop, $nullablefields)) { //check on agreed nullables (remove if this is too restrictive in future) $registrysite->{$prop} = null; } } } } //drop otpnull and revalidate. unset($registrysite->otpnull); try { $registrysite = (object) self::validate_parameters(self::sync_into_sitesregister_parameters_safe(), (array) $registrysite); } catch (invalid_parameter_exception $ex) { //allow if it had been confirmed during moodle.org registration process. if (strpos($ex->debuginfo, 'url') == 0) { //exception starts with fieldname. if ($registrysite->confirmed == 0) { throw $ex; } } else { throw $ex; } } // if not the same, merge legacy 'mailme' into 'contactable' for hub. if ($registrysite->mailme != $registrysite->contactable) { //somewhere contactable was brought to registry@moodle.org. only 1.9 site registration uses mailme. $registrysite->contactable = $registrysite->mailme; // 1.9 changes to mailme brought through to hub. (this is a quick fix to a mess) @todo cleanup } //fix some common data length errors - just truncate (original is stored in moodle.org registry and a 2.x upgrade can fix it at hub) if (strlen($registrysite->moodlerelease) > 50) { $registrysite->moodlerelease = substr($registrysite->moodlerelease, 0, 49); } if (strlen($registrysite->ip) > 45) { $registrysite->ip = substr($registrysite->ip, 0, 44); } if (mb_detect_encoding($registrysite->name) == 'UTF-8' && mb_strlen($registrysite->name) > 255) { $registrysite->name = mb_substr($registrysite->name, 0, 246); //truncate must be mb safe!... } else { if (strlen($registrysite->name) > 255) { $registrysite->name = substr($registrysite->name, 0, 248); } } if (strlen($registrysite->countrycode) > 2) { $registrysite->countrycode = 'ZZ'; //the code for unknown country. solve this later in some checker. } if ($registrysite->hubid > 0) { // update this record $registrysite->id = $registrysite->hubid; unset($registrysite->hubid); // we don't care about registry ids at hub. $registrysite->unreachable = 0; //updated site means we should re-check this. $hub->update_site($registrysite); // has its own timemodified stamp $syncrec->hubid = $registrysite->id; // regsiteid -> hubid } else { if ($registrysite->hubid == null) { // add new unsycned site record // check! (remote may have failed in updating hubid, so this may just be an old skippable update to try again) unset($registrysite->id); unset($registrysite->hubid); $hubsite = $hub->add_site($registrysite, true); // has its own timecreated stamp $syncrec->hubid = $hubsite->id; } else { // just try to see if there is any match by url. (hubid would be < 1 to indicate previously failed syncs) $matchedsite = $hub->get_site_by_url($registrysite->url); if ($matchedsite && $registrysite->hubid < 1 && $matchedsite->secret == $registrysite->secret) { foreach (get_object_vars($registrysite) as $prop => $val) { if (isset($matchedsite->{$prop})) { $matchedsite->{$prop} = $val; } } $hub->update_site($matchedsite); $syncrec->hubid = $matchedsite->id; } } } } catch (Exception $ex) { // don't limit type of exception - carry on for all exceptions since we're working per record now. $syncrec->exception = $ex->debuginfo; } if (isset($syncrec->exception)) { error_log('sync_into_sitesregister() failed processing id ' . $syncrec->id); error_log('hubid ' . $syncrec->hubid); error_log('url ' . $registrysite->url); $syncrec->exception = utf8_encode($syncrec->exception); //avoid character codings (non-utf8) causing response validation errors. error_log('exception: ' . $syncrec->exception); } $syncerecs[] = $syncrec; } $returnable->reghubidmap = $syncerecs; $returnable->timesynced = time(); return $returnable; }