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