admin/webservices MDL-12886 Fixed some strings
[moodle.git] / portfolio / mahara / lib.php
CommitLineData
254f2d05 1<?php
de260e0f
PL
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 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
254f2d05 28
29define('PORTFOLIO_MAHARA_ERR_NETWORKING_OFF', 'err_networkingoff');
30define('PORTFOLIO_MAHARA_ERR_NOHOSTS', 'err_nomnethosts');
aae8cfdc 31define('PORTFOLIO_MAHARA_ERR_INVALIDHOST', 'err_invalidhost');
7254f56e 32define('PORTFOLIO_MAHARA_ERR_NOMNETAUTH', 'err_nomnetauth');
254f2d05 33
24ba58ee
PL
34require_once($CFG->libdir . '/portfoliolib.php');
35require_once($CFG->libdir . '/portfolio/plugin.php');
36require_once($CFG->libdir . '/portfolio/exporter.php');
254f2d05 37require_once($CFG->dirroot . '/mnet/lib.php');
38
39define('PORTFOLIO_MAHARA_QUEUE', PORTFOLIO_TIME_HIGH);
40define('PORTFOLIO_MAHARA_IMMEDIATE', PORTFOLIO_TIME_MODERATE);
41
42class 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)
2f6bd2a9 50 private $totalsize; // total size of all included files added together
14cb94eb 51 private $continueurl; // if we've been sent back a specific url to continue to (eg folder id)
254f2d05 52
0f71f48b 53 public static function get_name() {
54 return get_string('pluginname', 'portfolio_mahara');
55 }
56
254f2d05 57 public static function get_allowed_config() {
58 return array('mnethostid');
59 }
60
61 public static function supported_formats() {
62 return array(PORTFOLIO_FORMAT_FILE);
59dd457e
PL
63 // TODO remove above line once leap over mnet is tested
64 return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_LEAP2A);
254f2d05 65 }
66
67 public function expected_time($callertime) {
68 if ($this->sendtype == PORTFOLIO_MAHARA_QUEUE) {
69 return PORTFOLIO_TIME_FORCEQUEUE;
70 }
71 return $callertime;
72 }
73
74 public static function has_admin_config() {
75 return true;
76 }
77
78 public function admin_config_form(&$mform) {
254f2d05 79 $strrequired = get_string('required');
80 $hosts = self::get_mnet_hosts(); // this is called by sanity check but it's ok because it's cached
81 foreach ($hosts as $host) {
82 $hosts[$host->id] = $host->name;
83 }
84 $mform->addElement('select', 'mnethostid', get_string('mnethost', 'portfolio_mahara'), $hosts);
85 $mform->addRule('mnethostid', $strrequired, 'required', null, 'client');
86 }
87
aae8cfdc 88 public function instance_sanity_check() {
89 // make sure the host record exists since we don't have referential integrity
b0482154 90 if (!is_enabled_auth('mnet')) {
91 return PORTFOLIO_MAHARA_ERR_NOMNETAUTH;
92 }
aae8cfdc 93 try {
94 $this->ensure_mnethost();
95 }
96 catch (portfolio_exception $e) {
97 return PORTFOLIO_MAHARA_ERR_INVALIDHOST;
98 }
99 // make sure we have the right services
100 $hosts = $this->get_mnet_hosts();
101 if (!array_key_exists($this->get_config('mnethostid'), $hosts)) {
102 return PORTFOLIO_MAHARA_ERR_INVALIDHOST;
103 }
104 return 0;
105 }
254f2d05 106
107 public static function plugin_sanity_check() {
254f2d05 108 global $CFG, $DB;
109 $errorcode = 0;
110 if (!isset($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode != 'strict') {
111 $errorcode = PORTFOLIO_MAHARA_ERR_NETWORKING_OFF;
112 }
7254f56e 113 if (!is_enabled_auth('mnet')) {
114 $errorcode = PORTFOLIO_MAHARA_ERR_NOMNETAUTH;
115 }
254f2d05 116 if (!self::get_mnet_hosts()) {
117 $errorcode = PORTFOLIO_MAHARA_ERR_NOHOSTS;
118 }
254f2d05 119 return $errorcode;
120 }
121
122 private static function get_mnet_hosts() {
123 global $DB, $CFG;
124 static $hosts;
125 if (isset($this) && is_object($this) && isset($this->hosts)) {
126 return $this->hosts;
127 } else if (!isset($this) && isset($hosts)) {
128 return $hosts;
129 }
130 $hosts = $DB->get_records_sql(' SELECT
131 h.id,
132 h.wwwroot,
133 h.ip_address,
134 h.name,
135 h.public_key,
136 h.public_key_expires,
137 h.transport,
138 h.portno,
139 h.last_connect_time,
140 h.last_log_id,
141 h.applicationid,
142 a.name as app_name,
143 a.display_name as app_display_name,
144 a.xmlrpc_server_url
145 FROM {mnet_host} h
146 JOIN {mnet_application} a ON h.applicationid=a.id
147 JOIN {mnet_host2service} hs1 ON hs1.hostid = h.id
148 JOIN {mnet_service} s1 ON hs1.serviceid = s1.id
149 JOIN {mnet_host2service} hs2 ON hs2.hostid = h.id
150 JOIN {mnet_service} s2 ON hs2.serviceid = s2.id
151 JOIN {mnet_host2service} hs3 ON hs3.hostid = h.id
152 JOIN {mnet_service} s3 ON hs3.serviceid = s3.id
153 WHERE
154 h.id <> ? AND
155 h.deleted = 0 AND
156 a.name = ? AND
157 s1.name = ? AND hs1.publish = ? AND
158 s2.name = ? AND hs2.subscribe = ? AND
8a709ecf
PL
159 s3.name = ? AND hs3.subscribe = ? AND
160 s3.name = ? AND hs3.publish = ?',
161 array($CFG->mnet_localhost_id, 'mahara', 'sso_idp', 1, 'sso_sp', 1, 'pf', 1, 'pf', 1));
254f2d05 162 if (empty($hosts)) { $hosts = array(); }
163 if (isset($this) && is_object($this)) {
164 $this->hosts = $hosts;
165 }
166 return $hosts;
167 }
168
169 public function prepare_package() {
170 $files = $this->exporter->get_tempfiles();
2f6bd2a9 171 $this->totalsize = 0;
254f2d05 172 foreach ($files as $f) {
173 $this->filesmanifest[$f->get_contenthash()] = array(
174 'filename' => $f->get_filename(),
175 'sha1' => $f->get_contenthash(),
2f6bd2a9 176 'size' => $f->get_filesize(),
254f2d05 177 );
2f6bd2a9 178 $this->totalsize += $f->get_filesize();
254f2d05 179 }
254f2d05 180
37f03ea0 181 $this->set('file', $this->exporter->zip_tempfiles()); // this will throw a file_exception which the exporter catches separately.
254f2d05 182 }
183
aed2937f 184 private function ensure_environment() {
254f2d05 185 global $MNET;
186 if (empty($MNET)) {
187 $MNET = new mnet_environment();
188 $MNET->init();
189 } // no idea why this happens :(
aed2937f 190 }
191
192 public function send_package() {
193 global $CFG;
194 $this->ensure_environment();
254f2d05 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());
1c597211 202 $client->add_param(array(
203 'filesmanifest' => $this->filesmanifest,
2f6bd2a9 204 'zipfilesha1' => $this->get('file')->get_contenthash(),
205 'zipfilesize' => $this->get('file')->get_filesize(),
206 'totalsize' => $this->totalsize,
1c597211 207 ));
254f2d05 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 }
37f03ea0 223 if ($response->type =='queued') {
f2e933bb 224 $this->exporter->set_forcequeue();
37f03ea0 225 }
14cb94eb 226 if (isset($response->querystring)) {
227 $this->continueurl = $response->querystring;
228 }
5d0dbf13
PL
229 // if we're not queuing the logging might have already happened
230 $this->exporter->update_log_url($this->get_static_continue_url());
254f2d05 231 }
232
5d0dbf13 233 public function get_static_continue_url() {
aed2937f 234 $remoteurl = '/artefact/file/';// @todo penny this might change later when we change formats.
14cb94eb 235 if (isset($this->continueurl)) {
236 $remoteurl .= $this->continueurl;
237 }
5d0dbf13
PL
238 return $remoteurl;
239 }
240
241 public function resolve_static_continue_url($remoteurl) {
242 static $sessions = array();
243 // if this is called mutliple times for the same host, stuff breaks
244 // so we have to keep track and just replace the wantsurl bit
245 // in case things go to different plugins or whatever
246 if (array_key_exists($this->get_config('mnethostid'), $sessions)) {
247 return preg_replace('/wantsurl=[^&]*&/', 'wantsurl=' . urlencode($remoteurl) . '&', $sessions[$this->get_config('mnethostid')]);
248 }
249 $this->ensure_mnethost();
250 $this->ensure_environment();
251 $mnetauth = get_auth_plugin('mnet');
aed2937f 252 if (!$url = $mnetauth->start_jump_session($this->get_config('mnethostid'), $remoteurl)) {
253 return false;
254 }
5d0dbf13 255 $sessions[$this->get_config('mnethostid')] = $url;
aed2937f 256 return $url;
254f2d05 257 }
258
5d0dbf13
PL
259 public function get_interactive_continue_url() {
260 return $this->resolve_static_continue_url($this->get_static_continue_url());
261 }
262
254f2d05 263 public function steal_control($stage) {
264 if ($stage != PORTFOLIO_STAGE_CONFIG) {
265 return false;
266 }
267 global $CFG;
edf1fc35 268 return $CFG->wwwroot . '/portfolio/mahara/preconfig.php?id=' . $this->exporter->get('id');
254f2d05 269 }
270
271 public function verify_file_request_params($params) {
272 return false;
273 // the data comes from an xmlrpc request,
274 // not a request to file.php
275 }
276
277 /**
278 * sends the 'content_intent' ping to mahara
279 * if all goes well, this will set the 'token' and 'sendtype' member variables.
280 */
281 public function send_intent() {
282 global $CFG, $DB;
283 require_once($CFG->dirroot . '/mnet/xmlrpc/client.php');
284 $client = new mnet_xmlrpc_client();
285 $client->set_method('portfolio/mahara/lib.php/send_content_intent');
286 $client->add_param($this->get('user')->username);
287 $this->ensure_mnethost();
288 if (!$client->send($this->mnethost)) {
289 foreach ($client->error as $errormessage) {
290 list($code, $message) = array_map('trim',explode(':', $errormessage, 2));
291 $message .= "ERROR $code:<br/>$errormessage<br/>";
292 }
293 throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara', '', $message);
294 }
295 // we should get back... the send type and a shared token
296 $response = (object)$client->response;
297 if (empty($response->sendtype) || empty($response->token)) {
298 throw new portfolio_export_exception($this->get('exporter'), 'senddisallowed', 'portfolio_mahara');
299 }
300 switch ($response->sendtype) {
301 case 'immediate':
302 $this->sendtype = PORTFOLIO_MAHARA_IMMEDIATE;
303 break;
304 case 'queue':
305 $this->sendtype = PORTFOLIO_MAHARA_QUEUE;
306 break;
307 case 'none':
308 default:
309 throw new portfolio_export_exception($this->get('exporter'), 'senddisallowed', 'portfolio_mahara');
310 }
311 $this->token = $response->token;
312 $this->get('exporter')->save();
313 // put the entry in the mahara queue table now too
314 $q = new stdClass;
315 $q->token = $this->token;
316 $q->transferid = $this->get('exporter')->get('id');
317 $DB->insert_record('portfolio_mahara_queue', $q);
318 }
319
320 private function ensure_mnethost() {
321 if (!empty($this->hostrecord) && !empty($this->mnethost)) {
322 return;
323 }
324 global $DB;
aae8cfdc 325 if (!$this->hostrecord = $DB->get_record('mnet_host', array('id' => $this->get_config('mnethostid')))) {
326 throw new portfolio_plugin_exception(PORTFOLIO_MAHARA_ERR_INVALIDHOST, 'portfolio_mahara');
327 }
254f2d05 328 $this->mnethost = new mnet_peer();
329 $this->mnethost->set_wwwroot($this->hostrecord->wwwroot);
330 }
331
254f2d05 332 /**
333 * xmlrpc (mnet) function to get the file.
334 * reads in the file and returns it base_64 encoded
335 * so that it can be enrypted by mnet.
336 *
337 * @param string $token the token recieved previously during send_content_intent
338 */
339 public static function fetch_file($token) {
340 global $DB, $MNET_REMOTE_CLIENT;;
341 try {
aed2937f 342 if (!$transferid = $DB->get_field('portfolio_mahara_queue', 'transferid', array('token' => $token))) {
939ea0bc 343 throw new mnet_server_exception(8009, get_string('mnet_notoken', 'portfolio_mahara'));
aed2937f 344 }
254f2d05 345 $exporter = portfolio_exporter::rewaken_object($transferid);
346 } catch (portfolio_exception $e) {
939ea0bc 347 throw new mnet_server_exception(8010, get_string('mnet_noid', 'portfolio_mahara'));
254f2d05 348 }
349 if ($exporter->get('instance')->get_config('mnethostid') != $MNET_REMOTE_CLIENT->id) {
939ea0bc 350 throw new mnet_server_exception(8011, get_string('mnet_wronghost', 'portfolio_mahara'));
254f2d05 351 }
352 global $CFG;
d5dfe1b3 353 try {
354 $i = $exporter->get('instance');
355 $f = $i->get('file');
432ad8bf 356 if (empty($f) || !($f instanceof stored_file)) {
939ea0bc 357 throw new mnet_server_exception(8012, get_string('mnet_nofile', 'portfolio_mahara'));
d5dfe1b3 358 }
432ad8bf 359 try {
360 $c = $f->get_content();
361 } catch (file_exception $e) {
939ea0bc 362 throw new mnet_server_exception(8013, get_string('mnet_nofilecontents', 'portfolio_mahara', $e->getMessage()));
432ad8bf 363 }
d5dfe1b3 364 $contents = base64_encode($c);
365 } catch (Exception $e) {
939ea0bc 366 throw new mnet_server_exception(8013, get_string('mnet_nofile', 'portfolio_mahara'));
d5dfe1b3 367 }
fae69685 368 $exporter->log_transfer();
254f2d05 369 $exporter->process_stage_cleanup(true);
370 return $contents;
371 }
372
373 public function cleanup() {
374 global $DB;
375 $DB->delete_records('portfolio_mahara_queue', array('transferid' => $this->get('exporter')->get('id'), 'token' => $this->token));
376 }
377
378
e862c50a 379 /**
380 * internal helper function, that converts between the format constant,
381 * which might be too specific (eg 'image') and the class in our *supported* list
382 * which might be higher up the format hierarchy tree (eg 'file')
383 */
254f2d05 384 private function resolve_format() {
24ba58ee 385 global $CFG;
254f2d05 386 $thisformat = $this->get_export_config('format');
387 $allformats = portfolio_supported_formats();
24ba58ee 388 require_once($CFG->libdir . '/portfolio/formats.php');
254f2d05 389 $thisobj = new $allformats[$thisformat];
390 foreach ($this->supported_formats() as $f) {
391 $class = $allformats[$f];
392 if ($thisobj instanceof $class) {
393 return $f;
394 }
395 }
396 }
254f2d05 397}
398
4317f92f 399