/** * Repository method to serve the referenced file * * @see send_stored_file * * @param stored_file $storedfile the file that contains the reference * @param int $lifetime Number of seconds before the file should expire from caches (null means $CFG->filelifetime) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime = null, $filter = 0, $forcedownload = false, array $options = null) { $reference = $this->unpack_reference($storedfile->get_reference()); if (false && $_SERVER['SCRIPT_NAME'] !== '/draftfile.php') { if ($reference['source'] === 'onedrive') { $sourceclient = $this->get_onedrive_apiclient(); $fileurl = isset($reference['url']) ? $reference['url'] : ''; $embedurl = $sourceclient->get_embed_url($reference['id'], $fileurl); if (!empty($embedurl)) { header('Location: ' . $embedurl); die; } else { if (!empty($fileurl)) { header('Location: ' . $fileurl); die; } } } } try { $fileinfo = $this->get_file($storedfile->get_reference()); if (isset($fileinfo['path'])) { $fs = get_file_storage(); list($contenthash, $filesize, $newfile) = $fs->add_file_to_pool($fileinfo['path']); // Set this file and other similar aliases synchronised. $storedfile->set_synchronized($contenthash, $filesize); } else { throw new \moodle_exception('errorwhiledownload', 'repository_office365'); } if (!is_array($options)) { $options = []; } $options['sendcachedexternalfile'] = true; send_stored_file($storedfile, $lifetime, $filter, $forcedownload, $options); } catch (\Exception $e) { send_file_not_found(); } }
/** * Repository method to serve the referenced file * * @param stored_file $storedfile the file that contains the reference * @param int $lifetime Number of seconds before the file should expire from caches (null means $CFG->filelifetime) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime = null, $filter = 0, $forcedownload = false, array $options = null) { $ref = unserialize(self::convert_to_valid_reference($storedfile->get_reference())); header('Location: ' . $ref->downloadurl); }
public function sync_reference(stored_file $file) { global $USER; if ($file->get_referencelastsync() + DAYSECS > time() || !$this->connection_result()) { // Synchronise not more often than once a day. // if we had several unsuccessfull attempts to connect to server - do not try any more. return false; } $ref = @unserialize(base64_decode($file->get_reference())); if (!isset($ref->url) || !($url = $this->appendtoken($ref->url))) { // Occurs when the user isn't known.. $file->set_missingsource(); return true; } $cookiepathname = $this->prepare_file($USER->id . '_' . uniqid('', true) . '.cookie'); $c = new curl(array('cookie' => $cookiepathname)); if (file_extension_in_typegroup($ref->filename, 'web_image')) { $path = $this->prepare_file(''); $result = $c->download_one($url, null, array('filepath' => $path, 'followlocation' => true, 'timeout' => $CFG->repositorysyncimagetimeout)); if ($result === true) { $fs = get_file_storage(); list($contenthash, $filesize, $newfile) = $fs->add_file_to_pool($path); $file->set_synchronized($contenthash, $filesize); return true; } } else { $result = $c->head($url, array('followlocation' => true, 'timeout' => $CFG->repositorysyncfiletimeout)); } // Delete cookie jar. if (file_exists($cookiepathname)) { unlink($cookiepathname); } $this->connection_result($c->get_errno()); $curlinfo = $c->get_info(); if (isset($curlinfo['http_code']) && $curlinfo['http_code'] == 200 && array_key_exists('download_content_length', $curlinfo) && $curlinfo['download_content_length'] >= 0) { // we received a correct header and at least can tell the file size $file->set_synchronized(null, $curlinfo['download_content_length']); return true; } $file->set_missingsource(); return true; }
/** * Repository method to serve the referenced file * * @see send_stored_file * * @param stored_file $storedfile the file that contains the reference * @param int $lifetime Number of seconds before the file should expire from caches (null means $CFG->filelifetime) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime = null, $filter = 0, $forcedownload = false, array $options = null) { $reference = $storedfile->get_reference(); $file = $this->get_rootpath() . ltrim($reference, '/'); if ($this->is_in_repository($reference) && is_readable($file)) { $filename = $storedfile->get_filename(); if ($options && isset($options['filename'])) { $filename = $options['filename']; } $dontdie = $options && isset($options['dontdie']); send_file($file, $filename, $lifetime, $filter, false, $forcedownload, '', $dontdie); } else { send_file_not_found(); } }
/** * Performs synchronisation of an external file if the previous one has expired. * * This function must be implemented for external repositories supporting * FILE_REFERENCE, it is called for existing aliases when their filesize, * contenthash or timemodified are requested. It is not called for internal * repositories (see {@link repository::has_moodle_files()}), references to * internal files are updated immediately when source is modified. * * Referenced files may optionally keep their content in Moodle filepool (for * thumbnail generation or to be able to serve cached copy). In this * case both contenthash and filesize need to be synchronized. Otherwise repositories * should use contenthash of empty file and correct filesize in bytes. * * Note that this function may be run for EACH file that needs to be synchronised at the * moment. If anything is being downloaded or requested from external sources there * should be a small timeout. The synchronisation is performed to update the size of * the file and/or to update image and re-generated image preview. There is nothing * fatal if syncronisation fails but it is fatal if syncronisation takes too long * and hangs the script generating a page. * * Note: If you wish to call $file->get_filesize(), $file->get_contenthash() or * $file->get_timemodified() make sure that recursion does not happen. * * Called from {@link stored_file::sync_external_file()} * * @uses stored_file::set_missingsource() * @uses stored_file::set_synchronized() * @param stored_file $file * @return bool false when file does not need synchronisation, true if it was synchronised */ public function sync_reference(stored_file $file) { if ($file->get_repository_id() != $this->id) { // This should not really happen because the function can be called from stored_file only. return false; } if ($this->has_moodle_files()) { // References to local files need to be synchronised only once. // Later they will be synchronised automatically when the source is changed. if ($file->get_referencelastsync()) { return false; } $fs = get_file_storage(); $params = file_storage::unpack_reference($file->get_reference(), true); if (!is_array($params) || !($storedfile = $fs->get_file($params['contextid'], $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']))) { $file->set_missingsource(); } else { $file->set_synchronized($storedfile->get_contenthash(), $storedfile->get_filesize(), 0, $storedfile->get_timemodified()); } return true; } return false; }
/** * Repository method to serve file * * @param stored_file $storedfile * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime = 86400, $filter = 0, $forcedownload = false, array $options = null) { $reference = $storedfile->get_reference(); if ($reference[0] == '/') { $file = $this->root_path . substr($reference, 1, strlen($reference) - 1); } else { $file = $this->root_path . $reference; } send_file($file, $storedfile->get_filename(), 'default', $filter, false, $forcedownload); }
/** * Repository method to serve file * * @param stored_file $storedfile * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime = 86400, $filter = 0, $forcedownload = false, array $options = null) { $ref = $storedfile->get_reference(); // Let box.net serve the file. header('Location: ' . $ref); }
/** * Downloads the file from external repository and saves it in moodle filepool. * This function is different from {@link repository::sync_external_file()} because it has * bigger request timeout and always downloads the content. * * This function is invoked when we try to unlink the file from the source and convert * a reference into a true copy. * * @throws exception when file could not be imported * * @param stored_file $file * @param int $maxbytes throw an exception if file size is bigger than $maxbytes (0 means no limit) */ public function import_external_file_contents(stored_file $file, $maxbytes = 0) { if (!$file->is_external_file()) { // nothing to import if the file is not a reference return; } else { if ($file->get_repository_id() != $this->id) { // error debugging('Repository instance id does not match'); return; } else { if ($this->has_moodle_files()) { // files that are references to local files are already in moodle filepool // just validate the size if ($maxbytes > 0 && $file->get_filesize() > $maxbytes) { throw new file_exception('maxbytes'); } return; } else { if ($maxbytes > 0 && $file->get_filesize() > $maxbytes) { // note that stored_file::get_filesize() also calls synchronisation throw new file_exception('maxbytes'); } $fs = get_file_storage(); $contentexists = $fs->content_exists($file->get_contenthash()); if ($contentexists && $file->get_filesize() && $file->get_contenthash() === sha1('')) { // even when 'file_storage::content_exists()' returns true this may be an empty // content for the file that was not actually downloaded $contentexists = false; } $now = time(); if ($file->get_referencelastsync() + $file->get_referencelifetime() >= $now && !$file->get_status() && $contentexists) { // we already have the content in moodle filepool and it was synchronised recently. // Repositories may overwrite it if they want to force synchronisation anyway! return; } else { // attempt to get a file try { $fileinfo = $this->get_file($file->get_reference()); if (isset($fileinfo['path'])) { list($contenthash, $filesize, $newfile) = $fs->add_file_to_pool($fileinfo['path']); // set this file and other similar aliases synchronised $lifetime = $this->get_reference_file_lifetime($file->get_reference()); $file->set_synchronized($contenthash, $filesize, 0, $lifetime); } else { throw new moodle_exception('errorwhiledownload', 'repository', '', ''); } } catch (Exception $e) { if ($contentexists) { // better something than nothing. We have a copy of file. It's sync time // has expired but it is still very likely that it is the last version } else { throw $e; } } } } } } }
/** * Repository method to serve the referenced file * * @see send_stored_file * * @param stored_file $storedfile the file that contains the reference * @param int $lifetime Number of seconds before the file should expire from caches (null means $CFG->filelifetime) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime = null, $filter = 0, $forcedownload = false, array $options = null) { global $USER; $caller = '\\repository_office365::send_file'; $reference = $this->unpack_reference($storedfile->get_reference()); $fileuserid = $storedfile->get_userid(); $sourceclient = $this->get_onedrive_apiclient(false, $fileuserid); if (empty($sourceclient)) { \local_o365\utils::debug('Could not construct api client for user', 'send_file', $fileuserid); send_file_not_found(); die; } $fileinfo = $sourceclient->get_file_metadata($reference['id']); // Do embedding if relevant. $doembed = $this->do_embedding($reference, $forcedownload); if ($doembed === true) { if (\local_o365\utils::is_o365_connected($USER->id) !== true) { // Embedding currently only supported for logged-in Office 365 users. echo get_string('erroro365required', 'repository_office365'); die; } if (!empty($sourceclient)) { if (isset($fileinfo['webUrl'])) { $fileurl = $fileinfo['webUrl']; } else { $fileurl = isset($reference['url']) ? $reference['url'] : ''; } if (empty($fileurl)) { $errstr = 'Embed was requested, but could not get file info to complete request.'; \local_o365\utils::debug($errstr, 'send_file', ['reference' => $reference, 'fileinfo' => $fileinfo]); } else { try { $embedurl = $sourceclient->get_embed_url($reference['id'], $fileurl); $embedurl = isset($embedurl['value']) ? $embedurl['value'] : ''; } catch (\Exception $e) { // Note: exceptions will already be logged in get_embed_url. $embedurl = ''; } if (!empty($embedurl)) { redirect($embedurl); } else { if (!empty($fileurl)) { redirect($fileurl); } else { $errstr = 'Embed was requested, but could not complete.'; \local_o365\utils::debug($errstr, 'send_file', $reference); } } } } else { \local_o365\utils::debug('Could not construct OneDrive client for system api user.', 'send_file'); } } redirect($fileinfo['webUrl']); }
/** * Send a file to the browser. * * @param stored_file $storedfile The storedfile object containing a reference to the file * @param int $lifetime How long to cache the file * @param int $filter * @param boolean $forcedownload * @param array $options */ public function send_file($storedfile, $lifetime = 86400, $filter = 0, $forcedownload = false, array $options = null) { $ref = unserialize($storedfile->get_reference()); if (isset($ref->webContentLink)) { header('Location: ' . $ref->webContentLink); return; } if ($forcedownload) { // arbitrarily pick the first export format if (isset($ref->exportLinks) && is_object($ref->exportLinks)) { $arr = (array) $ref->exportLinks; reset($arr); header('Location: ' . current($arr)); return; } } if (isset($ref->alternateLink)) { header('Location: ' . $ref->alternateLink); return; } }
/** * Repository method to serve the referenced file * * This method is ivoked from {@link send_stored_file()}. * Dropbox repository first caches the file by reading it into temporary folder and then * serves from there. * * @param stored_file $storedfile the file that contains the reference * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime=86400 , $filter=0, $forcedownload=false, array $options = null) { $fileinfo = $this->get_file_by_reference((object)array('reference' => $storedfile->get_reference())); if ($fileinfo && !empty($fileinfo->filepath) && is_readable($fileinfo->filepath)) { $filename = $storedfile->get_filename(); if ($options && isset($options['filename'])) { $filename = $options['filename']; } $dontdie = ($options && isset($options['dontdie'])); send_file($fileinfo->filepath, $filename, $lifetime , $filter, false, $forcedownload, '', $dontdie); } else { send_file_not_found(); } }
/** * Repository method to serve file * * @param stored_file $storedfile * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime = 86400, $filter = 0, $forcedownload = false, array $options = null) { $reference = unserialize($storedfile->get_reference()); $cachedfilepath = cache_file::get($reference, array('ttl' => $this->cachedfilettl)); if ($cachedfilepath === false) { // Cache the file. $this->set_access_key($reference->access_key); $this->set_access_secret($reference->access_secret); $path = $this->get_file($reference->path); $cachedfilepath = cache_file::create_from_file($reference, $path['path']); } send_file($cachedfilepath, $storedfile->get_filename(), 'default', $filter, false, $forcedownload); }
/** * Repository method to serve file * * @param stored_file $storedfile * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime=86400 , $filter=0, $forcedownload=false, array $options = null) { global $CFG; /* ob_start(); var_dump($storedfile); $tmp = ob_get_contents(); ob_end_clean(); error_log("send_file(storedfile = {$tmp});"); */ $uuid = $storedfile->get_reference(); $ref = $this->get_link($uuid); // Let Alfresco serve the file. // TBD: this should probably open in a new window/tab??? header('Location: ' . $ref); }
/** * Repository method to serve the referenced file * * @see send_stored_file * * @param stored_file $storedfile the file that contains the reference * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime=86400 , $filter=0, $forcedownload=false, array $options = null) { $reference = $storedfile->get_reference(); if ($reference{0} == '/') { $file = $this->root_path.substr($reference, 1, strlen($reference)-1); } else { $file = $this->root_path.$reference; } if (is_readable($file)) { $filename = $storedfile->get_filename(); if ($options && isset($options['filename'])) { $filename = $options['filename']; } $dontdie = ($options && isset($options['dontdie'])); send_file($file, $filename, $lifetime , $filter, false, $forcedownload, '', $dontdie); } else { send_file_not_found(); } }
/** * Repository method to serve file * * @param stored_file $storedfile * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime = 86400, $filter = 0, $forcedownload = false, array $options = null) { $fs = get_file_storage(); $reference = $storedfile->get_reference(); $params = file_storage::unpack_reference($reference); $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); // hard coded file area and component for security $srcfile = $fs->get_file($contextid, 'course', 'legacy', 0, $filepath, $filename); send_stored_file($srcfile, $lifetime, $filter, $forcedownload, $options); }
/** * Repository method to serve the referenced file * * @param stored_file $storedfile the file that contains the reference * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime=86400 , $filter=0, $forcedownload=false, array $options = null) { $ref = $storedfile->get_reference(); // Let box.net serve the file. It will return 'no such file' content if file not found // also if file has the different name than alias, it will be returned with the box.net filename header('Location: ' . $ref); }
/** * Repository method to serve the referenced file * * @see send_stored_file * * @param stored_file $storedfile the file that contains the reference * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime=86400 , $filter=0, $forcedownload=false, array $options = null) { if ($this->has_moodle_files()) { $fs = get_file_storage(); $params = file_storage::unpack_reference($storedfile->get_reference(), true); $srcfile = null; if (is_array($params)) { $srcfile = $fs->get_file($params['contextid'], $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']); } if (empty($options)) { $options = array(); } if (!isset($options['filename'])) { $options['filename'] = $storedfile->get_filename(); } if (!$srcfile) { send_file_not_found(); } else { send_stored_file($srcfile, $lifetime, $filter, $forcedownload, $options); } } else { throw new coding_exception("Repository plugin must implement send_file() method."); } }
/** * Performs synchronisation of an external file if the previous one has expired. * * This function must be implemented for external repositories supporting * FILE_REFERENCE, it is called for existing aliases when their filesize, * contenthash or timemodified are requested. It is not called for internal * repositories (see {@link repository::has_moodle_files()}), references to * internal files are updated immediately when source is modified. * * Referenced files may optionally keep their content in Moodle filepool (for * thumbnail generation or to be able to serve cached copy). In this * case both contenthash and filesize need to be synchronized. Otherwise repositories * should use contenthash of empty file and correct filesize in bytes. * * Note that this function may be run for EACH file that needs to be synchronised at the * moment. If anything is being downloaded or requested from external sources there * should be a small timeout. The synchronisation is performed to update the size of * the file and/or to update image and re-generated image preview. There is nothing * fatal if syncronisation fails but it is fatal if syncronisation takes too long * and hangs the script generating a page. * * Note: If you wish to call $file->get_filesize(), $file->get_contenthash() or * $file->get_timemodified() make sure that recursion does not happen. * * Called from {@link stored_file::sync_external_file()} * * @uses stored_file::set_missingsource() * @uses stored_file::set_synchronized() * @param stored_file $file * @return bool false when file does not need synchronisation, true if it was synchronised */ public function sync_reference(stored_file $file) { if ($file->get_repository_id() != $this->id) { // This should not really happen because the function can be called from stored_file only. return false; } if ($this->has_moodle_files()) { // References to local files need to be synchronised only once. // Later they will be synchronised automatically when the source is changed. if ($file->get_referencelastsync()) { return false; } $fs = get_file_storage(); $params = file_storage::unpack_reference($file->get_reference(), true); if (!is_array($params) || !($storedfile = $fs->get_file($params['contextid'], $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']))) { $file->set_missingsource(); } else { $file->set_synchronized($storedfile->get_contenthash(), $storedfile->get_filesize()); } return true; } // Backward compatibility (Moodle 2.3-2.5) implementation that calls // methods repository::get_reference_file_lifetime(), repository::sync_individual_file() // and repository::get_file_by_reference(). These methods are removed from the // base repository class but may still be implemented by the child classes. // THIS IS NOT A GOOD EXAMPLE of implementation. For good examples see the overwriting methods. if (!method_exists($this, 'get_file_by_reference')) { // Function get_file_by_reference() is not implemented. No synchronisation. return false; } // Check if the previous sync result is still valid. if (method_exists($this, 'get_reference_file_lifetime')) { $lifetime = $this->get_reference_file_lifetime($file->get_reference()); } else { // Default value that was hardcoded in Moodle 2.3 - 2.5. $lifetime = 60 * 60 * 24; } if (($lastsynced = $file->get_referencelastsync()) && $lastsynced + $lifetime >= time()) { return false; } $cache = cache::make('core', 'repositories'); if (($lastsyncresult = $cache->get('sync:'.$file->get_referencefileid())) !== false) { if ($lastsyncresult === true) { // We are in the process of synchronizing this reference. // Avoid recursion when calling $file->get_filesize() and $file->get_contenthash(). return false; } else { // We have synchronised the same reference inside this request already. // It looks like the object $file was created before the synchronisation and contains old data. if (!empty($lastsyncresult['missing'])) { $file->set_missingsource(); } else { $cache->set('sync:'.$file->get_referencefileid(), true); if ($file->get_contenthash() != $lastsyncresult['contenthash'] || $file->get_filesize() != $lastsyncresult['filesize']) { $file->set_synchronized($lastsyncresult['contenthash'], $lastsyncresult['filesize']); } $cache->set('sync:'.$file->get_referencefileid(), $lastsyncresult); } return true; } } // Weird function sync_individual_file() that was present in API in 2.3 - 2.5, default value was true. if (method_exists($this, 'sync_individual_file') && !$this->sync_individual_file($file)) { return false; } // Set 'true' into the cache to indicate that file is in the process of synchronisation. $cache->set('sync:'.$file->get_referencefileid(), true); // Create object with the structure that repository::get_file_by_reference() expects. $reference = new stdClass(); $reference->id = $file->get_referencefileid(); $reference->reference = $file->get_reference(); $reference->referencehash = sha1($file->get_reference()); $reference->lastsync = $file->get_referencelastsync(); $reference->lifetime = $lifetime; $fileinfo = $this->get_file_by_reference($reference); $contenthash = null; $filesize = null; $fs = get_file_storage(); if (!empty($fileinfo->filesize)) { // filesize returned if (!empty($fileinfo->contenthash) && $fs->content_exists($fileinfo->contenthash)) { // contenthash is specified and valid $contenthash = $fileinfo->contenthash; } else if ($fileinfo->filesize == $file->get_filesize()) { // we don't know the new contenthash but the filesize did not change, // assume the contenthash did not change either $contenthash = $file->get_contenthash(); } else { // we can't save empty contenthash so generate contenthash from empty string list($contenthash, $unused1, $unused2) = $fs->add_string_to_pool(''); } $filesize = $fileinfo->filesize; } else if (!empty($fileinfo->filepath)) { // File path returned list($contenthash, $filesize, $newfile) = $fs->add_file_to_pool($fileinfo->filepath); } else if (!empty($fileinfo->handle) && is_resource($fileinfo->handle)) { // File handle returned $contents = ''; while (!feof($fileinfo->handle)) { $contents .= fread($fileinfo->handle, 8192); } fclose($fileinfo->handle); list($contenthash, $filesize, $newfile) = $fs->add_string_to_pool($contents); } else if (isset($fileinfo->content)) { // File content returned list($contenthash, $filesize, $newfile) = $fs->add_string_to_pool($fileinfo->content); } if (!isset($contenthash) or !isset($filesize)) { $file->set_missingsource(null); $cache->set('sync:'.$file->get_referencefileid(), array('missing' => true)); } else { // update files table $file->set_synchronized($contenthash, $filesize); $cache->set('sync:'.$file->get_referencefileid(), array('contenthash' => $contenthash, 'filesize' => $filesize)); } return true; }
/** * Repository method to serve file * * @param stored_file $storedfile * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime = 86400, $filter = 0, $forcedownload = false, array $options = null) { $reference = $storedfile->get_reference(); $params = file_storage::unpack_reference($reference); $filepath = clean_param($params['filepath'], PARAM_PATH); $filename = clean_param($params['filename'], PARAM_FILE); $contextid = clean_param($params['contextid'], PARAM_INT); $filearea = 'private'; $component = 'user'; $itemid = 0; $fs = get_file_storage(); $storedfile = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename); send_stored_file($storedfile, $lifetime, $filter, $forcedownload, $options); }
/** * Repository method to serve the referenced file * * This method is ivoked from {@link send_stored_file()}. * Dropbox repository first caches the file by reading it into temporary folder and then * serves from there. * * @param stored_file $storedfile the file that contains the reference * @param int $lifetime Number of seconds before the file should expire from caches (null means $CFG->filelifetime) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param array $options additional options affecting the file serving */ public function send_file($storedfile, $lifetime = null, $filter = 0, $forcedownload = false, array $options = null) { $ref = unserialize($storedfile->get_reference()); if ($storedfile->get_filesize() > $this->max_cache_bytes()) { header('Location: ' . $this->get_file_download_link($ref->url)); die; } try { $this->import_external_file_contents($storedfile, $this->max_cache_bytes()); if (!is_array($options)) { $options = array(); } $options['sendcachedexternalfile'] = true; send_stored_file($storedfile, $lifetime, $filter, $forcedownload, $options); } catch (moodle_exception $e) { // redirect to Dropbox, it will show the error. // We redirect to Dropbox shared link, not to download link here! header('Location: ' . $ref->url); die; } }
/** * Performs synchronisation of an external file if the previous one has expired. * * This function must be implemented for external repositories supporting * FILE_REFERENCE, it is called for existing aliases when their filesize, * contenthash or timemodified are requested. It is not called for internal * repositories (see {@link repository::has_moodle_files()}), references to * internal files are updated immediately when source is modified. * * Referenced files may optionally keep their content in Moodle filepool (for * thumbnail generation or to be able to serve cached copy). In this * case both contenthash and filesize need to be synchronized. Otherwise repositories * should use contenthash of empty file and correct filesize in bytes. * * Note that this function may be run for EACH file that needs to be synchronised at the * moment. If anything is being downloaded or requested from external sources there * should be a small timeout. The synchronisation is performed to update the size of * the file and/or to update image and re-generated image preview. There is nothing * fatal if syncronisation fails but it is fatal if syncronisation takes too long * and hangs the script generating a page. * * Note: If you wish to call $file->get_filesize(), $file->get_contenthash() or * $file->get_timemodified() make sure that recursion does not happen. * * Called from {@link stored_file::sync_external_file()} * * @inheritDocs */ public function sync_reference(stored_file $file) { global $CFG; if ($file->get_referencelastsync() + DAYSECS > time()) { // Only synchronise once per day. return false; } $reference = $this->unpack_reference($file->get_reference()); if (!isset($reference->url)) { // The URL to sync with is missing. return false; } $c = new curl(); $url = $this->get_file_download_link($reference->url); if (file_extension_in_typegroup($reference->path, 'web_image')) { $saveas = $this->prepare_file(''); try { $result = $c->download_one($url, [], ['filepath' => $saveas, 'timeout' => $CFG->repositorysyncimagetimeout, 'followlocation' => true]); $info = $c->get_info(); if ($result === true && isset($info['http_code']) && $info['http_code'] == 200) { $fs = get_file_storage(); list($contenthash, $filesize, ) = $fs->add_file_to_pool($saveas); $file->set_synchronized($contenthash, $filesize); return true; } } catch (Exception $e) { // IF the download_one fails, we will attempt to download // again with get() anyway. } } $c->get($url, null, array('timeout' => $CFG->repositorysyncimagetimeout, 'followlocation' => true, 'nobody' => true)); $info = $c->get_info(); if (isset($info['http_code']) && $info['http_code'] == 200 && array_key_exists('download_content_length', $info) && $info['download_content_length'] >= 0) { $filesize = (int) $info['download_content_length']; $file->set_synchronized(null, $filesize); return true; } $file->set_missingsource(); return true; }