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