See Release Notes
Long Term Support Release
Differences Between: [Versions 401 and 402] [Versions 401 and 403]
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/>. 16 17 18 /** 19 * This file contains the class definition for the mahara portfolio plugin 20 * 21 * @since Moodle 2.0 22 * @package moodlecore 23 * @subpackage portfolio 24 * @copyright 2009 Penny Leach 25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 */ 27 28 29 define('PORTFOLIO_MAHARA_ERR_NETWORKING_OFF', 'err_networkingoff'); 30 define('PORTFOLIO_MAHARA_ERR_NOHOSTS', 'err_nomnethosts'); 31 define('PORTFOLIO_MAHARA_ERR_INVALIDHOST', 'err_invalidhost'); 32 define('PORTFOLIO_MAHARA_ERR_NOMNETAUTH', 'err_nomnetauth'); 33 34 require_once($CFG->libdir . '/portfoliolib.php'); 35 require_once($CFG->libdir . '/portfolio/plugin.php'); 36 require_once($CFG->libdir . '/portfolio/exporter.php'); 37 require_once($CFG->dirroot . '/mnet/lib.php'); 38 39 define('PORTFOLIO_MAHARA_QUEUE', PORTFOLIO_TIME_HIGH); 40 define('PORTFOLIO_MAHARA_IMMEDIATE', PORTFOLIO_TIME_MODERATE); 41 42 class portfolio_plugin_mahara extends portfolio_plugin_pull_base { 43 44 private $hosts; // used in the admin config form 45 private $mnethost; // privately set during export from the admin config value (mnethostid) 46 private $hostrecord; // the host record that corresponds to the peer 47 private $token; // during-transfer token 48 private $sendtype; // whatever mahara has said it can handle (immediate or queued) 49 private $filesmanifest; // manifest of files to send to mahara (set during prepare_package and sent later) 50 private $totalsize; // total size of all included files added together 51 private $continueurl; // if we've been sent back a specific url to continue to (eg folder id) 52 53 protected function init() { 54 $this->mnet = get_mnet_environment(); 55 } 56 57 public function __wakeup() { 58 $this->mnet = get_mnet_environment(); 59 } 60 61 public static function get_name() { 62 return get_string('pluginname', 'portfolio_mahara'); 63 } 64 65 public static function get_allowed_config() { 66 return array('mnethostid', 'enableleap2a'); 67 } 68 69 public function supported_formats() { 70 if ($this->get_config('enableleap2a')) { 71 return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_LEAP2A); 72 } 73 return array(PORTFOLIO_FORMAT_FILE); 74 } 75 76 public function expected_time($callertime) { 77 if ($this->sendtype == PORTFOLIO_MAHARA_QUEUE) { 78 return PORTFOLIO_TIME_FORCEQUEUE; 79 } 80 return $callertime; 81 } 82 83 public static function has_admin_config() { 84 return true; 85 } 86 87 public static function admin_config_form(&$mform) { 88 $strrequired = get_string('required'); 89 $hosts = self::get_mnet_hosts(); // this is called by sanity check but it's ok because it's cached 90 foreach ($hosts as $host) { 91 $hosts[$host->id] = $host->name; 92 } 93 $mform->addElement('select', 'mnethostid', get_string('mnethost', 'portfolio_mahara'), $hosts); 94 $mform->addRule('mnethostid', $strrequired, 'required', null, 'client'); 95 $mform->setType('mnethostid', PARAM_INT); 96 $mform->addElement('selectyesno', 'enableleap2a', get_string('enableleap2a', 'portfolio_mahara')); 97 $mform->setType('enableleap2a', PARAM_BOOL); 98 } 99 100 public function instance_sanity_check() { 101 // make sure the host record exists since we don't have referential integrity 102 if (!is_enabled_auth('mnet')) { 103 return PORTFOLIO_MAHARA_ERR_NOMNETAUTH; 104 } 105 try { 106 $this->ensure_mnethost(); 107 } 108 catch (portfolio_exception $e) { 109 return PORTFOLIO_MAHARA_ERR_INVALIDHOST; 110 } 111 // make sure we have the right services 112 $hosts = $this->get_mnet_hosts(); 113 if (!array_key_exists($this->get_config('mnethostid'), $hosts)) { 114 return PORTFOLIO_MAHARA_ERR_INVALIDHOST; 115 } 116 return 0; 117 } 118 119 public static function plugin_sanity_check() { 120 global $CFG, $DB; 121 $errorcode = 0; 122 if (!isset($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode != 'strict') { 123 $errorcode = PORTFOLIO_MAHARA_ERR_NETWORKING_OFF; 124 } 125 if (!is_enabled_auth('mnet')) { 126 $errorcode = PORTFOLIO_MAHARA_ERR_NOMNETAUTH; 127 } 128 if (!self::get_mnet_hosts()) { 129 $errorcode = PORTFOLIO_MAHARA_ERR_NOHOSTS; 130 } 131 return $errorcode; 132 } 133 134 private static function get_mnet_hosts() { 135 global $DB, $CFG; 136 static $hosts; 137 if (isset($hosts)) { 138 return $hosts; 139 } 140 $hosts = $DB->get_records_sql(' SELECT 141 h.id, 142 h.wwwroot, 143 h.ip_address, 144 h.name, 145 h.public_key, 146 h.public_key_expires, 147 h.transport, 148 h.portno, 149 h.last_connect_time, 150 h.last_log_id, 151 h.applicationid, 152 a.name as app_name, 153 a.display_name as app_display_name, 154 a.xmlrpc_server_url 155 FROM {mnet_host} h 156 JOIN {mnet_application} a ON h.applicationid=a.id 157 JOIN {mnet_host2service} hs1 ON hs1.hostid = h.id 158 JOIN {mnet_service} s1 ON hs1.serviceid = s1.id 159 JOIN {mnet_host2service} hs2 ON hs2.hostid = h.id 160 JOIN {mnet_service} s2 ON hs2.serviceid = s2.id 161 JOIN {mnet_host2service} hs3 ON hs3.hostid = h.id 162 JOIN {mnet_service} s3 ON hs3.serviceid = s3.id 163 WHERE 164 h.id <> ? AND 165 h.deleted = 0 AND 166 a.name = ? AND 167 s1.name = ? AND hs1.publish = ? AND 168 s2.name = ? AND hs2.subscribe = ? AND 169 s3.name = ? AND hs3.subscribe = ? AND 170 s3.name = ? AND hs3.publish = ?', 171 array($CFG->mnet_localhost_id, 'mahara', 'sso_idp', 1, 'sso_sp', 1, 'pf', 1, 'pf', 1)); 172 return $hosts; 173 } 174 175 public function prepare_package() { 176 $files = $this->exporter->get_tempfiles(); 177 $this->totalsize = 0; 178 foreach ($files as $f) { 179 $this->filesmanifest[$f->get_contenthash()] = array( 180 'filename' => $f->get_filename(), 181 'sha1' => $f->get_contenthash(), 182 'size' => $f->get_filesize(), 183 ); 184 $this->totalsize += $f->get_filesize(); 185 } 186 187 $this->set('file', $this->exporter->zip_tempfiles()); // this will throw a file_exception which the exporter catches separately. 188 } 189 190 public function send_package() { 191 global $CFG; 192 // send the 'content_ready' request to mahara 193 require_once($CFG->dirroot . '/mnet/xmlrpc/client.php'); 194 $client = new mnet_xmlrpc_client(); 195 $client->set_method('portfolio/mahara/lib.php/send_content_ready'); 196 $client->add_param($this->token); 197 $client->add_param($this->get('user')->username); 198 $client->add_param($this->resolve_format()); 199 $client->add_param(array( 200 'filesmanifest' => $this->filesmanifest, 201 'zipfilesha1' => $this->get('file')->get_contenthash(), 202 'zipfilesize' => $this->get('file')->get_filesize(), 203 'totalsize' => $this->totalsize, 204 )); 205 $client->add_param($this->get_export_config('wait')); 206 $this->ensure_mnethost(); 207 if (!$client->send($this->mnethost)) { 208 foreach ($client->error as $errormessage) { 209 list($code, $message) = array_map('trim',explode(':', $errormessage, 2)); 210 $message .= "ERROR $code:<br/>$errormessage<br/>"; 211 } 212 throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara', '', $message); 213 } 214 // we should get back... an ok and a status 215 // either we've been waiting a while and mahara has fetched the file or has queued it. 216 $response = (object)$client->response; 217 if (!$response->status) { 218 throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara'); 219 } 220 if ($response->type =='queued') { 221 $this->exporter->set_forcequeue(); 222 } 223 if (isset($response->querystring)) { 224 $this->continueurl = $response->querystring; 225 } 226 // if we're not queuing the logging might have already happened 227 $this->exporter->update_log_url($this->get_static_continue_url()); 228 } 229 230 public function get_static_continue_url() { 231 $remoteurl = ''; 232 if ($this->resolve_format() == 'file') { 233 $remoteurl = '/artefact/file/'; // we hopefully get the files that were imported highlighted 234 } 235 if (isset($this->continueurl)) { 236 $remoteurl .= $this->continueurl; 237 } 238 return $remoteurl; 239 } 240 241 public function resolve_static_continue_url($remoteurl) { 242 global $CFG; 243 $this->ensure_mnethost(); 244 $u = new moodle_url('/auth/mnet/jump.php', array('hostid' => $this->get_config('mnethostid'), 'wantsurl' => $remoteurl)); 245 return $u->out(); 246 } 247 248 public function get_interactive_continue_url() { 249 return $this->resolve_static_continue_url($this->get_static_continue_url()); 250 } 251 252 public function steal_control($stage) { 253 if ($stage != PORTFOLIO_STAGE_CONFIG) { 254 return false; 255 } 256 global $CFG; 257 return $CFG->wwwroot . '/portfolio/mahara/preconfig.php?id=' . $this->exporter->get('id'); 258 } 259 260 public function verify_file_request_params($params) { 261 return false; 262 // the data comes from an xmlrpc request, 263 // not a request to file.php 264 } 265 266 /** 267 * sends the 'content_intent' ping to mahara 268 * if all goes well, this will set the 'token' and 'sendtype' member variables. 269 */ 270 public function send_intent() { 271 global $CFG, $DB; 272 require_once($CFG->dirroot . '/mnet/xmlrpc/client.php'); 273 $client = new mnet_xmlrpc_client(); 274 $client->set_method('portfolio/mahara/lib.php/send_content_intent'); 275 $client->add_param($this->get('user')->username); 276 $this->ensure_mnethost(); 277 if (!$client->send($this->mnethost)) { 278 foreach ($client->error as $errormessage) { 279 list($code, $message) = array_map('trim',explode(':', $errormessage, 2)); 280 $message .= "ERROR $code:<br/>$errormessage<br/>"; 281 } 282 throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara', '', $message); 283 } 284 // we should get back... the send type and a shared token 285 $response = (object)$client->response; 286 if (empty($response->sendtype) || empty($response->token)) { 287 throw new portfolio_export_exception($this->get('exporter'), 'senddisallowed', 'portfolio_mahara'); 288 } 289 switch ($response->sendtype) { 290 case 'immediate': 291 $this->sendtype = PORTFOLIO_MAHARA_IMMEDIATE; 292 break; 293 case 'queue': 294 $this->sendtype = PORTFOLIO_MAHARA_QUEUE; 295 break; 296 case 'none': 297 default: 298 throw new portfolio_export_exception($this->get('exporter'), 'senddisallowed', 'portfolio_mahara'); 299 } 300 $this->token = $response->token; 301 $this->get('exporter')->save(); 302 // put the entry in the mahara queue table now too 303 $q = new stdClass; 304 $q->token = $this->token; 305 $q->transferid = $this->get('exporter')->get('id'); 306 $DB->insert_record('portfolio_mahara_queue', $q); 307 } 308 309 private function ensure_mnethost() { 310 if (!empty($this->hostrecord) && !empty($this->mnethost)) { 311 return; 312 } 313 global $DB; 314 if (!$this->hostrecord = $DB->get_record('mnet_host', array('id' => $this->get_config('mnethostid')))) { 315 throw new portfolio_plugin_exception(PORTFOLIO_MAHARA_ERR_INVALIDHOST, 'portfolio_mahara'); 316 } 317 $this->mnethost = new mnet_peer(); 318 $this->mnethost->set_wwwroot($this->hostrecord->wwwroot); 319 } 320 321 /** 322 * xmlrpc (mnet) function to get the file. 323 * reads in the file and returns it base_64 encoded 324 * so that it can be enrypted by mnet. 325 * 326 * @param string $token the token recieved previously during send_content_intent 327 */ 328 public static function fetch_file($token) { 329 global $DB; 330 $remoteclient = get_mnet_remote_client(); 331 try { 332 if (!$transferid = $DB->get_field('portfolio_mahara_queue', 'transferid', array('token' => $token))) { 333 throw new mnet_server_exception(8009, 'mnet_notoken', 'portfolio_mahara'); 334 } 335 $exporter = portfolio_exporter::rewaken_object($transferid); 336 } catch (portfolio_exception $e) { 337 throw new mnet_server_exception(8010, 'mnet_noid', 'portfolio_mahara'); 338 } 339 if ($exporter->get('instance')->get_config('mnethostid') != $remoteclient->id) { 340 throw new mnet_server_exception(8011, 'mnet_wronghost', 'portfolio_mahara'); 341 } 342 global $CFG; 343 try { 344 $i = $exporter->get('instance'); 345 $f = $i->get('file'); 346 if (empty($f) || !($f instanceof stored_file)) { 347 throw new mnet_server_exception(8012, 'mnet_nofile', 'portfolio_mahara'); 348 } 349 try { 350 $c = $f->get_content(); 351 } catch (file_exception $e) { 352 throw new mnet_server_exception(8013, 'mnet_nofilecontents', 'portfolio_mahara', $e->getMessage()); 353 } 354 $contents = base64_encode($c); 355 } catch (Exception $e) { 356 throw new mnet_server_exception(8013, 'mnet_nofile', 'portfolio_mahara'); 357 } 358 $exporter->log_transfer(); 359 $exporter->process_stage_cleanup(true); 360 return $contents; 361 } 362 363 public function cleanup() { 364 global $DB; 365 $DB->delete_records('portfolio_mahara_queue', array('transferid' => $this->get('exporter')->get('id'), 'token' => $this->token)); 366 } 367 368 369 /** 370 * internal helper function, that converts between the format constant, 371 * which might be too specific (eg 'image') and the class in our *supported* list 372 * which might be higher up the format hierarchy tree (eg 'file') 373 */ 374 private function resolve_format() { 375 global $CFG; 376 $thisformat = $this->get_export_config('format'); 377 $allformats = portfolio_supported_formats(); 378 require_once($CFG->libdir . '/portfolio/formats.php'); 379 $thisobj = new $allformats[$thisformat]; 380 foreach ($this->supported_formats() as $f) { 381 $class = $allformats[$f]; 382 if ($thisobj instanceof $class) { 383 return $f; 384 } 385 } 386 } 387 } 388 389
title
Description
Body
title
Description
Body
title
Description
Body
title
Body