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