254f2d05 |
1 | <?php |
2 | |
3 | define('PORTFOLIO_MAHARA_ERR_NETWORKING_OFF', 'err_networkingoff'); |
4 | define('PORTFOLIO_MAHARA_ERR_NOHOSTS', 'err_nomnethosts'); |
5 | |
6 | require_once($CFG->dirroot . '/lib/portfoliolib.php'); |
7 | require_once($CFG->dirroot . '/mnet/lib.php'); |
8 | |
9 | define('PORTFOLIO_MAHARA_QUEUE', PORTFOLIO_TIME_HIGH); |
10 | define('PORTFOLIO_MAHARA_IMMEDIATE', PORTFOLIO_TIME_MODERATE); |
11 | |
12 | class 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()); |
aed2937f |
157 | $client->add_param(array('filesmanifest' => $this->filesmanifest)); |
254f2d05 |
158 | $client->add_param($this->get_export_config('wait')); |
159 | $this->ensure_mnethost(); |
160 | if (!$client->send($this->mnethost)) { |
161 | foreach ($client->error as $errormessage) { |
162 | list($code, $message) = array_map('trim',explode(':', $errormessage, 2)); |
163 | $message .= "ERROR $code:<br/>$errormessage<br/>"; |
164 | } |
165 | throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara', '', $message); |
166 | } |
167 | // we should get back... an ok and a status |
168 | // either we've been waiting a while and mahara has fetched the file or has queued it. |
169 | $response = (object)$client->response; |
170 | if (!$response->status) { |
171 | throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara'); |
172 | } |
173 | return true; |
174 | } |
175 | |
176 | public function get_continue_url() { |
177 | $this->ensure_mnethost(); |
aed2937f |
178 | $this->ensure_environment(); |
179 | $mnetauth = get_auth_plugin('mnet'); |
180 | $remoteurl = '/artefact/file/';// @todo penny this might change later when we change formats. |
181 | if (!$url = $mnetauth->start_jump_session($this->get_config('mnethostid'), $remoteurl)) { |
182 | return false; |
183 | } |
184 | return $url; |
254f2d05 |
185 | } |
186 | |
187 | public function steal_control($stage) { |
188 | if ($stage != PORTFOLIO_STAGE_CONFIG) { |
189 | return false; |
190 | } |
191 | global $CFG; |
192 | return $CFG->wwwroot . '/portfolio/type/mahara/preconfig.php?id=' . $this->exporter->get('id'); |
193 | } |
194 | |
195 | public function verify_file_request_params($params) { |
196 | return false; |
197 | // the data comes from an xmlrpc request, |
198 | // not a request to file.php |
199 | } |
200 | |
201 | /** |
202 | * sends the 'content_intent' ping to mahara |
203 | * if all goes well, this will set the 'token' and 'sendtype' member variables. |
204 | */ |
205 | public function send_intent() { |
206 | global $CFG, $DB; |
207 | require_once($CFG->dirroot . '/mnet/xmlrpc/client.php'); |
208 | $client = new mnet_xmlrpc_client(); |
209 | $client->set_method('portfolio/mahara/lib.php/send_content_intent'); |
210 | $client->add_param($this->get('user')->username); |
211 | $this->ensure_mnethost(); |
212 | if (!$client->send($this->mnethost)) { |
213 | foreach ($client->error as $errormessage) { |
214 | list($code, $message) = array_map('trim',explode(':', $errormessage, 2)); |
215 | $message .= "ERROR $code:<br/>$errormessage<br/>"; |
216 | } |
217 | throw new portfolio_export_exception($this->get('exporter'), 'failedtoping', 'portfolio_mahara', '', $message); |
218 | } |
219 | // we should get back... the send type and a shared token |
220 | $response = (object)$client->response; |
221 | if (empty($response->sendtype) || empty($response->token)) { |
222 | throw new portfolio_export_exception($this->get('exporter'), 'senddisallowed', 'portfolio_mahara'); |
223 | } |
224 | switch ($response->sendtype) { |
225 | case 'immediate': |
226 | $this->sendtype = PORTFOLIO_MAHARA_IMMEDIATE; |
227 | break; |
228 | case 'queue': |
229 | $this->sendtype = PORTFOLIO_MAHARA_QUEUE; |
230 | break; |
231 | case 'none': |
232 | default: |
233 | throw new portfolio_export_exception($this->get('exporter'), 'senddisallowed', 'portfolio_mahara'); |
234 | } |
235 | $this->token = $response->token; |
236 | $this->get('exporter')->save(); |
237 | // put the entry in the mahara queue table now too |
238 | $q = new stdClass; |
239 | $q->token = $this->token; |
240 | $q->transferid = $this->get('exporter')->get('id'); |
241 | $DB->insert_record('portfolio_mahara_queue', $q); |
242 | } |
243 | |
244 | private function ensure_mnethost() { |
245 | if (!empty($this->hostrecord) && !empty($this->mnethost)) { |
246 | return; |
247 | } |
248 | global $DB; |
249 | $this->hostrecord = $DB->get_record('mnet_host', array('id' => $this->get_config('mnethostid'))); |
250 | $this->mnethost = new mnet_peer(); |
251 | $this->mnethost->set_wwwroot($this->hostrecord->wwwroot); |
252 | } |
253 | |
254 | public static function mnet_publishes() { |
255 | $pf= array(); |
256 | $pf['name'] = 'pf'; // Name & Description go in lang file |
257 | $pf['apiversion'] = 1; |
258 | $pf['methods'] = array('send_content_intent', 'send_content_ready', 'fetch_file'); |
259 | |
260 | return array($pf); |
261 | } |
262 | |
263 | /** |
264 | * xmlrpc (mnet) function to get the file. |
265 | * reads in the file and returns it base_64 encoded |
266 | * so that it can be enrypted by mnet. |
267 | * |
268 | * @param string $token the token recieved previously during send_content_intent |
269 | */ |
270 | public static function fetch_file($token) { |
271 | global $DB, $MNET_REMOTE_CLIENT;; |
272 | try { |
aed2937f |
273 | if (!$transferid = $DB->get_field('portfolio_mahara_queue', 'transferid', array('token' => $token))) { |
274 | exit(mnet_server_fault(8009, 'could not find token')); |
275 | } |
254f2d05 |
276 | $exporter = portfolio_exporter::rewaken_object($transferid); |
277 | } catch (portfolio_exception $e) { |
d5dfe1b3 |
278 | exit(mnet_server_fault(8010, 'invalid transfer id')); |
254f2d05 |
279 | } |
280 | if ($exporter->get('instance')->get_config('mnethostid') != $MNET_REMOTE_CLIENT->id) { |
d5dfe1b3 |
281 | exit(mnet_server_fault(8011, "remote host didn't match saved host")); |
254f2d05 |
282 | } |
283 | global $CFG; |
d5dfe1b3 |
284 | try { |
285 | $i = $exporter->get('instance'); |
286 | $f = $i->get('file'); |
287 | if (empty($f)) { |
288 | exit(mnet_server_fault(8012, 'could not find file in transfer object - weird error')); |
289 | } |
290 | $c = $f->get_content(); |
291 | $contents = base64_encode($c); |
292 | } catch (Exception $e) { |
293 | exit(mnet_server_fault(8013, 'could not get file to send')); |
294 | } |
254f2d05 |
295 | $exporter->process_stage_cleanup(true); |
296 | return $contents; |
297 | } |
298 | |
299 | public function cleanup() { |
300 | global $DB; |
301 | $DB->delete_records('portfolio_mahara_queue', array('transferid' => $this->get('exporter')->get('id'), 'token' => $this->token)); |
302 | } |
303 | |
304 | |
305 | private function resolve_format() { |
306 | $thisformat = $this->get_export_config('format'); |
307 | $allformats = portfolio_supported_formats(); |
308 | $thisobj = new $allformats[$thisformat]; |
309 | foreach ($this->supported_formats() as $f) { |
310 | $class = $allformats[$f]; |
311 | if ($thisobj instanceof $class) { |
312 | return $f; |
313 | } |
314 | } |
315 | } |
254f2d05 |
316 | } |
317 | |
318 | ?> |