Example #1
0
 /**
  * Prepare file reference information
  *
  * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned)
  * @return string file reference, ready to be stored
  */
 public function get_file_reference($source)
 {
     if ($source && $this->has_moodle_files()) {
         $params = @json_decode(base64_decode($source), true);
         if (!is_array($params) || empty($params['contextid'])) {
             throw new repository_exception('invalidparams', 'repository');
         }
         $params = array('component' => empty($params['component']) ? '' : clean_param($params['component'], PARAM_COMPONENT), 'filearea' => empty($params['filearea']) ? '' : clean_param($params['filearea'], PARAM_AREA), 'itemid' => empty($params['itemid']) ? 0 : clean_param($params['itemid'], PARAM_INT), 'filename' => empty($params['filename']) ? null : clean_param($params['filename'], PARAM_FILE), 'filepath' => empty($params['filepath']) ? null : clean_param($params['filepath'], PARAM_PATH), 'contextid' => clean_param($params['contextid'], PARAM_INT));
         // Check if context exists.
         if (!context::instance_by_id($params['contextid'], IGNORE_MISSING)) {
             throw new repository_exception('invalidparams', 'repository');
         }
         return file_storage::pack_reference($params);
     }
     return $source;
 }
Example #2
0
 /**
  * What to do when this step is executed.
  */
 protected function define_execution()
 {
     global $DB;
     $this->log('processing file aliases queue', backup::LOG_DEBUG);
     $fs = get_file_storage();
     // Load the queue.
     $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $this->get_restoreid(), 'itemname' => 'file_aliases_queue'), '', 'info');
     // Iterate over aliases in the queue.
     foreach ($rs as $record) {
         $info = backup_controller_dbops::decode_backup_temp_info($record->info);
         // Try to pick a repository instance that should serve the alias.
         $repository = $this->choose_repository($info);
         if (is_null($repository)) {
             $this->notify_failure($info, 'unable to find a matching repository instance');
             continue;
         }
         if ($info->oldfile->repositorytype === 'local' or $info->oldfile->repositorytype === 'coursefiles') {
             // Aliases to Server files and Legacy course files may refer to a file
             // contained in the backup file or to some existing file (if we are on the
             // same site).
             try {
                 $reference = file_storage::unpack_reference($info->oldfile->reference);
             } catch (Exception $e) {
                 $this->notify_failure($info, 'invalid reference field format');
                 continue;
             }
             // Let's see if the referred source file was also included in the backup.
             $candidates = $DB->get_recordset('backup_files_temp', array('backupid' => $this->get_restoreid(), 'contextid' => $reference['contextid'], 'component' => $reference['component'], 'filearea' => $reference['filearea'], 'itemid' => $reference['itemid']), '', 'info, newcontextid, newitemid');
             $source = null;
             foreach ($candidates as $candidate) {
                 $candidateinfo = backup_controller_dbops::decode_backup_temp_info($candidate->info);
                 if ($candidateinfo->filename === $reference['filename'] and $candidateinfo->filepath === $reference['filepath'] and !is_null($candidate->newcontextid) and !is_null($candidate->newitemid)) {
                     $source = $candidateinfo;
                     $source->contextid = $candidate->newcontextid;
                     $source->itemid = $candidate->newitemid;
                     break;
                 }
             }
             $candidates->close();
             if ($source) {
                 // We have an alias that refers to another file also included in
                 // the backup. Let us change the reference field so that it refers
                 // to the restored copy of the original file.
                 $reference = file_storage::pack_reference($source);
                 // Send the new alias to the filepool.
                 $fs->create_file_from_reference($info->newfile, $repository->id, $reference);
                 $this->notify_success($info);
                 continue;
             } else {
                 // This is a reference to some moodle file that was not contained in the backup
                 // file. If we are restoring to the same site, keep the reference untouched
                 // and restore the alias as is if the referenced file exists.
                 if ($this->task->is_samesite()) {
                     if ($fs->file_exists($reference['contextid'], $reference['component'], $reference['filearea'], $reference['itemid'], $reference['filepath'], $reference['filename'])) {
                         $reference = file_storage::pack_reference($reference);
                         $fs->create_file_from_reference($info->newfile, $repository->id, $reference);
                         $this->notify_success($info);
                         continue;
                     } else {
                         $this->notify_failure($info, 'referenced file not found');
                         continue;
                     }
                     // If we are at other site, we can't restore this alias.
                 } else {
                     $this->notify_failure($info, 'referenced file not included');
                     continue;
                 }
             }
         } else {
             if ($info->oldfile->repositorytype === 'user') {
                 if ($this->task->is_samesite()) {
                     // For aliases to user Private files at the same site, we have a chance to check
                     // if the referenced file still exists.
                     try {
                         $reference = file_storage::unpack_reference($info->oldfile->reference);
                     } catch (Exception $e) {
                         $this->notify_failure($info, 'invalid reference field format');
                         continue;
                     }
                     if ($fs->file_exists($reference['contextid'], $reference['component'], $reference['filearea'], $reference['itemid'], $reference['filepath'], $reference['filename'])) {
                         $reference = file_storage::pack_reference($reference);
                         $fs->create_file_from_reference($info->newfile, $repository->id, $reference);
                         $this->notify_success($info);
                         continue;
                     } else {
                         $this->notify_failure($info, 'referenced file not found');
                         continue;
                     }
                     // If we are at other site, we can't restore this alias.
                 } else {
                     $this->notify_failure($info, 'restoring at another site');
                     continue;
                 }
             } else {
                 // This is a reference to some external file such as in boxnet or dropbox.
                 // If we are restoring to the same site, keep the reference untouched and
                 // restore the alias as is.
                 if ($this->task->is_samesite()) {
                     $fs->create_file_from_reference($info->newfile, $repository->id, $info->oldfile->reference);
                     $this->notify_success($info);
                     continue;
                     // If we are at other site, we can't restore this alias.
                 } else {
                     $this->notify_failure($info, 'restoring at another site');
                     continue;
                 }
             }
         }
     }
     $rs->close();
 }
