From d67bfc32a1641f29aade4156db3921b2ec8dfab7 Mon Sep 17 00:00:00 2001 From: mjollnir_ Date: Sat, 9 Aug 2008 14:24:58 +0000 Subject: [PATCH] MDL-15777 - updated most of the portfolio code to use files api. Not done: - forum (the rest of the module isn't using files api yet) - database mod (touches other parts of the code (ods and excel libs) - portfolio download plugin (needs discussion with petr about userfiles) all of these have been disabled in the meantime. --- lib/portfoliolib.php | 148 ++++++++++++------ mod/assignment/lib.php | 37 ++--- .../type/online/assignment.class.php | 7 +- mod/data/export_form.php | 15 +- mod/data/lib.php | 2 +- mod/forum/lib.php | 11 +- mod/glossary/lib.php | 19 +-- mod/resource/lib.php | 15 +- mod/resource/type/html/resource.class.php | 4 +- mod/resource/type/text/resource.class.php | 4 +- portfolio/add.php | 22 +-- portfolio/type/boxnet/lib.php | 14 +- portfolio/type/download/lib.php | 6 +- 13 files changed, 178 insertions(+), 126 deletions(-) diff --git a/lib/portfoliolib.php b/lib/portfoliolib.php index 95390f366c..f21e9968c7 100644 --- a/lib/portfoliolib.php +++ b/lib/portfoliolib.php @@ -502,26 +502,6 @@ function portfolio_report_insane($insane, $instances=false, $return=false) { echo $output; } -/** -* temporary functions until the File API settles -* to do with moving files around -*/ -function temp_portfolio_working_directory($unique) { - return make_upload_directory('temp/portfolio/export/' . $unique); -} - -function temp_portfolio_usertemp_directory($userid) { - return make_upload_directory('userdata/' . $userid . '/temp'); -} - -/** -*cleans up the working directory -*/ -function temp_portfolio_cleanup($unique) { - $workdir = temp_portfolio_working_directory($unique); - return remove_dir($workdir); -} - /** * fake the url to portfolio/add.php from data from somewhere else * you should use portfolio_add_button instead 99% of the time @@ -572,6 +552,10 @@ abstract class portfolio_caller_base { */ protected $user; + /** + * a reference to the exporter object + */ + protected $exporter; /** * @@ -659,9 +643,9 @@ abstract class portfolio_caller_base { * * @todo determine what to return in the error case */ - public final function set($field, $value) { + public final function set($field, &$value) { if (property_exists($this, $field)) { - $this->{$field} = $value; + $this->{$field} =& $value; $this->dirty = true; return true; } @@ -745,11 +729,10 @@ abstract class portfolio_caller_base { /** * called before the portfolio plugin gets control * this function should copy all the files it wants to - * the temporary directory. - * - * @param string $tempdir path to tempdir to put files in + * the temporary directory, using {@see copy_existing_file} + * or {@see write_new_file} */ - public abstract function prepare_package($tempdir); + public abstract function prepare_package(); /** * array of formats this caller supports @@ -875,6 +858,10 @@ abstract class portfolio_plugin_base { */ protected $user; + /** + * a reference to the exporter object + */ + protected $exporter; /** * array of formats this portfolio supports @@ -1039,13 +1026,14 @@ abstract class portfolio_plugin_base { } /** - * called before the portfolio plugin gets control - * this function should copy all the files it wants to - * the temporary directory. + * called after the caller has finished having control + * of its prepare_package function. + * this function should read all the files from the portfolio + * working file area and zip them and send them or whatever it wants. + * {@see get_tempfiles} to get the list of files. * - * @param string $tempdir path to temporary directory */ - public abstract function prepare_package($tempdir); + public abstract function prepare_package(); /** * this is the function that is responsible for sending @@ -1111,7 +1099,7 @@ abstract class portfolio_plugin_base { * @param array $data data from form. * @return array keyvalue pairs - form element => error string */ - public static function admin_config_validation($data) {} + public function admin_config_validation($data) {} /** * mform to display to the user exporting data using this plugin. * if your plugin doesn't need user input at this time, @@ -1396,7 +1384,7 @@ abstract class portfolio_plugin_base { */ public final function set($field, $value) { if (property_exists($this, $field)) { - $this->{$field} = $value; + $this->{$field} =& $value; $this->dirty = true; return true; } @@ -1631,15 +1619,15 @@ final class portfolio_exporter { private $instance; private $noconfig; private $navigation; - private $uniquekey; - private $tempdir; private $user; public $instancefile; public $callerfile; /** - * id of this export - matches record in portfolio_tempdata + * id of this export + * matches record in portfolio_tempdata table + * and used for itemid for file storage. */ private $id; @@ -1655,10 +1643,12 @@ final class portfolio_exporter { $this->caller =& $caller; if ($instance) { $this->instancefile = 'portfolio/type/' . $instance->get('plugin') . '/lib.php'; + $this->instance->set('exporter', $this); } $this->callerfile = $callerfile; $this->stage = PORTFOLIO_STAGE_CONFIG; $this->navigation = $navigation; + $this->caller->set('exporter', $this); } /* @@ -1683,11 +1673,12 @@ final class portfolio_exporter { * @todo determine what to return in the error case */ - public function set($field, $value) { + public function set($field, &$value) { if (property_exists($this, $field)) { - $this->{$field} = $value; + $this->{$field} =& $value; if ($field == 'instance') { $this->instancefile = 'portfolio/type/' . $this->instance->get('plugin') . '/lib.php'; + $this->instance->set('exporter', $this); } $this->dirty = true; return true; @@ -1934,14 +1925,10 @@ final class portfolio_exporter { // now we've agreed on a format, // the caller is given control to package it up however it wants // and then the portfolio plugin is given control to do whatever it wants. - $unique = $this->user->id . '-' . time(); - $tempdir = temp_portfolio_working_directory($unique); - $this->uniquekey = $unique; - $this->tempdir = $tempdir; - if (!$this->caller->prepare_package($tempdir)) { + if (!$this->caller->prepare_package()) { return $this->raise_error('callercouldnotpackage', 'portfolio', $this->caller->get_return_url()); } - if (!$package = $this->instance->prepare_package($tempdir)) { + if (!$package = $this->instance->prepare_package()) { return $this->raise_error('plugincouldnotpackage', 'portfolio', $this->caller->get_return_url()); } return true; @@ -1954,11 +1941,10 @@ final class portfolio_exporter { */ public function process_stage_cleanup() { global $CFG, $DB, $SESSION; - // @todo this is unpleasant. fix it. - require_once($CFG->dirroot . '/backup/lib.php'); - delete_dir_contents($this->tempdir); // @todo maybe add a hook in the plugin(s) $DB->delete_records('portfolio_tempdata', array('id' => $this->id)); + $fs = get_file_storage(); + $fs->delete_area_files(SYSCONTEXTID, 'portfolio_exporter', $this->id); unset($SESSION->portfolioexport); return true; } @@ -2038,7 +2024,7 @@ final class portfolio_exporter { * error handler - decides whether we're running interactively or not * and behaves accordingly */ - public static function raise_error($string, $module='moodle', $continue=null) { + public function raise_error($string, $module='moodle', $continue=null) { if (defined('FULLME') && FULLME == 'cron') { debugging(get_string($string, $module)); return false; @@ -2071,6 +2057,7 @@ final class portfolio_exporter { 'expirytime' => time() + (60*60*24), ); $this->id = $DB->insert_record('portfolio_tempdata', $r); + $this->save(); // call again so that id gets added to the save data. } else { $DB->set_field('portfolio_tempdata', 'data', base64_encode(serialize($this)), array('id' => $this->id)); } @@ -2089,6 +2076,71 @@ final class portfolio_exporter { $exporter = unserialize(serialize($exporter)); return $exporter; } + + /** + * copies a file from somewhere else in moodle + * to the portfolio temporary working directory + * associated with this export + * + * @param $oldfile stored_file object + */ + public function copy_existing_file($oldfile) { + $fs = get_file_storage(); + $file_record = $this->new_file_record_base($oldfile->get_filename()); + try { + return $fs->create_file_from_storedfile($file_record, $oldfile->get_id()); + } catch (file_exception $e) { + return false; + } + } + + /** + * writes out some content to a file in the + * portfolio temporary working directory + * associated with this export + * + * @param string $content content to write + * @param string $name filename to use + */ + public function write_new_file($content, $name) { + $fs = get_file_storage(); + $file_record = $this->new_file_record_base($name); + return $fs->create_file_from_string($file_record, $content); + } + + /** + * returns an arary of files in the temporary working directory + * for this export + * always use this instead of the files api directly + * + * @return arary + */ + public function get_tempfiles() { + $fs = get_file_storage(); + $files = $fs->get_area_files(SYSCONTEXTID, 'portfolio_exporter', $this->id, '', false); + if (empty($files)) { + return array(); + } + return $files; + } + + /** + * helper function to create the beginnings of a file_record object + * to create a new file in the portfolio_temporary working directory + * use {@see write_new_file} or {@see copy_existing_file} externally + * + * @param string $name filename of new record + */ + private function new_file_record_base($name) { + return (object)array( + 'contextid' => SYSCONTEXTID, + 'filearea' => 'portfolio_exporter', + 'itemid' => $this->id, + 'filepath' => '/', + 'filename' => $name, + ); + } + } class portfolio_instance_select extends moodleform { diff --git a/mod/assignment/lib.php b/mod/assignment/lib.php index 0891a990dd..c3cce74b9f 100644 --- a/mod/assignment/lib.php +++ b/mod/assignment/lib.php @@ -1714,7 +1714,7 @@ class assignment_base { $path = $browser->encodepath($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/assignment_submission/'.$userid.'/'.$filename); $output .= ''.$icon.''.s($filename).''; if ($this->portfolio_exportable() && true) { // @todo replace with capability check - $p['file'] = $file; + $p['file'] = $file->get_id(); $output .= portfolio_add_button('assignment_portfolio_caller', $p, null, false, true); } $output .= '
'; @@ -3144,20 +3144,19 @@ class assignment_portfolio_caller extends portfolio_module_caller_base { $this->file = (array_key_exists('file', $callbackargs)) ? $callbackargs['file'] : null; } - public function prepare_package($tempdir) { + public function prepare_package() { global $CFG; if (is_callable(array($this->assignment, 'portfolio_prepare_package'))) { - return $this->assignment->portfolio_prepare_package($tempdir); + return $this->assignment->portfolio_prepare_package($this->exporter); } -error('TODO: covert'); - // default... - $filearea = $CFG->dataroot . '/' . $this->assignment->file_area_name($this->userid); - //@todo penny this is a dreadful thing to have to call (replace with files api anyway) - require_once($CFG->dirroot . '/backup/lib.php'); - if ($this->file) { - return backup_copy_file($filearea . '/' . $this->file, $tempdir . '/' . $this->file); + $fs = get_file_storage(); + $status = true; + if ($files = $fs->get_area_files($this->assignment->context->id, 'assignment_submission', $this->user->id, '', false)) { + foreach ($files as $file) { + $status = $status && $this->exporter->copy_existing_file($file); + } } - return backup_copy_file($filearea, $tempdir); + return $status; } public function get_sha1() { @@ -3166,17 +3165,19 @@ error('TODO: covert'); return $this->assignment->portfolio_get_sha1(); } -error('TODO: covert'); // default ... - $filearea = $CFG->dataroot . '/' . $this->assignment->file_area_name($this->userid); + $fs = get_file_storage(); + $status = true; if ($this->file) { - return sha1_file($filearea . '/' . $this->file); + return $fs->get_file($this->file)->get_contenthash(); } - $sha1s = array(); - foreach (get_directory_list($filearea) as $file) { - $sha1s[] = sha1_file($filearea . '/' . $file); + if ($files = $fs->get_area_files($this->assignment->context->id, 'assignment_submission', $this->user->id, '', false)) { + $sha1s = array(); + foreach ($files as $file) { + $sha1s[] = $file->get_contenthash(); + } + asort($sha1s); } - asort($sha1s); return sha1(implode('', $sha1s)); } diff --git a/mod/assignment/type/online/assignment.class.php b/mod/assignment/type/online/assignment.class.php index 31542098d5..bbf00cb772 100644 --- a/mod/assignment/type/online/assignment.class.php +++ b/mod/assignment/type/online/assignment.class.php @@ -275,12 +275,9 @@ class assignment_online extends assignment_base { return sha1(format_text($submission->data1, $submission->data2)); } - function portfolio_prepare_package($tempdir) { + function portfolio_prepare_package($exporter) { $submission = $this->get_submission(); - $handle = fopen($tempdir . '/assignment.html', 'w'); - $status = $handle && fwrite($handle, format_text($submission->data1, $submission->data2)); - $status = $status && fclose($handle); - return $status; + return $exporter->write_new_file(format_text($submission->data1, $submission->data2), 'assignment.html'); } } diff --git a/mod/data/export_form.php b/mod/data/export_form.php index 8016bf556b..ad53356f37 100644 --- a/mod/data/export_form.php +++ b/mod/data/export_form.php @@ -53,11 +53,16 @@ class mod_data_export_form extends moodleform { } $this->add_checkbox_controller(1, null, null, 1); require_once($CFG->libdir . '/portfoliolib.php'); - if ($portfoliooptions = portfolio_instance_select(portfolio_instances(), call_user_func(array('data_portfolio_caller', 'supported_formats')), 'data_portfolio_caller', '', true, true)) { - $mform->addElement('header', 'notice', get_string('portfolionotfile', 'portfolio') . ':'); - $portfoliooptions[0] = get_string('none'); - ksort($portfoliooptions); - $mform->addElement('select', 'portfolio', get_string('portfolio', 'portfolio'), $portfoliooptions); + if (false) { // @todo penny replace with permissions check + if ($portfoliooptions = portfolio_instance_select( + portfolio_instances(), + call_user_func(array('data_portfolio_caller', 'supported_formats')), + 'data_portfolio_caller', '', true, true)) { + $mform->addElement('header', 'notice', get_string('portfolionotfile', 'portfolio') . ':'); + $portfoliooptions[0] = get_string('none'); + ksort($portfoliooptions); + $mform->addElement('select', 'portfolio', get_string('portfolio', 'portfolio'), $portfoliooptions); + } } $this->add_action_buttons(true, get_string('exportdatabaserecords', 'data')); } diff --git a/mod/data/lib.php b/mod/data/lib.php index 05d7db0f21..8a66872ad0 100755 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -2472,7 +2472,7 @@ class data_portfolio_caller extends portfolio_module_caller_base { return sha1($str . ',' . $this->exporttype); } - public function prepare_package($tempdir) { + public function prepare_package() { global $DB; $count = count($this->exportdata); switch ($this->exporttype) { diff --git a/mod/forum/lib.php b/mod/forum/lib.php index 8672ae7ddf..dc37fb60b0 100644 --- a/mod/forum/lib.php +++ b/mod/forum/lib.php @@ -3072,7 +3072,7 @@ function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=fa $p = array( 'postid' => $post->id, ); - $commands[] = portfolio_add_button('forum_portfolio_caller', $p, '/mod/forum/lib.php', false, true); + //$commands[] = portfolio_add_button('forum_portfolio_caller', $p, '/mod/forum/lib.php', false, true); } echo '
'; @@ -3840,7 +3840,7 @@ function forum_print_attachments($post, $return=NULL) { 'postid' => $post->id, 'attachment' => 1, ); - $output .= portfolio_add_button('forum_portfolio_caller', $p, '/mod/forum/lib.php', false, true); + //$output .= portfolio_add_button('forum_portfolio_caller', $p, '/mod/forum/lib.php', false, true); } $output .= "
"; @@ -3859,7 +3859,7 @@ function forum_print_attachments($post, $return=NULL) { 'postid' => $post->id, 'attachment' => 1, ); - portfolio_add_button('forum_portfolio_caller', $p, '/mod/forum/lib.php', false); + //portfolio_add_button('forum_portfolio_caller', $p, '/mod/forum/lib.php', false); } echo '
'; } @@ -7044,15 +7044,16 @@ class forum_portfolio_caller extends portfolio_module_caller_base { return array($navlinks, $this->cm); } - function prepare_package($tempdir) { + function prepare_package() { global $CFG, $SESSION; // either a whole discussion // a single post, with or without attachment // or just an attachment with no post if (!$this->post) { // whole discussion portfolio_exporter::raise_error('exoprting whole discussion not implemented - see MDL-15758'); - // @todo see MDL-15758 + // @todo see MDL-15758 } else { + if ($basedir = forum_file_area($this->post)) { //@todo penny fix all this with files api require_once($CFG->dirroot . '/backup/lib.php'); diff --git a/mod/glossary/lib.php b/mod/glossary/lib.php index 5f46db4b5e..9408a8ac35 100644 --- a/mod/glossary/lib.php +++ b/mod/glossary/lib.php @@ -2415,7 +2415,7 @@ class glossary_csv_portfolio_caller extends portfolio_module_caller_base { return sha1(serialize($this->exportdata)); } - public function prepare_package($tempdir) { + public function prepare_package() { $entries = $this->exportdata['entries']; $aliases = array(); $categories = array(); @@ -2436,11 +2436,7 @@ class glossary_csv_portfolio_caller extends portfolio_module_caller_base { } } $csv = glossary_generate_export_csv($entries, $aliases, $categories); - // @todo - convert to files api. - $status = ($handle = fopen($tempdir . '/' . clean_filename($this->cm->name) . '.csv', 'w')); - $status = $status && fwrite($handle, $csv); - $status = $status && fclose($handle); - return $status; + return $this->exporter->write_new_file($csv, clean_filename($this->cm->name) . '.csv'); } public function check_permissions() { @@ -2493,17 +2489,14 @@ class glossary_entry_portfolio_caller extends portfolio_module_caller_base { return get_string('modname', 'glossary'); } - public function prepare_package($tempdir) { - $this->entry->approved = true; // in case we don't have $USER which this function checks + public function prepare_package() { + // in case we don't have USER this will make the entry be printed + $this->entry->approved = true; define('PORTFOLIO_INTERNAL', true); ob_start(); glossary_print_entry($this->get('course'), $this->cm, $this->glossary, $this->entry, null, null, false); $content = ob_get_clean(); - // @todo - convert to files api. - $status = ($handle = fopen($tempdir . '/' . clean_filename($this->entry->concept) . '.html', 'w')); - $status = $status && fwrite($handle, $content); - $status = $status && fclose($handle); - return $status; + return $this->exporter->write_new_file($content, clean_filename($this->entry->concept) . '.html'); } public function get_sha1() { diff --git a/mod/resource/lib.php b/mod/resource/lib.php index 047ea08259..6d949cf87f 100644 --- a/mod/resource/lib.php +++ b/mod/resource/lib.php @@ -249,20 +249,17 @@ class resource_base { //override to add your own options } - function portfolio_prepare_package_uploaded($tempdir) { + function portfolio_prepare_package_uploaded($exporter) { // @todo penny implement later - see MDL-15758 } - function portfolio_prepare_package_online($tempdir, $text=false) { - //@todo penny use the files api here - $status = $handle = fopen($tempdir . '/' . clean_filename($this->cm->name . '.' . (($text) ? 'txt' : 'html')), 'w'); + function portfolio_prepare_package_online($exporter, $text=false) { + $filename = clean_filename($this->cm->name . '.' . (($text) ? 'txt' : 'html')); $formatoptions = (object)array('noclean' => true); $format = (($text) ? FORMAT_MOODLE : FORMAT_HTML); $content = format_text($this->resource->alltext, $format, $formatoptions, $this->course->id); - $status = $status && fwrite($handle, $content); - $status = $status && fclose($handle); - return $status; + return $exporter->write_new_file($content, $filename); } function portfolio_get_sha1_online($text=false) { @@ -741,8 +738,8 @@ class resource_portfolio_caller extends portfolio_module_caller_base { return PORTFOLIO_TIME_LOW; } - public function prepare_package($tempdir) { - return $this->resource->portfolio_prepare_package($tempdir); + public function prepare_package() { + return $this->resource->portfolio_prepare_package($this->exporter); } public function check_permissions() { diff --git a/mod/resource/type/html/resource.class.php b/mod/resource/type/html/resource.class.php index f77860dc04..1e2e387fa7 100644 --- a/mod/resource/type/html/resource.class.php +++ b/mod/resource/type/html/resource.class.php @@ -193,8 +193,8 @@ function setup_elements(&$mform) { } } -function portfolio_prepare_package($tempdir) { - return parent::portfolio_prepare_package_online($tempdir); +function portfolio_prepare_package($exporter) { + return parent::portfolio_prepare_package_online($exporter); } function portfolio_get_sha1() { diff --git a/mod/resource/type/text/resource.class.php b/mod/resource/type/text/resource.class.php index 8c2e377fd7..99f3689431 100644 --- a/mod/resource/type/text/resource.class.php +++ b/mod/resource/type/text/resource.class.php @@ -199,8 +199,8 @@ function setup_elements(&$mform) { } -function portfolio_prepare_package($tempdir) { - return parent::portfolio_prepare_package_online($tempdir, true); +function portfolio_prepare_package($exporter) { + return parent::portfolio_prepare_package_online($exporter, true); } function portfolio_get_sha1() { diff --git a/portfolio/add.php b/portfolio/add.php index 240cc71963..e467be1983 100644 --- a/portfolio/add.php +++ b/portfolio/add.php @@ -35,6 +35,7 @@ if ($dataid) { } $instance->set('user', $USER); $exporter->set('instance', $instance); + $exporter->save(); } } } else { @@ -94,18 +95,8 @@ if ($dataid) { $SESSION->portfolioexport = $exporter->get('id'); } - -$stage = optional_param('stage', PORTFOLIO_STAGE_CONFIG); -$alreadystolen = false; -// for places returning control to pass (rather than PORTFOLIO_STAGE_PACKAGE -// which is unstable if they can't get to the constant (eg external system) -if ($postcontrol = optional_param('postcontrol', 0, PARAM_INT)) { - $stage = $exporter->get('stage'); - $exporter->instance()->post_control($stage, array_merge($_GET, $_POST)); - $alreadystolen = true; -} - if (!$exporter->get('instance')) { + print_object($exporter); // we've just arrived but have no instance // so retrieve everything from the request, // add them as hidden fields in a new form @@ -135,6 +126,15 @@ if (!$exporter->get('instance')) { } } +$stage = optional_param('stage', PORTFOLIO_STAGE_CONFIG); +$alreadystolen = false; +// for places returning control to pass (rather than PORTFOLIO_STAGE_PACKAGE +// which is unstable if they can't get to the constant (eg external system) +if ($postcontrol = optional_param('postcontrol', 0, PARAM_INT)) { + $stage = $exporter->get('stage'); + $exporter->instance()->post_control($stage, array_merge($_GET, $_POST)); + $alreadystolen = true; +} $exporter->process_stage($stage, $alreadystolen); ?> diff --git a/portfolio/type/boxnet/lib.php b/portfolio/type/boxnet/lib.php index ebcf548258..28f89808c0 100644 --- a/portfolio/type/boxnet/lib.php +++ b/portfolio/type/boxnet/lib.php @@ -7,24 +7,26 @@ class portfolio_plugin_boxnet extends portfolio_plugin_base { private $boxclient; private $ticket; private $authtoken; - private $workdir; private $folders; - public function prepare_package($tempdir) { - $this->workdir = $tempdir; + public function prepare_package() { return true; // don't do anything else for this plugin, we want to send all files as they are. } public function send_package() { $ret = array(); - foreach (get_directory_list($this->workdir) as $file) { - $file = $this->workdir . '/' . $file; - $ret[] = $this->boxclient->uploadFile( + foreach ($this->exporter->get_tempfiles() as $file) { + $return = $this->boxclient->uploadFile( array( 'file' => $file, 'folder_id' => $this->get_export_config('folder') ) ); + if (array_key_exists('status', $return) && $return['status'] == 'upload_ok' + && array_key_exists('id', $return) && count($return['id']) == 1) { + $return['rename'] = $this->boxclient->renameFile($return['id'][array_pop(array_keys($return['id']))], $file->get_filename()); + $ret[] = $return; + } } if ($this->boxclient->isError()) { return false; diff --git a/portfolio/type/download/lib.php b/portfolio/type/download/lib.php index e37c1aa9e4..1c8678f36e 100644 --- a/portfolio/type/download/lib.php +++ b/portfolio/type/download/lib.php @@ -15,7 +15,7 @@ class portfolio_plugin_download extends portfolio_plugin_base { return PORTFOLIO_TIME_LOW; } - public function prepare_package($tempdir) { + public function prepare_package() { // just zip up whatever files the caller has created for us // and move them to the user's temporary area. $userdir = temp_portfolio_usertemp_directory($this->get('user')->id); @@ -47,6 +47,10 @@ class portfolio_plugin_download extends portfolio_plugin_base { public function get_continue_url() { return false; } + + public static function plugin_sanity_check() { + return 'notupgradedtousefilesapi'; + } } ?> -- 2.39.5