87fcac8d |
1 | <?php |
2 | /** |
3 | * Moodle - Modular Object-Oriented Dynamic Learning Environment |
4 | * http://moodle.org |
5 | * Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com |
6 | * |
7 | * This program is free software: you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation, either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | * |
12 | * This program is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * GNU General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | * |
20 | * @package moodle |
21 | * @subpackage portfolio |
22 | * @author Penny Leach <penny@catalyst.net.nz> |
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL |
24 | * @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com |
25 | * |
26 | * This file contains the class definition for the exporter object. |
27 | */ |
28 | |
29 | /** |
30 | * The class that handles the various stages of the actual export |
31 | * and the communication between the caller and the portfolio plugin. |
32 | * this is stored in the database between page requests in serialized base64 encoded form |
33 | * also contains helper methods for the plugin and caller to use (at the end of the file) |
34 | * {@see get_base_filearea} - where to write files to |
35 | * {@see write_new_file} - write some content to a file in the export filearea |
36 | * {@see copy_existing_file} - copy an existing file into the export filearea |
37 | * {@see get_tempfiles} - return list of all files in the export filearea |
38 | */ |
39 | class portfolio_exporter { |
40 | |
41 | /** |
42 | * the caller object used during the export |
43 | */ |
44 | private $caller; |
45 | |
46 | /** the portfolio plugin instanced used during the export |
47 | */ |
48 | private $instance; |
49 | |
50 | /** |
51 | * if there has been no config form displayed to the user |
52 | */ |
36b1cfd6 |
53 | private $noexportconfig; |
87fcac8d |
54 | |
55 | /** |
56 | * the navigation to display on the wizard screens |
57 | * built from build_navigation |
58 | */ |
59 | private $navigation; |
60 | |
61 | /** |
62 | * the user currently exporting content |
63 | * always $USER, but more conveniently placed here |
64 | */ |
65 | private $user; |
66 | |
67 | /** the file to include that contains the class defintion |
68 | * of the portfolio instance plugin |
69 | * used to re-waken the object after sleep |
70 | */ |
71 | public $instancefile; |
72 | |
73 | /** |
74 | * the file to include that contains the class definition |
75 | * of the caller object |
76 | * used to re-waken the object after sleep |
77 | */ |
78 | public $callerfile; |
79 | |
80 | /** |
81 | * the current stage of the export |
82 | */ |
83 | private $stage; |
84 | |
85 | /** |
86 | * whether something (usually the portfolio plugin) |
87 | * has forced queuing |
88 | */ |
89 | private $forcequeue; |
90 | |
91 | /** |
92 | * id of this export |
93 | * matches record in portfolio_tempdata table |
94 | * and used for itemid for file storage. |
95 | */ |
96 | private $id; |
97 | |
98 | /** |
99 | * the session key during the export |
100 | * used to avoid hijacking transfers |
101 | */ |
102 | private $sesskey; |
103 | |
104 | /** |
105 | * array of stages that have had the portfolio plugin already steal control from them |
106 | */ |
107 | private $alreadystolen; |
108 | |
2ddd044a |
109 | /** |
110 | * files that the exporter has written to this temp area |
111 | * keep track of this in case of duplicates within one export |
112 | * see MDL-16390 |
113 | */ |
114 | private $newfilehashes; |
115 | |
87fcac8d |
116 | /** |
117 | * construct a new exporter for use |
118 | * |
119 | * @param portfolio_plugin_base subclass $instance portfolio instance (passed by reference) |
120 | * @param portfolio_caller_base subclass $caller portfolio caller (passed by reference) |
121 | * @param string $callerfile path to callerfile (relative to dataroot) |
122 | * @param string $navigation result of build_navigation (passed to print_header) |
123 | */ |
124 | public function __construct(&$instance, &$caller, $callerfile, $navigation) { |
125 | $this->instance =& $instance; |
126 | $this->caller =& $caller; |
127 | if ($instance) { |
128 | $this->instancefile = 'portfolio/type/' . $instance->get('plugin') . '/lib.php'; |
129 | $this->instance->set('exporter', $this); |
130 | } |
131 | $this->callerfile = $callerfile; |
132 | $this->stage = PORTFOLIO_STAGE_CONFIG; |
133 | $this->navigation = $navigation; |
134 | $this->caller->set('exporter', $this); |
135 | $this->alreadystolen = array(); |
2ddd044a |
136 | $this->newfilehashes = array(); |
87fcac8d |
137 | } |
138 | |
139 | /* |
140 | * generic getter for properties belonging to this instance |
141 | * <b>outside</b> the subclasses |
142 | * like name, visible etc. |
143 | */ |
144 | public function get($field) { |
145 | if (property_exists($this, $field)) { |
146 | return $this->{$field}; |
147 | } |
148 | $a = (object)array('property' => $field, 'class' => get_class($this)); |
149 | throw new portfolio_export_exception($this, 'invalidproperty', 'portfolio', null, $a); |
150 | } |
151 | |
152 | /** |
153 | * generic setter for properties belonging to this instance |
154 | * <b>outside</b> the subclass |
155 | * like name, visible, etc. |
156 | */ |
157 | public function set($field, &$value) { |
158 | if (property_exists($this, $field)) { |
159 | $this->{$field} =& $value; |
160 | if ($field == 'instance') { |
161 | $this->instancefile = 'portfolio/type/' . $this->instance->get('plugin') . '/lib.php'; |
162 | $this->instance->set('exporter', $this); |
163 | } |
164 | $this->dirty = true; |
165 | return true; |
166 | } |
167 | $a = (object)array('property' => $field, 'class' => get_class($this)); |
168 | throw new portfolio_export_exception($this, 'invalidproperty', 'portfolio', null, $a); |
169 | |
170 | } |
171 | |
172 | /** |
173 | * process the given stage calling whatever functions are necessary |
174 | * |
175 | * @param int $stage (see PORTFOLIO_STAGE_* constants) |
176 | * @param boolean $alreadystolen used to avoid letting plugins steal control twice. |
177 | * |
178 | * @return boolean whether or not to process the next stage. this is important as the function is called recursively. |
179 | */ |
180 | public function process_stage($stage, $alreadystolen=false) { |
181 | $this->set('stage', $stage); |
182 | if ($alreadystolen) { |
183 | $this->alreadystolen[$stage] = true; |
184 | } else { |
185 | if (!array_key_exists($stage, $this->alreadystolen)) { |
186 | $this->alreadystolen[$stage] = false; |
187 | } |
188 | } |
87fcac8d |
189 | if (!$this->alreadystolen[$stage] && $url = $this->instance->steal_control($stage)) { |
2e0c7925 |
190 | $this->save(); |
87fcac8d |
191 | redirect($url); |
192 | break; |
2e0c7925 |
193 | } else { |
194 | $this->save(); |
87fcac8d |
195 | } |
196 | |
197 | $waiting = $this->instance->get_export_config('wait'); |
198 | if ($stage > PORTFOLIO_STAGE_QUEUEORWAIT && empty($waiting)) { |
199 | $stage = PORTFOLIO_STAGE_FINISHED; |
200 | } |
201 | $functionmap = array( |
202 | PORTFOLIO_STAGE_CONFIG => 'config', |
203 | PORTFOLIO_STAGE_CONFIRM => 'confirm', |
204 | PORTFOLIO_STAGE_QUEUEORWAIT => 'queueorwait', |
205 | PORTFOLIO_STAGE_PACKAGE => 'package', |
206 | PORTFOLIO_STAGE_CLEANUP => 'cleanup', |
207 | PORTFOLIO_STAGE_SEND => 'send', |
208 | PORTFOLIO_STAGE_FINISHED => 'finished' |
209 | ); |
210 | |
211 | $function = 'process_stage_' . $functionmap[$stage]; |
212 | try { |
213 | if ($this->$function()) { |
214 | // if we get through here it means control was returned |
215 | // as opposed to wanting to stop processing |
216 | // eg to wait for user input. |
217 | $this->save(); |
218 | $stage++; |
219 | return $this->process_stage($stage); |
220 | } else { |
221 | $this->save(); |
222 | return false; |
223 | } |
224 | } catch (portfolio_caller_exception $e) { |
225 | portfolio_export_rethrow_exception($this, $e); |
226 | } catch (portfolio_plugin_exception $e) { |
227 | portfolio_export_rethrow_exception($this, $e); |
228 | } catch (portfolio_export_exception $e) { |
229 | throw $e; |
230 | } catch (Exception $e) { |
231 | debugging(get_string('thirdpartyexception', 'portfolio', get_class($e))); |
232 | portfolio_export_rethrow_exception($this, $e); |
233 | } |
234 | } |
235 | |
236 | /** |
237 | * helper function to return the portfolio instance |
238 | * |
239 | * @return portfolio_plugin_base subclass |
240 | */ |
241 | public function instance() { |
242 | return $this->instance; |
243 | } |
244 | |
245 | /** |
246 | * helper function to return the caller object |
247 | * |
248 | * @return portfolio_caller_base subclass |
249 | */ |
250 | public function caller() { |
251 | return $this->caller; |
252 | } |
253 | |
254 | /** |
255 | * processes the 'config' stage of the export |
256 | * |
257 | * @return boolean whether or not to process the next stage. this is important as the control function is called recursively. |
258 | */ |
259 | public function process_stage_config() { |
260 | |
261 | $pluginobj = $callerobj = null; |
262 | if ($this->instance->has_export_config()) { |
263 | $pluginobj = $this->instance; |
264 | } |
265 | if ($this->caller->has_export_config()) { |
266 | $callerobj = $this->caller; |
267 | } |
268 | $formats = portfolio_supported_formats_intersect($this->caller->supported_formats($this->caller), $this->instance->supported_formats()); |
269 | $expectedtime = $this->instance->expected_time($this->caller->expected_time()); |
270 | if (count($formats) == 0) { |
271 | // something went wrong, we should not have gotten this far. |
272 | throw new portfolio_export_exception($this, 'nocommonformats', 'portfolio', null, get_class($this->caller)); |
273 | } |
274 | // even if neither plugin or caller wants any config, we have to let the user choose their format, and decide to wait. |
275 | if ($pluginobj || $callerobj || count($formats) > 1 || ($expectedtime != PORTFOLIO_TIME_LOW && $expectedtime != PORTFOLIO_TIME_FORCEQUEUE)) { |
276 | $customdata = array( |
277 | 'instance' => $this->instance, |
278 | 'plugin' => $pluginobj, |
279 | 'caller' => $callerobj, |
280 | 'userid' => $this->user->id, |
281 | 'formats' => $formats, |
282 | 'expectedtime' => $expectedtime, |
283 | ); |
284 | $mform = new portfolio_export_form('', $customdata); |
285 | if ($mform->is_cancelled()){ |
286 | $this->cancel_request(); |
287 | } else if ($fromform = $mform->get_data()){ |
288 | if (!confirm_sesskey()) { |
289 | throw new portfolio_export_exception($this, 'confirmsesskeybad'); |
290 | } |
291 | $pluginbits = array(); |
292 | $callerbits = array(); |
293 | foreach ($fromform as $key => $value) { |
294 | if (strpos($key, 'plugin_') === 0) { |
295 | $pluginbits[substr($key, 7)] = $value; |
296 | } else if (strpos($key, 'caller_') === 0) { |
297 | $callerbits[substr($key, 7)] = $value; |
298 | } |
299 | } |
300 | $callerbits['format'] = $pluginbits['format'] = $fromform->format; |
301 | $pluginbits['wait'] = $fromform->wait; |
302 | if ($expectedtime == PORTFOLIO_TIME_LOW) { |
303 | $pluginbits['wait'] = 1; |
304 | $pluginbits['hidewait'] = 1; |
305 | } else if ($expectedtime == PORTFOLIO_TIME_FORCEQUEUE) { |
306 | $pluginbits['wait'] = 0; |
307 | $pluginbits['hidewait'] = 1; |
308 | $this->forcequeue = true; |
309 | } |
310 | $callerbits['hideformat'] = $pluginbits['hideformat'] = (count($formats) == 1); |
311 | $this->caller->set_export_config($callerbits); |
312 | $this->instance->set_export_config($pluginbits); |
313 | return true; |
314 | } else { |
315 | $this->print_header('configexport'); |
316 | print_simple_box_start(); |
317 | $mform->display(); |
318 | print_simple_box_end(); |
319 | print_footer(); |
320 | return false;; |
321 | } |
322 | } else { |
323 | $this->noexportconfig = true; |
324 | $format = array_shift($formats); |
325 | $config = array( |
326 | 'hidewait' => 1, |
327 | 'wait' => (($expectedtime == PORTFOLIO_TIME_LOW) ? 1 : 0), |
328 | 'format' => $format, |
329 | 'hideformat' => 1 |
330 | ); |
331 | $this->instance->set_export_config($config); |
332 | $this->caller->set_export_config(array('format' => $format, 'hideformat' => 1)); |
333 | if ($expectedtime == PORTFOLIO_TIME_FORCEQUEUE) { |
334 | $this->forcequeue = true; |
335 | } |
336 | return true; |
337 | // do not break - fall through to confirm |
338 | } |
339 | } |
340 | |
341 | /** |
342 | * processes the 'confirm' stage of the export |
343 | * |
344 | * @return boolean whether or not to process the next stage. this is important as the control function is called recursively. |
345 | */ |
346 | public function process_stage_confirm() { |
347 | global $CFG, $DB; |
348 | |
349 | $previous = $DB->get_records( |
350 | 'portfolio_log', |
351 | array( |
352 | 'userid' => $this->user->id, |
353 | 'portfolio' => $this->instance->get('id'), |
354 | 'caller_sha1' => $this->caller->get_sha1(), |
355 | ) |
356 | ); |
357 | if (isset($this->noexportconfig) && empty($previous)) { |
358 | return true; |
359 | } |
360 | $strconfirm = get_string('confirmexport', 'portfolio'); |
361 | $yesurl = $CFG->wwwroot . '/portfolio/add.php?stage=' . PORTFOLIO_STAGE_QUEUEORWAIT; |
362 | $nourl = $CFG->wwwroot . '/portfolio/add.php?cancel=1'; |
363 | $this->print_header('confirmexport'); |
364 | print_simple_box_start(); |
365 | print_heading(get_string('confirmsummary', 'portfolio'), '', 4); |
366 | $mainsummary = array(); |
367 | if (!$this->instance->get_export_config('hideformat')) { |
368 | $mainsummary[get_string('selectedformat', 'portfolio')] = get_string('format_' . $this->instance->get_export_config('format'), 'portfolio'); |
369 | } |
370 | if (!$this->instance->get_export_config('hidewait')) { |
371 | $mainsummary[get_string('selectedwait', 'portfolio')] = get_string(($this->instance->get_export_config('wait') ? 'yes' : 'no')); |
372 | } |
373 | if ($previous) { |
374 | $previousstr = ''; |
375 | foreach ($previous as $row) { |
376 | $previousstr .= userdate($row->time); |
377 | if ($row->caller_class != get_class($this->caller)) { |
378 | require_once($CFG->dirroot . '/' . $row->caller_file); |
379 | $previousstr .= ' (' . call_user_func(array($row->caller_class, 'display_name')) . ')'; |
380 | } |
381 | $previousstr .= '<br />'; |
382 | } |
383 | $mainsummary[get_string('exportedpreviously', 'portfolio')] = $previousstr; |
384 | } |
385 | if (!$csummary = $this->caller->get_export_summary()) { |
386 | $csummary = array(); |
387 | } |
388 | if (!$isummary = $this->instance->get_export_summary()) { |
389 | $isummary = array(); |
390 | } |
391 | $mainsummary = array_merge($mainsummary, $csummary, $isummary); |
392 | $table = new StdClass; |
393 | $table->data = array(); |
394 | foreach ($mainsummary as $string => $value) { |
395 | $table->data[] = array($string, $value); |
396 | } |
397 | print_table($table); |
398 | notice_yesno($strconfirm, $yesurl, $nourl); |
399 | print_simple_box_end(); |
400 | print_footer(); |
401 | return false; |
402 | } |
403 | |
404 | /** |
405 | * processes the 'queueornext' stage of the export |
406 | * |
407 | * @return boolean whether or not to process the next stage. this is important as the control function is called recursively. |
408 | */ |
409 | public function process_stage_queueorwait() { |
410 | global $SESSION; |
411 | $wait = $this->instance->get_export_config('wait'); |
412 | if (empty($wait)) { |
413 | events_trigger('portfolio_send', $this->id); |
414 | unset($SESSION->portfolioexport); |
415 | return $this->process_stage_finished(true); |
416 | } |
417 | return true; |
418 | } |
419 | |
420 | /** |
421 | * processes the 'package' stage of the export |
422 | * |
423 | * @return boolean whether or not to process the next stage. this is important as the control function is called recursively. |
424 | */ |
425 | public function process_stage_package() { |
426 | // now we've agreed on a format, |
427 | // the caller is given control to package it up however it wants |
428 | // and then the portfolio plugin is given control to do whatever it wants. |
37f03ea0 |
429 | try { |
430 | $this->caller->prepare_package(); |
431 | } catch (portfolio_exception $e) { |
432 | throw new portfolio_export_exception($this, 'callercouldnotpackage', 'portfolio', null, $e->getMessage()); |
433 | } |
434 | catch (file_exception $e) { |
435 | throw new portfolio_export_exception($this, 'callercouldnotpackage', 'portfolio', null, $e->getMessage()); |
436 | } |
437 | try { |
438 | $this->instance->prepare_package(); |
87fcac8d |
439 | } |
37f03ea0 |
440 | catch (portfolio_exception $e) { |
441 | throw new portfolio_export_exception($this, 'plugincouldnotpackage', 'portfolio', null, $e->getMessage()); |
442 | } |
443 | catch (file_exception $e) { |
444 | throw new portfolio_export_exception($this, 'plugincouldnotpackage', 'portfolio', null, $e->getMessage()); |
87fcac8d |
445 | } |
446 | return true; |
447 | } |
448 | |
449 | /** |
450 | * processes the 'cleanup' stage of the export |
451 | * |
452 | * @return boolean whether or not to process the next stage. this is important as the control function is called recursively. |
453 | */ |
454 | public function process_stage_cleanup($pullok=false) { |
455 | global $CFG, $DB, $SESSION; |
456 | |
457 | if (!$pullok && $this->get('instance') && !$this->get('instance')->is_push()) { |
458 | unset($SESSION->portfolioexport); |
459 | return true; |
460 | } |
461 | if ($this->get('instance')) { |
462 | // might not be set - before export really starts |
463 | $this->get('instance')->cleanup(); |
464 | } |
465 | $DB->delete_records('portfolio_tempdata', array('id' => $this->id)); |
466 | $fs = get_file_storage(); |
467 | $fs->delete_area_files(SYSCONTEXTID, 'portfolio_exporter', $this->id); |
468 | unset($SESSION->portfolioexport); |
469 | return true; |
470 | } |
471 | |
472 | /** |
473 | * processes the 'send' stage of the export |
474 | * |
475 | * @return boolean whether or not to process the next stage. this is important as the control function is called recursively. |
476 | */ |
477 | public function process_stage_send() { |
478 | // send the file |
37f03ea0 |
479 | try { |
480 | $this->instance->send_package(); |
481 | } |
482 | catch (portfolio_plugin_exception $e) { |
483 | // not catching anything more general here. plugins with dependencies on other libraries that throw exceptions should catch and rethrow. |
484 | // eg curl exception |
485 | throw new portfolio_export_exception($this, 'failedtosendpackage', 'portfolio', null, $e->getMessage()); |
87fcac8d |
486 | } |
de01a507 |
487 | // only log push types, pull happens in send_file |
488 | if ($this->get('instance')->is_push()) { |
489 | $this->log_transfer(); |
490 | } |
491 | return true; |
492 | } |
493 | |
494 | /** |
495 | * log the transfer |
496 | * this should only be called after the file has been sent |
497 | * either via push, or sent from a pull request. |
498 | */ |
499 | public function log_transfer() { |
87fcac8d |
500 | global $DB; |
501 | $l = array( |
502 | 'userid' => $this->user->id, |
503 | 'portfolio' => $this->instance->get('id'), |
504 | 'caller_file' => $this->callerfile, |
505 | 'caller_sha1' => $this->caller->get_sha1(), |
506 | 'caller_class' => get_class($this->caller), |
507 | 'time' => time(), |
508 | ); |
509 | $DB->insert_record('portfolio_log', $l); |
87fcac8d |
510 | } |
511 | |
512 | /** |
513 | * processes the 'finish' stage of the export |
514 | * |
515 | * @return boolean whether or not to process the next stage. this is important as the control function is called recursively. |
516 | */ |
517 | public function process_stage_finished($queued=false) { |
518 | $returnurl = $this->caller->get_return_url(); |
519 | $continueurl = $this->instance->get_continue_url(); |
520 | $extras = $this->instance->get_extra_finish_options(); |
521 | |
522 | $key = 'exportcomplete'; |
37f03ea0 |
523 | if ($queued || $this->forcequeue) { |
87fcac8d |
524 | $key = 'exportqueued'; |
525 | if ($this->forcequeue) { |
526 | $key = 'exportqueuedforced'; |
527 | } |
528 | } |
529 | $this->print_header($key, false); |
530 | if ($returnurl) { |
531 | echo '<a href="' . $returnurl . '">' . get_string('returntowhereyouwere', 'portfolio') . '</a><br />'; |
532 | } |
533 | if ($continueurl) { |
534 | echo '<a href="' . $continueurl . '">' . get_string('continuetoportfolio', 'portfolio') . '</a><br />'; |
535 | } |
536 | if (is_array($extras)) { |
537 | foreach ($extras as $link => $string) { |
538 | echo '<a href="' . $link . '">' . $string . '</a><br />'; |
539 | } |
540 | } |
541 | print_footer(); |
542 | return false; |
543 | } |
544 | |
545 | |
546 | /** |
547 | * local print header function to be reused across the export |
548 | * |
549 | * @param string $titlestring key for a portfolio language string |
550 | * @param string $headerstring key for a portfolio language string |
551 | */ |
552 | public function print_header($headingstr, $summary=true) { |
553 | $titlestr = get_string('exporting', 'portfolio'); |
554 | $headerstr = get_string('exporting', 'portfolio'); |
555 | |
556 | print_header($titlestr, $headerstr, $this->navigation); |
557 | print_heading(get_string($headingstr, 'portfolio')); |
558 | |
559 | if (!$summary) { |
560 | return; |
561 | } |
562 | |
563 | print_simple_box_start(); |
564 | echo $this->caller->heading_summary(); |
565 | print_simple_box_end(); |
566 | } |
567 | |
568 | /** |
569 | * cancels a potfolio request and cleans up the tempdata |
570 | * and redirects the user back to where they started |
571 | */ |
572 | public function cancel_request() { |
573 | if (!isset($this)) { |
574 | return; |
575 | } |
576 | $this->process_stage_cleanup(true); |
577 | redirect($this->caller->get_return_url()); |
578 | exit; |
579 | } |
580 | |
581 | /** |
582 | * writes out the contents of this object and all its data to the portfolio_tempdata table and sets the 'id' field. |
583 | */ |
584 | public function save() { |
585 | global $DB; |
586 | if (empty($this->id)) { |
587 | $r = (object)array( |
588 | 'data' => base64_encode(serialize($this)), |
589 | 'expirytime' => time() + (60*60*24), |
590 | 'userid' => $this->user->id, |
591 | ); |
592 | $this->id = $DB->insert_record('portfolio_tempdata', $r); |
593 | $this->save(); // call again so that id gets added to the save data. |
594 | } else { |
595 | $DB->set_field('portfolio_tempdata', 'data', base64_encode(serialize($this)), array('id' => $this->id)); |
596 | } |
597 | } |
598 | |
599 | /** |
600 | * rewakens the data from the database given the id |
601 | * makes sure to load the required files with the class definitions |
602 | * |
603 | * @param int $id id of data |
604 | * |
605 | * @return portfolio_exporter |
606 | */ |
607 | public static function rewaken_object($id) { |
608 | global $DB, $CFG; |
609 | require_once($CFG->libdir . '/filelib.php'); |
610 | if (!$data = $DB->get_record('portfolio_tempdata', array('id' => $id))) { |
611 | throw new portfolio_exception('invalidtempid', 'portfolio'); |
612 | } |
613 | $exporter = unserialize(base64_decode($data->data)); |
614 | if ($exporter->instancefile) { |
615 | require_once($CFG->dirroot . '/' . $exporter->instancefile); |
616 | } |
617 | require_once($CFG->dirroot . '/' . $exporter->callerfile); |
618 | $exporter = unserialize(serialize($exporter)); |
619 | return $exporter; |
620 | } |
621 | |
622 | /** |
623 | * helper function to create the beginnings of a file_record object |
624 | * to create a new file in the portfolio_temporary working directory |
625 | * use {@see write_new_file} or {@see copy_existing_file} externally |
626 | * |
627 | * @param string $name filename of new record |
628 | */ |
629 | private function new_file_record_base($name) { |
630 | return (object)array_merge($this->get_base_filearea(), array( |
631 | 'filepath' => '/', |
632 | 'filename' => $name, |
633 | )); |
634 | } |
635 | |
636 | /** |
637 | * verifies a rewoken object |
638 | * |
639 | * checks to make sure it belongs to the same user and session as is currently in use. |
640 | * |
de01a507 |
641 | * @param boolean $readonly if we're reawakening this for a user to just display in the log view, don't verify the sessionkey |
642 | * when continuing transfers, you must pass false here. |
643 | * |
87fcac8d |
644 | * @throws portfolio_exception |
645 | */ |
de01a507 |
646 | public function verify_rewaken($readonly=false) { |
87fcac8d |
647 | global $USER; |
648 | if ($this->get('user')->id != $USER->id) { |
649 | throw new portfolio_exception('notyours', 'portfolio'); |
650 | } |
de01a507 |
651 | if (!$readonly && !confirm_sesskey($this->get('sesskey'))) { |
87fcac8d |
652 | throw new portfolio_exception('confirmsesskeybad'); |
653 | } |
654 | } |
655 | /** |
656 | * copies a file from somewhere else in moodle |
657 | * to the portfolio temporary working directory |
658 | * associated with this export |
659 | * |
660 | * @param $oldfile stored_file object |
661 | * @return new stored_file object |
662 | */ |
663 | public function copy_existing_file($oldfile) { |
2ddd044a |
664 | if (array_key_exists($oldfile->get_contenthash(), $this->newfilehashes)) { |
665 | return $this->newfilehashes[$oldfile->get_contenthash()]; |
666 | } |
87fcac8d |
667 | $fs = get_file_storage(); |
668 | $file_record = $this->new_file_record_base($oldfile->get_filename()); |
669 | try { |
2ddd044a |
670 | $newfile = $fs->create_file_from_storedfile($file_record, $oldfile->get_id()); |
671 | $this->newfilehashes[$newfile->get_contenthash()] = $newfile; |
672 | return $newfile; |
87fcac8d |
673 | } catch (file_exception $e) { |
674 | return false; |
675 | } |
676 | } |
677 | |
678 | /** |
679 | * writes out some content to a file in the |
680 | * portfolio temporary working directory |
681 | * associated with this export |
682 | * |
683 | * @param string $content content to write |
684 | * @param string $name filename to use |
685 | * @return new stored_file object |
686 | */ |
687 | public function write_new_file($content, $name) { |
688 | $fs = get_file_storage(); |
689 | $file_record = $this->new_file_record_base($name); |
690 | return $fs->create_file_from_string($file_record, $content); |
691 | } |
692 | |
de01a507 |
693 | /** |
694 | * zips all files in the temporary directory |
695 | * |
696 | * @param string $filename name of resulting zipfile (optional, defaults to portfolio-export.zip |
697 | * @param string $filepath subpath in the filearea (optional, defaults to final) |
698 | * |
699 | * @return stored_file resulting stored_file object |
700 | */ |
37f03ea0 |
701 | public function zip_tempfiles($filename='portfolio-export.zip', $filepath='/final/') { |
702 | $zipper = new zip_packer(); |
703 | |
704 | list ($contextid, $filearea, $itemid) = array_values($this->get_base_filearea()); |
705 | if ($newfile = $zipper->archive_to_storage($files, $contextid, $filearea, $itemid, $filepath, $filename, $this->user->id)) { |
706 | return $newfile; |
707 | } |
708 | return false; |
709 | |
710 | } |
711 | |
87fcac8d |
712 | /** |
713 | * returns an arary of files in the temporary working directory |
714 | * for this export |
715 | * always use this instead of the files api directly |
716 | * |
717 | * @return array of stored_file objects keyed by name |
718 | */ |
719 | public function get_tempfiles() { |
720 | $fs = get_file_storage(); |
721 | $files = $fs->get_area_files(SYSCONTEXTID, 'portfolio_exporter', $this->id, '', false); |
722 | if (empty($files)) { |
723 | return array(); |
724 | } |
725 | $returnfiles = array(); |
726 | foreach ($files as $f) { |
727 | $returnfiles[$f->get_filename()] = $f; |
728 | } |
729 | return $returnfiles; |
730 | } |
731 | |
732 | /** |
733 | * returns the context, filearea, and itemid |
734 | * parts of a filearea (not filepath) to be used by |
735 | * plugins if they want to do things like zip up the contents of |
736 | * the temp area to here, or something that can't be done just using |
737 | * write_new_file, copy_existing_file or get_tempfiles |
738 | * |
739 | * @return array contextid, filearea, itemid are the keys. |
740 | */ |
741 | public function get_base_filearea() { |
742 | return array( |
743 | 'contextid' => SYSCONTEXTID, |
744 | 'filearea' => 'portfolio_exporter', |
745 | 'itemid' => $this->id, |
746 | ); |
747 | } |
748 | |
749 | } |
750 | |
751 | ?> |