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