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