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