Example #3
0
/**
 * Initialise a draft file area from a real one by copying the files. A draft
 * area will be created if one does not already exist. Normally you should
 * get $draftitemid by calling file_get_submitted_draft_itemid('elementname');
 *
 * @category files
 * @global stdClass $CFG
 * @global stdClass $USER
 * @param int $draftitemid the id of the draft area to use, or 0 to create a new one, in which case this parameter is updated.
 * @param int $contextid This parameter and the next two identify the file area to copy files from.
 * @param string $component
 * @param string $filearea helps indentify the file area.
 * @param int $itemid helps identify the file area. Can be null if there are no files yet.
 * @param array $options text and file options ('subdirs'=>false, 'forcehttps'=>false)
 * @param string $text some html content that needs to have embedded links rewritten to point to the draft area.
 * @return string|null returns string if $text was passed in, the rewritten $text is returned. Otherwise NULL.
 */
function file_prepare_draft_area(&$draftitemid, $contextid, $component, $filearea, $itemid, array $options = null, $text = null)
{
    global $CFG, $USER, $CFG;
    $options = (array) $options;
    if (!isset($options['subdirs'])) {
        $options['subdirs'] = false;
    }
    if (!isset($options['forcehttps'])) {
        $options['forcehttps'] = false;
    }
    $usercontext = context_user::instance($USER->id);
    $fs = get_file_storage();
    if (empty($draftitemid)) {
        // create a new area and copy existing files into
        $draftitemid = file_get_unused_draft_itemid();
        $file_record = array('contextid' => $usercontext->id, 'component' => 'user', 'filearea' => 'draft', 'itemid' => $draftitemid);
        if (!is_null($itemid) and $files = $fs->get_area_files($contextid, $component, $filearea, $itemid)) {
            foreach ($files as $file) {
                if ($file->is_directory() and $file->get_filepath() === '/') {
                    // we need a way to mark the age of each draft area,
                    // by not copying the root dir we force it to be created automatically with current timestamp
                    continue;
                }
                if (!$options['subdirs'] and ($file->is_directory() or $file->get_filepath() !== '/')) {
                    continue;
                }
                $draftfile = $fs->create_file_from_storedfile($file_record, $file);
                // XXX: This is a hack for file manager (MDL-28666)
                // File manager needs to know the original file information before copying
                // to draft area, so we append these information in mdl_files.source field
                // {@link file_storage::search_references()}
                // {@link file_storage::search_references_count()}
                $sourcefield = $file->get_source();
                $newsourcefield = new stdClass();
                $newsourcefield->source = $sourcefield;
                $original = new stdClass();
                $original->contextid = $contextid;
                $original->component = $component;
                $original->filearea = $filearea;
                $original->itemid = $itemid;
                $original->filename = $file->get_filename();
                $original->filepath = $file->get_filepath();
                $newsourcefield->original = file_storage::pack_reference($original);
                $draftfile->set_source(serialize($newsourcefield));
                // End of file manager hack
            }
        }
        if (!is_null($text)) {
            // at this point there should not be any draftfile links yet,
            // because this is a new text from database that should still contain the @@pluginfile@@ links
            // this happens when developers forget to post process the text
            $text = str_replace("\"{$CFG->httpswwwroot}/draftfile.php", "\"{$CFG->httpswwwroot}/brokenfile.php#", $text);
        }
    } else {
        // nothing to do
    }
    if (is_null($text)) {
        return null;
    }
    // relink embedded files - editor can not handle @@PLUGINFILE@@ !
    return file_rewrite_pluginfile_urls($text, 'draftfile.php', $usercontext->id, 'user', 'draft', $draftitemid, $options);
}
Example #4
0
 /**
  * Prepare file reference information
  *
  * @param string $source source of the file, returned by repository as 'source' and received back from user (not cleaned)
  * @return string file reference, ready to be stored
  */
 public function get_file_reference($source) {
     if ($source && $this->has_moodle_files()) {
         $params = @json_decode(base64_decode($source), true);
         if (!$params && !in_array($this->get_typename(), array('recent', 'user', 'local', 'coursefiles'))) {
             // IMPORTANT! Since default format for moodle files was changed in the minor release as a security fix
             // we maintain an old code here in order not to break 3rd party repositories that deal
             // with moodle files. Repositories are strongly encouraged to be upgraded, see MDL-45616.
             // In Moodle 2.8 this fallback will be removed.
             $params = file_storage::unpack_reference($source, true);
             return file_storage::pack_reference($params);
         }
         if (!is_array($params) || empty($params['contextid'])) {
             throw new repository_exception('invalidparams', 'repository');
         }
         $params = array(
             'component' => empty($params['component']) ? ''   : clean_param($params['component'], PARAM_COMPONENT),
             'filearea'  => empty($params['filearea'])  ? ''   : clean_param($params['filearea'], PARAM_AREA),
             'itemid'    => empty($params['itemid'])    ? 0    : clean_param($params['itemid'], PARAM_INT),
             'filename'  => empty($params['filename'])  ? null : clean_param($params['filename'], PARAM_FILE),
             'filepath'  => empty($params['filepath'])  ? null : clean_param($params['filepath'], PARAM_PATH),
             'contextid' => clean_param($params['contextid'], PARAM_INT)
         );
         // Check if context exists.
         if (!context::instance_by_id($params['contextid'], IGNORE_MISSING)) {
             throw new repository_exception('invalidparams', 'repository');
         }
         return file_storage::pack_reference($params);
     }
     return $source;
 }
 public function test_search_references()
 {
     $user = $this->setup_three_private_files();
     $fs = get_file_storage();
     $repos = repository::get_instances(array('type' => 'user'));
     $repo = reset($repos);
     $alias1 = array('contextid' => $user->ctxid, 'component' => 'user', 'filearea' => 'private', 'itemid' => 0, 'filepath' => '/aliases/', 'filename' => 'alias-to-1.txt');
     $alias2 = array('contextid' => $user->ctxid, 'component' => 'user', 'filearea' => 'private', 'itemid' => 0, 'filepath' => '/aliases/', 'filename' => 'another-alias-to-1.txt');
     $reference = file_storage::pack_reference(array('contextid' => $user->ctxid, 'component' => 'user', 'filearea' => 'private', 'itemid' => 0, 'filepath' => '/', 'filename' => '1.txt'));
     // There are no aliases now.
     $result = $fs->search_references($reference);
     $this->assertEquals(array(), $result);
     $result = $fs->search_references_count($reference);
     $this->assertSame($result, 0);
     // Create two aliases and make sure they are returned.
     $fs->create_file_from_reference($alias1, $repo->id, $reference);
     $fs->create_file_from_reference($alias2, $repo->id, $reference);
     $result = $fs->search_references($reference);
     $this->assertTrue(is_array($result));
     $this->assertEquals(count($result), 2);
     foreach ($result as $alias) {
         $this->assertTrue($alias instanceof stored_file);
     }
     $result = $fs->search_references_count($reference);
     $this->assertSame($result, 2);
     // The method can't be used for references to files outside the filepool.
     $exceptionthrown = false;
     try {
         $fs->search_references('http://dl.dropbox.com/download/1234567/naked-dougiamas.jpg');
     } catch (file_reference_exception $e) {
         $exceptionthrown = true;
     }
     $this->assertTrue($exceptionthrown);
     $exceptionthrown = false;
     try {
         $fs->search_references_count('http://dl.dropbox.com/download/1234567/naked-dougiamas.jpg');
     } catch (file_reference_exception $e) {
         $exceptionthrown = true;
     }
     $this->assertTrue($exceptionthrown);
 }
