Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/portfolio/exporter.php
Go to the documentation of this file.
00001 <?php
00029 defined('MOODLE_INTERNAL') || die();
00030 
00041 class portfolio_exporter {
00042 
00046     private $caller;
00047 
00050     private $instance;
00051 
00055     private $noexportconfig;
00056 
00061     private $user;
00062 
00067     public $instancefile;
00068 
00074     public $callerfile;
00075 
00079     private $stage;
00080 
00085     private $forcequeue;
00086 
00092     private $id;
00093 
00097     private $alreadystolen;
00098 
00104     private $newfilehashes;
00105 
00110     private $format;
00111 
00115     private $queued = false;
00116 
00120     private $expirytime;
00121 
00126     private $deleted = false;
00127 
00135     public function __construct(&$instance, &$caller, $callerfile) {
00136         $this->instance =& $instance;
00137         $this->caller =& $caller;
00138         if ($instance) {
00139             $this->instancefile = 'portfolio/' . $instance->get('plugin') . '/lib.php';
00140             $this->instance->set('exporter', $this);
00141         }
00142         $this->callerfile = $callerfile;
00143         $this->stage = PORTFOLIO_STAGE_CONFIG;
00144         $this->caller->set('exporter', $this);
00145         $this->alreadystolen = array();
00146         $this->newfilehashes = array();
00147     }
00148 
00149     /*
00150     * generic getter for properties belonging to this instance
00151     * <b>outside</b> the subclasses
00152     * like name, visible etc.
00153     */
00154     public function get($field) {
00155         if ($field == 'format') {
00156             return portfolio_format_object($this->format);
00157         } else if ($field == 'formatclass') {
00158             return $this->format;
00159         }
00160         if (property_exists($this, $field)) {
00161             return $this->{$field};
00162         }
00163         $a = (object)array('property' => $field, 'class' => get_class($this));
00164         throw new portfolio_export_exception($this, 'invalidproperty', 'portfolio', null, $a);
00165     }
00166 
00172     public function set($field, &$value) {
00173         if (property_exists($this, $field)) {
00174             $this->{$field} =& $value;
00175             if ($field == 'instance') {
00176                 $this->instancefile = 'portfolio/' . $this->instance->get('plugin') . '/lib.php';
00177                 $this->instance->set('exporter', $this);
00178             }
00179             $this->dirty = true;
00180             return true;
00181         }
00182         $a = (object)array('property' => $field, 'class' => get_class($this));
00183         throw new portfolio_export_exception($this, 'invalidproperty', 'portfolio', null, $a);
00184 
00185     }
00186 
00193     public function set_forcequeue() {
00194         $this->forcequeue = true;
00195     }
00196 
00205     public function process_stage($stage, $alreadystolen=false) {
00206         $this->set('stage', $stage);
00207         if ($alreadystolen) {
00208             $this->alreadystolen[$stage] = true;
00209         } else {
00210             if (!array_key_exists($stage, $this->alreadystolen)) {
00211                 $this->alreadystolen[$stage] = false;
00212             }
00213         }
00214         if (!$this->alreadystolen[$stage] && $url = $this->instance->steal_control($stage)) {
00215             $this->save();
00216             redirect($url); // does not return
00217         } else {
00218             $this->save();
00219         }
00220 
00221         $waiting = $this->instance->get_export_config('wait');
00222         if ($stage > PORTFOLIO_STAGE_QUEUEORWAIT && empty($waiting)) {
00223             $stage = PORTFOLIO_STAGE_FINISHED;
00224         }
00225         $functionmap = array(
00226             PORTFOLIO_STAGE_CONFIG        => 'config',
00227             PORTFOLIO_STAGE_CONFIRM       => 'confirm',
00228             PORTFOLIO_STAGE_QUEUEORWAIT   => 'queueorwait',
00229             PORTFOLIO_STAGE_PACKAGE       => 'package',
00230             PORTFOLIO_STAGE_CLEANUP       => 'cleanup',
00231             PORTFOLIO_STAGE_SEND          => 'send',
00232             PORTFOLIO_STAGE_FINISHED      => 'finished'
00233         );
00234 
00235         $function = 'process_stage_' . $functionmap[$stage];
00236         try {
00237             if ($this->$function()) {
00238                 // if we get through here it means control was returned
00239                 // as opposed to wanting to stop processing
00240                 // eg to wait for user input.
00241                 $this->save();
00242                 $stage++;
00243                 return $this->process_stage($stage);
00244             } else {
00245                 $this->save();
00246                 return false;
00247             }
00248         } catch (portfolio_caller_exception $e) {
00249             portfolio_export_rethrow_exception($this, $e);
00250         } catch (portfolio_plugin_exception $e) {
00251             portfolio_export_rethrow_exception($this, $e);
00252         } catch (portfolio_export_exception $e) {
00253             throw $e;
00254         } catch (Exception $e) {
00255             debugging(get_string('thirdpartyexception', 'portfolio', get_class($e)));
00256             debugging($e);
00257             portfolio_export_rethrow_exception($this, $e);
00258         }
00259     }
00260 
00266     public function instance() {
00267         return $this->instance;
00268     }
00269 
00275     public function caller() {
00276         return $this->caller;
00277     }
00278 
00284     public function process_stage_config() {
00285         global $OUTPUT, $CFG;
00286         $pluginobj = $callerobj = null;
00287         if ($this->instance->has_export_config()) {
00288             $pluginobj = $this->instance;
00289         }
00290         if ($this->caller->has_export_config()) {
00291             $callerobj = $this->caller;
00292         }
00293         $formats = portfolio_supported_formats_intersect($this->caller->supported_formats(), $this->instance->supported_formats());
00294         $expectedtime = $this->instance->expected_time($this->caller->expected_time());
00295         if (count($formats) == 0) {
00296             // something went wrong, we should not have gotten this far.
00297             throw new portfolio_export_exception($this, 'nocommonformats', 'portfolio', null, array('location' => get_class($this->caller), 'formats' => implode(',', $formats)));
00298         }
00299         // even if neither plugin or caller wants any config, we have to let the user choose their format, and decide to wait.
00300         if ($pluginobj || $callerobj || count($formats) > 1 || ($expectedtime != PORTFOLIO_TIME_LOW && $expectedtime != PORTFOLIO_TIME_FORCEQUEUE)) {
00301             $customdata = array(
00302                 'instance' => $this->instance,
00303                 'id'       => $this->id,
00304                 'plugin' => $pluginobj,
00305                 'caller' => $callerobj,
00306                 'userid' => $this->user->id,
00307                 'formats' => $formats,
00308                 'expectedtime' => $expectedtime,
00309             );
00310             require_once($CFG->libdir . '/portfolio/forms.php');
00311             $mform = new portfolio_export_form('', $customdata);
00312             if ($mform->is_cancelled()){
00313                 $this->cancel_request();
00314             } else if ($fromform = $mform->get_data()){
00315                 if (!confirm_sesskey()) {
00316                     throw new portfolio_export_exception($this, 'confirmsesskeybad');
00317                 }
00318                 $pluginbits = array();
00319                 $callerbits = array();
00320                 foreach ($fromform as $key => $value) {
00321                     if (strpos($key, 'plugin_') === 0) {
00322                         $pluginbits[substr($key, 7)]  = $value;
00323                     } else if (strpos($key, 'caller_') === 0) {
00324                         $callerbits[substr($key, 7)] = $value;
00325                     }
00326                 }
00327                 $callerbits['format'] = $pluginbits['format'] = $fromform->format;
00328                 $pluginbits['wait'] = $fromform->wait;
00329                 if ($expectedtime == PORTFOLIO_TIME_LOW) {
00330                     $pluginbits['wait'] = 1;
00331                     $pluginbits['hidewait'] = 1;
00332                 } else if ($expectedtime == PORTFOLIO_TIME_FORCEQUEUE) {
00333                     $pluginbits['wait'] = 0;
00334                     $pluginbits['hidewait'] = 1;
00335                     $this->forcequeue = true;
00336                 }
00337                 $callerbits['hideformat'] = $pluginbits['hideformat'] = (count($formats) == 1);
00338                 $this->caller->set_export_config($callerbits);
00339                 $this->instance->set_export_config($pluginbits);
00340                 $this->set('format', $fromform->format);
00341                 return true;
00342             } else {
00343                 $this->print_header(get_string('configexport', 'portfolio'));
00344                 echo $OUTPUT->box_start();
00345                 $mform->display();
00346                 echo $OUTPUT->box_end();
00347                 echo $OUTPUT->footer();
00348                 return false;;
00349             }
00350         } else {
00351             $this->noexportconfig = true;
00352             $format = array_shift($formats);
00353             $config = array(
00354                 'hidewait' => 1,
00355                 'wait' => (($expectedtime == PORTFOLIO_TIME_LOW) ? 1 : 0),
00356                 'format' => $format,
00357                 'hideformat' => 1
00358             );
00359             $this->set('format', $format);
00360             $this->instance->set_export_config($config);
00361             $this->caller->set_export_config(array('format' => $format, 'hideformat' => 1));
00362             if ($expectedtime == PORTFOLIO_TIME_FORCEQUEUE) {
00363                 $this->forcequeue = true;
00364             }
00365             return true;
00366             // do not break - fall through to confirm
00367         }
00368     }
00369 
00375     public function process_stage_confirm() {
00376         global $CFG, $DB, $OUTPUT;
00377 
00378         $previous = $DB->get_records(
00379             'portfolio_log',
00380             array(
00381                 'userid'      => $this->user->id,
00382                 'portfolio'   => $this->instance->get('id'),
00383                 'caller_sha1' => $this->caller->get_sha1(),
00384             )
00385         );
00386         if (isset($this->noexportconfig) && empty($previous)) {
00387             return true;
00388         }
00389         $strconfirm = get_string('confirmexport', 'portfolio');
00390         $baseurl = $CFG->wwwroot . '/portfolio/add.php?sesskey=' . sesskey() . '&id=' . $this->get('id');
00391         $yesurl = $baseurl . '&stage=' . PORTFOLIO_STAGE_QUEUEORWAIT;
00392         $nourl  = $baseurl . '&cancel=1';
00393         $this->print_header(get_string('confirmexport', 'portfolio'));
00394         echo $OUTPUT->box_start();
00395         echo $OUTPUT->heading(get_string('confirmsummary', 'portfolio'), 3);
00396         $mainsummary = array();
00397         if (!$this->instance->get_export_config('hideformat')) {
00398             $mainsummary[get_string('selectedformat', 'portfolio')] = get_string('format_' . $this->instance->get_export_config('format'), 'portfolio');
00399         }
00400         if (!$this->instance->get_export_config('hidewait')) {
00401             $mainsummary[get_string('selectedwait', 'portfolio')] = get_string(($this->instance->get_export_config('wait') ? 'yes' : 'no'));
00402         }
00403         if ($previous) {
00404             $previousstr = '';
00405             foreach ($previous as $row) {
00406                 $previousstr .= userdate($row->time);
00407                 if ($row->caller_class != get_class($this->caller)) {
00408                     require_once($CFG->dirroot . '/' . $row->caller_file);
00409                     $previousstr .= ' (' . call_user_func(array($row->caller_class, 'display_name')) . ')';
00410                 }
00411                 $previousstr .= '<br />';
00412             }
00413             $mainsummary[get_string('exportedpreviously', 'portfolio')] = $previousstr;
00414         }
00415         if (!$csummary = $this->caller->get_export_summary()) {
00416             $csummary = array();
00417         }
00418         if (!$isummary = $this->instance->get_export_summary()) {
00419             $isummary = array();
00420         }
00421         $mainsummary = array_merge($mainsummary, $csummary, $isummary);
00422         $table = new html_table();
00423         $table->attributes['class'] = 'generaltable exportsummary';
00424         $table->data = array();
00425         foreach ($mainsummary as $string => $value) {
00426             $table->data[] = array($string, $value);
00427         }
00428         echo html_writer::table($table);
00429         echo $OUTPUT->confirm($strconfirm, $yesurl, $nourl);
00430         echo $OUTPUT->box_end();
00431         echo $OUTPUT->footer();
00432         return false;
00433     }
00434 
00440     public function process_stage_queueorwait() {
00441         $wait = $this->instance->get_export_config('wait');
00442         if (empty($wait)) {
00443             events_trigger('portfolio_send', $this->id);
00444             $this->queued = true;
00445             return $this->process_stage_finished(true);
00446         }
00447         return true;
00448     }
00449 
00455     public function process_stage_package() {
00456         // now we've agreed on a format,
00457         // the caller is given control to package it up however it wants
00458         // and then the portfolio plugin is given control to do whatever it wants.
00459         try {
00460             $this->caller->prepare_package();
00461         } catch (portfolio_exception $e) {
00462             throw new portfolio_export_exception($this, 'callercouldnotpackage', 'portfolio', null, $e->getMessage());
00463         }
00464         catch (file_exception $e) {
00465             throw new portfolio_export_exception($this, 'callercouldnotpackage', 'portfolio', null, $e->getMessage());
00466         }
00467         try {
00468             $this->instance->prepare_package();
00469         }
00470         catch (portfolio_exception $e) {
00471             throw new portfolio_export_exception($this, 'plugincouldnotpackage', 'portfolio', null, $e->getMessage());
00472         }
00473         catch (file_exception $e) {
00474             throw new portfolio_export_exception($this, 'plugincouldnotpackage', 'portfolio', null, $e->getMessage());
00475         }
00476         return true;
00477     }
00478 
00486     public function process_stage_cleanup($pullok=false) {
00487         global $CFG, $DB;
00488 
00489         if (!$pullok && $this->get('instance') && !$this->get('instance')->is_push()) {
00490             return true;
00491         }
00492         if ($this->get('instance')) {
00493             // might not be set - before export really starts
00494             $this->get('instance')->cleanup();
00495         }
00496         $DB->delete_records('portfolio_tempdata', array('id' => $this->id));
00497         $fs = get_file_storage();
00498         $fs->delete_area_files(SYSCONTEXTID, 'portfolio', 'exporter', $this->id);
00499         $this->deleted = true;
00500         return true;
00501     }
00502 
00508     public function process_stage_send() {
00509         // send the file
00510         try {
00511             $this->instance->send_package();
00512         }
00513         catch (portfolio_plugin_exception $e) {
00514             // not catching anything more general here. plugins with dependencies on other libraries that throw exceptions should catch and rethrow.
00515             // eg curl exception
00516             throw new portfolio_export_exception($this, 'failedtosendpackage', 'portfolio', null, $e->getMessage());
00517         }
00518         // only log push types, pull happens in send_file
00519         if ($this->get('instance')->is_push()) {
00520             $this->log_transfer();
00521         }
00522         return true;
00523     }
00524 
00530     public function log_transfer() {
00531         global $DB;
00532         $l = array(
00533             'userid'         => $this->user->id,
00534             'portfolio'      => $this->instance->get('id'),
00535             'caller_file'    => $this->callerfile,
00536             'caller_sha1'    => $this->caller->get_sha1(),
00537             'caller_class'   => get_class($this->caller),
00538             'continueurl'    => $this->instance->get_static_continue_url(),
00539             'returnurl'      => $this->caller->get_return_url(),
00540             'tempdataid'     => $this->id,
00541             'time'           => time(),
00542         );
00543         $DB->insert_record('portfolio_log', $l);
00544     }
00545 
00550     public function update_log_url($url) {
00551         global $DB;
00552         $DB->set_field('portfolio_log', 'continueurl', $url, array('tempdataid' => $this->id));
00553     }
00554 
00560     public function process_stage_finished($queued=false) {
00561         global $OUTPUT;
00562         $returnurl = $this->caller->get_return_url();
00563         $continueurl = $this->instance->get_interactive_continue_url();
00564         $extras = $this->instance->get_extra_finish_options();
00565 
00566         $key = 'exportcomplete';
00567         if ($queued || $this->forcequeue) {
00568             $key = 'exportqueued';
00569             if ($this->forcequeue) {
00570                 $key = 'exportqueuedforced';
00571             }
00572         }
00573         $this->print_header(get_string($key, 'portfolio'), false);
00574         self::print_finish_info($returnurl, $continueurl, $extras);
00575         echo $OUTPUT->footer();
00576         return false;
00577     }
00578 
00579 
00585     public function print_header($headingstr, $summary=true) {
00586         global $OUTPUT, $PAGE;
00587         $titlestr = get_string('exporting', 'portfolio');
00588         $headerstr = get_string('exporting', 'portfolio');
00589 
00590         $PAGE->set_title($titlestr);
00591         $PAGE->set_heading($headerstr);
00592         echo $OUTPUT->header();
00593         echo $OUTPUT->heading($headingstr);
00594 
00595         if (!$summary) {
00596             return;
00597         }
00598 
00599         echo $OUTPUT->box_start();
00600         echo $OUTPUT->box_start();
00601         echo $this->caller->heading_summary();
00602         echo $OUTPUT->box_end();
00603         if ($this->instance) {
00604             echo $OUTPUT->box_start();
00605             echo $this->instance->heading_summary();
00606             echo $OUTPUT->box_end();
00607         }
00608         echo $OUTPUT->box_end();
00609     }
00610 
00615     public function cancel_request($logreturn=false) {
00616         global $CFG;
00617         if (!isset($this)) {
00618             return;
00619         }
00620         $this->process_stage_cleanup(true);
00621         if ($logreturn) {
00622             redirect($CFG->wwwroot . '/user/portfoliologs.php');
00623         }
00624         redirect($this->caller->get_return_url());
00625         exit;
00626     }
00627 
00631     public function save() {
00632         global $DB;
00633         if (empty($this->id)) {
00634             $r = (object)array(
00635                 'data' => base64_encode(serialize($this)),
00636                 'expirytime' => time() + (60*60*24),
00637                 'userid' => $this->user->id,
00638                 'instance' => (empty($this->instance)) ? null : $this->instance->get('id'),
00639             );
00640             $this->id = $DB->insert_record('portfolio_tempdata', $r);
00641             $this->expirytime = $r->expirytime;
00642             $this->save(); // call again so that id gets added to the save data.
00643         } else {
00644             if (!$r = $DB->get_record('portfolio_tempdata', array('id' => $this->id))) {
00645                 if (!$this->deleted) {
00646                     //debugging("tried to save current object, but failed - see MDL-20872");
00647                 }
00648                 return;
00649             }
00650             $r->data = base64_encode(serialize($this));
00651             $r->instance = (empty($this->instance)) ? null : $this->instance->get('id');
00652             $DB->update_record('portfolio_tempdata', $r);
00653         }
00654     }
00655 
00664     public static function rewaken_object($id) {
00665         global $DB, $CFG;
00666         require_once($CFG->libdir . '/filelib.php');
00667         require_once($CFG->libdir . '/portfolio/exporter.php');
00668         require_once($CFG->libdir . '/portfolio/caller.php');
00669         require_once($CFG->libdir . '/portfolio/plugin.php');
00670         if (!$data = $DB->get_record('portfolio_tempdata', array('id' => $id))) {
00671             // maybe it's been finished already by a pull plugin
00672             // so look in the logs
00673             if ($log = $DB->get_record('portfolio_log', array('tempdataid' => $id))) {
00674                 self::print_cleaned_export($log);
00675             }
00676             throw new portfolio_exception('invalidtempid', 'portfolio');
00677         }
00678         $exporter = unserialize(base64_decode($data->data));
00679         if ($exporter->instancefile) {
00680             require_once($CFG->dirroot . '/' . $exporter->instancefile);
00681         }
00682         require_once($CFG->dirroot . '/' . $exporter->callerfile);
00683         $exporter = unserialize(serialize($exporter));
00684         if (!$exporter->get('id')) {
00685             // workaround for weird case
00686             // where the id doesn't get saved between a new insert
00687             // and the subsequent call that sets this field in the serialised data
00688             $exporter->set('id', $id);
00689             $exporter->save();
00690         }
00691         return $exporter;
00692     }
00693 
00701     private function new_file_record_base($name) {
00702         return (object)array_merge($this->get_base_filearea(), array(
00703             'filepath' => '/',
00704             'filename' => $name,
00705         ));
00706     }
00707 
00718     public function verify_rewaken($readonly=false) {
00719         global $USER, $CFG;
00720         if ($this->get('user')->id != $USER->id) { // make sure it belongs to the right user
00721             throw new portfolio_exception('notyours', 'portfolio');
00722         }
00723         if (!$readonly && $this->get('instance') && !$this->get('instance')->allows_multiple_exports()
00724             && ($already = portfolio_existing_exports($this->get('user')->id, $this->get('instance')->get('plugin')))
00725             && array_shift(array_keys($already)) != $this->get('id')
00726         ) {
00727             $a = (object)array(
00728                 'plugin'  => $this->get('instance')->get('plugin'),
00729                 'link'    => $CFG->wwwroot . '/user/portfoliologs.php',
00730             );
00731             throw new portfolio_exception('nomultipleexports', 'portfolio', '', $a);
00732         }
00733         if (!$this->caller->check_permissions()) { // recall the caller permission check
00734             throw new portfolio_caller_exception('nopermissions', 'portfolio', $this->caller->get_return_url());
00735         }
00736     }
00745     public function copy_existing_file($oldfile) {
00746         if (array_key_exists($oldfile->get_contenthash(), $this->newfilehashes)) {
00747             return $this->newfilehashes[$oldfile->get_contenthash()];
00748         }
00749         $fs = get_file_storage();
00750         $file_record = $this->new_file_record_base($oldfile->get_filename());
00751         if ($dir = $this->get('format')->get_file_directory()) {
00752             $file_record->filepath = '/'. $dir . '/';
00753         }
00754         try {
00755             $newfile = $fs->create_file_from_storedfile($file_record, $oldfile->get_id());
00756             $this->newfilehashes[$newfile->get_contenthash()] = $newfile;
00757             return $newfile;
00758         } catch (file_exception $e) {
00759             return false;
00760         }
00761     }
00762 
00773     public function write_new_file($content, $name, $manifest=true) {
00774         $fs = get_file_storage();
00775         $file_record = $this->new_file_record_base($name);
00776         if (empty($manifest) && ($dir = $this->get('format')->get_file_directory())) {
00777             $file_record->filepath = '/' . $dir . '/';
00778         }
00779         return $fs->create_file_from_string($file_record, $content);
00780     }
00781 
00790     public function zip_tempfiles($filename='portfolio-export.zip', $filepath='/final/') {
00791         $zipper = new zip_packer();
00792 
00793         list ($contextid, $component, $filearea, $itemid) = array_values($this->get_base_filearea());
00794         if ($newfile = $zipper->archive_to_storage($this->get_tempfiles(), $contextid, $component, $filearea, $itemid, $filepath, $filename, $this->user->id)) {
00795             return $newfile;
00796         }
00797         return false;
00798 
00799     }
00800 
00808     public function get_tempfiles($skipfile='portfolio-export.zip') {
00809         $fs = get_file_storage();
00810         $files = $fs->get_area_files(SYSCONTEXTID, 'portfolio', 'exporter', $this->id, '', false);
00811         if (empty($files)) {
00812             return array();
00813         }
00814         $returnfiles = array();
00815         foreach ($files as $f) {
00816             if ($f->get_filename() == $skipfile) {
00817                 continue;
00818             }
00819             $returnfiles[$f->get_filepath() . '/' . $f->get_filename()] = $f;
00820         }
00821         return $returnfiles;
00822     }
00823 
00833     public function get_base_filearea() {
00834         return array(
00835             'contextid' => SYSCONTEXTID,
00836             'component' => 'portfolio',
00837             'filearea'  => 'exporter',
00838             'itemid'    => $this->id,
00839         );
00840     }
00841 
00847     public static function print_expired_export() {
00848         global $CFG, $OUTPUT, $PAGE;
00849         $title = get_string('exportexpired', 'portfolio');
00850         $PAGE->navbar->add(get_string('exportexpired', 'portfolio'));
00851         $PAGE->set_title($title);
00852         $PAGE->set_heading($title);
00853         echo $OUTPUT->header();
00854         echo $OUTPUT->notification(get_string('exportexpireddesc', 'portfolio'));
00855         echo $OUTPUT->continue_button($CFG->wwwroot);
00856         echo $OUTPUT->footer();
00857         exit;
00858     }
00859 
00860     public static function print_cleaned_export($log, $instance=null) {
00861         global $CFG, $OUTPUT, $PAGE;
00862         if (empty($instance) || !$instance instanceof portfolio_plugin_base) {
00863             $instance = portfolio_instance($log->portfolio);
00864         }
00865         $title = get_string('exportalreadyfinished', 'portfolio');
00866         $PAGE->navbar->add($title);
00867         $PAGE->set_title($title);
00868         $PAGE->set_heading($title);
00869         echo $OUTPUT->header();
00870         echo $OUTPUT->notification(get_string('exportalreadyfinished', 'portfolio'));
00871         self::print_finish_info($log->returnurl, $instance->resolve_static_continue_url($log->continueurl));
00872         echo $OUTPUT->continue_button($CFG->wwwroot);
00873         echo $OUTPUT->footer();
00874         exit;
00875     }
00876 
00877     public static function print_finish_info($returnurl, $continueurl, $extras=null) {
00878         if ($returnurl) {
00879             echo '<a href="' . $returnurl . '">' . get_string('returntowhereyouwere', 'portfolio') . '</a><br />';
00880         }
00881         if ($continueurl) {
00882             echo '<a href="' . $continueurl . '">' . get_string('continuetoportfolio', 'portfolio') . '</a><br />';
00883         }
00884         if (is_array($extras)) {
00885             foreach ($extras as $link => $string) {
00886                 echo '<a href="' . $link . '">' . $string . '</a><br />';
00887             }
00888         }
00889     }
00890 }
 All Data Structures Namespaces Files Functions Variables Enumerations