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