Example #6
0
 /**
  * Prepare file reference information
  *
  * @param string $source
  * @return string file referece
  */
 public function get_file_reference($source)
 {
     if ($this->has_moodle_files() && $this->supported_returntypes() & FILE_REFERENCE) {
         $params = file_storage::unpack_reference($source);
         if (!is_array($params)) {
             throw new repository_exception('invalidparams', 'repository');
         }
         return file_storage::pack_reference($params);
     }
     return $source;
 }
Example #7
0
 /**
  * Prepare file reference information
  *
  * @param string $source
  * @return string file referece
  */
 public function get_file_reference($source)
 {
     global $USER;
     $params = unserialize(base64_decode($source));
     if (is_array($params)) {
         $filepath = clean_param($params['filepath'], PARAM_PATH);
         $filename = clean_param($params['filename'], PARAM_FILE);
         $contextid = clean_param($params['contextid'], PARAM_INT);
     }
     // We store all file parameters, so file api could
     // find the refernces later.
     $reference = array();
     $reference['contextid'] = $contextid;
     $reference['component'] = 'user';
     $reference['filearea'] = 'private';
     $reference['itemid'] = 0;
     $reference['filepath'] = $filepath;
     $reference['filename'] = $filename;
     return file_storage::pack_reference($reference);
 }
Example #8
0
 /**
  * Unpack file info and pack it, mainly for data validation
  *
  * @param string $source
  * @return string file referece
  */
 public function get_file_reference($source)
 {
     $params = unserialize(base64_decode($source));
     if (!is_array($params)) {
         throw new repository_exception('invalidparams', 'repository');
     }
     $filename = is_null($params['filename']) ? null : clean_param($params['filename'], PARAM_FILE);
     $filepath = is_null($params['filepath']) ? null : clean_param($params['filepath'], PARAM_PATH);
     $contextid = is_null($params['contextid']) ? null : clean_param($params['contextid'], PARAM_INT);
     $reference = array();
     // hard coded filearea, component and itemid for security
     $reference['component'] = 'course';
     $reference['filearea'] = 'legacy';
     $reference['itemid'] = 0;
     $reference['contextid'] = $contextid;
     $reference['filepath'] = $filepath;
     $reference['filename'] = $filename;
     return file_storage::pack_reference($reference);
 }
Example #9
0
/**
 * Determine if dir path exists or not in repository
 *
 * @param string   $dirpath
 * @param stdclass $repository
 * @param boolean  $encodepath
 * @param array    $params
 * @return boolean true if dir path exists in repository, false otherwise
 */
function hotpot_pluginfile_dirpath_exists($dirpath, $repository, $encodepath, $params)
{
    $dirs = explode('/', $dirpath);
    foreach ($dirs as $i => $dir) {
        $dirpath = implode('/', array_slice($dirs, 0, $i));
        if ($encodepath) {
            $params['filepath'] = '/' . $dirpath . ($dirpath == '' ? '' : '/');
            $params['filename'] = '.';
            // "." signifies a directory
            $dirpath = file_storage::pack_reference($params);
        }
        $exists = false;
        $listing = $repository->get_listing($dirpath);
        foreach ($listing['list'] as $file) {
            if (empty($file['source'])) {
                if ($file['title'] == $dir) {
                    $exists = true;
                    break;
                }
            }
        }
        if (!$exists) {
            return false;
        }
    }
    // all dirs in path exist - success !!
    return true;
}
Example #10
0
function xmldb_hotpot_locate_externalfile($contextid, $component, $filearea, $itemid, $filepath, $filename)
{
    global $CFG, $DB;
    if (!class_exists('repository')) {
        return false;
        // Moodle <= 2.2 has no repositories
    }
    static $repositories = null;
    if ($repositories === null) {
        $exclude_types = array('recent', 'upload', 'user', 'areafiles');
        $repositories = repository::get_instances();
        foreach (array_keys($repositories) as $id) {
            if (method_exists($repositories[$id], 'get_typename')) {
                $type = $repositories[$id]->get_typename();
            } else {
                $type = $repositories[$id]->options['type'];
            }
            if (in_array($type, $exclude_types)) {
                unset($repositories[$id]);
            }
        }
        // ensure upgraderunning is set
        if (empty($CFG->upgraderunning)) {
            $CFG->upgraderunning = null;
        }
    }
    // get file storage
    $fs = get_file_storage();
    // the following types repository use encoded params
    $encoded_types = array('user', 'areafiles', 'coursefiles');
    foreach ($repositories as $id => $repository) {
        // "filesystem" path is in plain text, others are encoded
        if (method_exists($repositories[$id], 'get_typename')) {
            $type = $repositories[$id]->get_typename();
        } else {
            $type = $repositories[$id]->options['type'];
        }
        $encodepath = in_array($type, $encoded_types);
        // save $root_path, because it may get messed up by
        // $repository->get_listing($path), if $path is non-existant
        if (method_exists($repository, 'get_rootpath')) {
            $root_path = $repository->get_rootpath();
        } else {
            if (isset($repository->root_path)) {
                $root_path = $repository->root_path;
            } else {
                $root_path = false;
            }
        }
        // get repository type
        switch (true) {
            case isset($repository->options['type']):
                $type = $repository->options['type'];
                break;
            case isset($repository->instance->typeid):
                $type = repository::get_type_by_id($repository->instance->typeid);
                $type = $type->get_typename();
                break;
            default:
                $type = '';
                // shouldn't happen !!
        }
        $path = $filepath;
        $source = trim($filepath . $filename, '/');
        // setup $params for path encoding, if necessary
        $params = array();
        if ($encodepath) {
            $listing = $repository->get_listing();
            switch (true) {
                case isset($listing['list'][0]['source']):
                    $param = 'source';
                    break;
                    // file
                // file
                case isset($listing['list'][0]['path']):
                    $param = 'path';
                    break;
                    // dir
                // dir
                default:
                    return false;
                    // shouldn't happen !!
            }
            $params = file_storage::unpack_reference($listing['list'][0][$param], true);
            $params['filepath'] = '/' . $path . ($path == '' ? '' : '/');
            $params['filename'] = '.';
            // "." signifies a directory
            $path = file_storage::pack_reference($params);
        }
        // reset $repository->root_path (filesystem repository only)
        if ($root_path) {
            $repository->root_path = $root_path;
        }
        // unset upgraderunning because it can cause get_listing() to fail
        $upgraderunning = $CFG->upgraderunning;
        $CFG->upgraderunning = null;
        // Note: we use "@" to suppress warnings in case $path does not exist
        $listing = @$repository->get_listing($path);
        // restore upgraderunning flag
        $CFG->upgraderunning = $upgraderunning;
        // check each file to see if it is the one we want
        foreach ($listing['list'] as $file) {
            switch (true) {
                case isset($file['source']):
                    $param = 'source';
                    break;
                    // file
                // file
                case isset($file['path']):
                    $param = 'path';
                    break;
                    // dir
                // dir
                default:
                    continue;
                    // shouldn't happen !!
            }
            if ($encodepath) {
                $file[$param] = file_storage::unpack_reference($file[$param]);
                $file[$param] = trim($file[$param]['filepath'], '/') . '/' . $file[$param]['filename'];
            }
            if ($file[$param] == $source) {
                if ($encodepath) {
                    $params['filename'] = $filename;
                    $source = file_storage::pack_reference($params);
                }
                $file_record = array('contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'sortorder' => 0, 'itemid' => 0, 'filepath' => $filepath, 'filename' => $filename);
                if ($file = $fs->create_file_from_reference($file_record, $id, $source)) {
                    return $file;
                }
                break;
                // try another repository
            }
        }
    }
    // external file not found (or found but not created)
    return false;
}
Example #11
0
/**
 * Gets main file in a file area
 *
 * if the main file is a link from an external repository
 * look for the target file in the main file's repository
 * Note: this functionality only exists in Moodle 2.3+
 *
 * @param stdclass $context
 * @param string $component 'mod_hotpot'
 * @param string $filearea  'sourcefile', 'entrytext' or 'exittext'
 * @param string $filepath  despite the name, this is a dir path with leading and trailing "/"
 * @param string $filename
 * @return stdclass if external file found, false otherwise
 */
function hotpot_pluginfile_externalfile($context, $component, $filearea, $filepath, $filename)
{
    // get file storage
    $fs = get_file_storage();
    // get main file for this $component/$filearea
    // typically this will be the HotPot quiz file
    $mainfile = hotpot_pluginfile_mainfile($context, $component, $filearea);
    // get repository - cautiously :-)
    if (!$mainfile) {
        return false;
        // no main file - shouldn't happen !!
    }
    if (!method_exists($mainfile, 'get_repository_id')) {
        return false;
        // no file linking in Moodle 2.0 - 2.2
    }
    if (!($repositoryid = $mainfile->get_repository_id())) {
        return false;
        // $mainfile is not from an external repository
    }
    if (!($repository = repository::get_repository_by_id($repositoryid, $context))) {
        return false;
        // $repository is not accessible in this context - shouldn't happen !!
    }
    // get repository type
    switch (true) {
        case isset($repository->options['type']):
            $type = $repository->options['type'];
            break;
        case isset($repository->instance->typeid):
            $type = repository::get_type_by_id($repository->instance->typeid);
            $type = $type->get_typename();
            break;
        default:
            $type = '';
            // shouldn't happen !!
    }
    // set paths (within repository) to required file
    // how we do this depends on the repository $typename
    // "filesystem" path is in plain text, others are encoded
    $mainreference = $mainfile->get_reference();
    switch ($type) {
        case 'filesystem':
            $maindirname = dirname($mainreference);
            $encodepath = false;
            break;
        case 'coursefiles':
        case 'user':
            $params = file_storage::unpack_reference($mainreference, true);
            $maindirname = $params['filepath'];
            $encodepath = true;
            break;
        default:
            echo 'unknown repository type in hotpot_pluginfile_externalfile(): ' . $type;
            die;
    }
    // remove leading and trailing "/" from dir names
    $maindirname = trim($maindirname, '/');
    $dirname = trim($filepath, '/');
    // assume path to target dir is same as path to main dir
    $path = explode('/', $maindirname);
    // traverse back up folder hierarchy if necessary
    $count = count(explode('/', $dirname));
    array_splice($path, -$count);
    // reconstruct expected dir path for source file
    if ($dirname) {
        $path[] = $dirname;
    }
    $source = $path;
    $source[] = $filename;
    $source = implode('/', $source);
    $path = implode('/', $path);
    // filepaths in the repository to search for the file
    $paths = array();
    // add to the list of possible paths
    $paths[$path] = $source;
    if ($dirname) {
        $paths[$dirname] = $dirname . '/' . $filename;
    }
    if ($maindirname) {
        $paths[$maindirname] = $maindirname . '/' . $filename;
    }
    if ($maindirname && $dirname) {
        $paths[$maindirname . '/' . $dirname] = $maindirname . '/' . $dirname . '/' . $filename;
        $paths[$dirname . '/' . $maindirname] = $dirname . '/' . $maindirname . '/' . $filename;
    }
    // add leading and trailing "/" to dir names
    $dirname = $dirname == '' ? '/' : '/' . $dirname . '/';
    $maindirname = $maindirname == '' ? '/' : '/' . $maindirname . '/';
    // locate $dirname within $maindirname
    // typically it will be absent or occur just once,
    // but it could possibly occur several times
    $search = '/' . preg_quote($dirname, '/') . '/i';
    if (preg_match_all($search, $maindirname, $matches, PREG_OFFSET_CAPTURE)) {
        $i_max = count($matches[0]);
        for ($i = 0; $i < $i_max; $i++) {
            list($match, $start) = $matches[0][$i];
            $path = substr($maindirname, 0, $start) . $match;
            $path = trim($path, '/');
            // e.g. hp6.2/html_files
            $paths[$path] = $path . '/' . $filename;
        }
    }
    // setup $params for path encoding, if necessary
    $params = array();
    if ($encodepath) {
        $listing = $repository->get_listing();
        switch (true) {
            case isset($listing['list'][0]['source']):
                $param = 'source';
                break;
                // file
            // file
            case isset($listing['list'][0]['path']):
                $param = 'path';
                break;
                // dir
            // dir
            default:
                return false;
                // shouldn't happen !!
        }
        $params = $listing['list'][0][$param];
        $params = json_decode(base64_decode($params), true);
    }
    foreach ($paths as $path => $source) {
        if (!hotpot_pluginfile_dirpath_exists($path, $repository, $type, $encodepath, $params)) {
            continue;
        }
        if ($encodepath) {
            $params['filepath'] = '/' . $path . ($path == '' ? '' : '/');
            $params['filename'] = '.';
            // "." signifies a directory
            $path = base64_encode(json_encode($params));
        }
        $listing = $repository->get_listing($path);
        foreach ($listing['list'] as $file) {
            switch (true) {
                case isset($file['source']):
                    $param = 'source';
                    break;
                    // file
                // file
                case isset($file['path']):
                    $param = 'path';
                    break;
                    // dir
                // dir
                default:
                    continue;
                    // shouldn't happen !!
            }
            if ($encodepath) {
                $file[$param] = json_decode(base64_decode($file[$param]), true);
                $file[$param] = trim($file[$param]['filepath'], '/') . '/' . $file[$param]['filename'];
            }
            if ($file[$param] == $source) {
                if ($encodepath) {
                    $params['filename'] = $filename;
                    $source = file_storage::pack_reference($params);
                }
                $file_record = array('contextid' => $context->id, 'component' => $component, 'filearea' => $filearea, 'sortorder' => 0, 'itemid' => 0, 'filepath' => $filepath, 'filename' => $filename);
                if ($file = $fs->create_file_from_reference($file_record, $repositoryid, $source)) {
                    return $file;
                }
                break;
                // couldn't create file, so give up and try a different $path
            }
        }
    }
    // external file not found (or found but not created)
    return false;
}