Ejemplo n.º 1
0
    /**
     * 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;
    }
Ejemplo n.º 2
0
 /**
  * Performs synchronisation of reference to an external file if the previous one has expired.
  *
  * @param stored_file $file
  * @param bool $resetsynchistory whether to reset all history of sync (used by phpunit)
  * @return bool success
  */
 public static function sync_external_file($file, $resetsynchistory = false)
 {
     global $DB;
     // TODO MDL-25290 static should be replaced with MUC code.
     static $synchronized = array();
     if ($resetsynchistory) {
         $synchronized = array();
     }
     $fs = get_file_storage();
     if (!$file || !$file->get_referencefileid()) {
         return false;
     }
     if (array_key_exists($file->get_id(), $synchronized)) {
         return $synchronized[$file->get_id()];
     }
     // remember that we already cached in current request to prevent from querying again
     $synchronized[$file->get_id()] = false;
     if (!($reference = $DB->get_record('files_reference', array('id' => $file->get_referencefileid())))) {
         return false;
     }
     if (!empty($reference->lastsync) and $reference->lastsync + $reference->lifetime > time()) {
         $synchronized[$file->get_id()] = true;
         return true;
     }
     if (!($repository = self::get_repository_by_id($reference->repositoryid, SYSCONTEXTID))) {
         return false;
     }
     if (!$repository->sync_individual_file($file)) {
         return false;
     }
     $lifetime = $repository->get_reference_file_lifetime($reference);
     $fileinfo = $repository->get_file_by_reference($reference);
     if ($fileinfo === null) {
         // does not exist any more - set status to missing
         $file->set_missingsource($lifetime);
         $synchronized[$file->get_id()] = true;
         return true;
     }
     $contenthash = null;
     $filesize = null;
     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
                 $fs->add_string_to_pool('');
                 $contenthash = sha1('');
             }
         }
         $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($handle, 8192);
                 }
                 fclose($fileinfo->handle);
                 list($contenthash, $filesize, $newfile) = $fs->add_string_to_pool($content);
             } 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)) {
         return false;
     }
     // update files table
     $file->set_synchronized($contenthash, $filesize, 0, $lifetime);
     $synchronized[$file->get_id()] = true;
     return true;
 }
Ejemplo n.º 3
0
    /**
     * Call to request proxy file sync with repository source.
     *
     * @param stored_file $file
     * @return bool success
     */
    public static function sync_external_file(stored_file $file) {
        global $DB;

        $fs = get_file_storage();

        if (!$reference = $DB->get_record('files_reference', array('id'=>$file->get_referencefileid()))) {
            return false;
        }

        if (!empty($reference->lastsync) and ($reference->lastsync + $reference->lifetime > time())) {
            return false;
        }

        if (!$repository = self::get_repository_by_id($reference->repositoryid, SYSCONTEXTID)) {
            return false;
        }

        if (!$repository->sync_individual_file($file)) {
            return false;
        }

        $fileinfo = $repository->get_file_by_reference($reference);
        if ($fileinfo === null) {
            // does not exist any more - set status to missing
            $sql = "UPDATE {files} SET status = :missing WHERE referencefileid = :referencefileid";
            $params = array('referencefileid'=>$reference->id, 'missing'=>666);
            $DB->execute($sql, $params);
            //TODO: purge content from pool if we set some other content hash and it is no used any more
            return true;
        } else if ($fileinfo === false) {
            // error
            return false;
        }

        $contenthash = null;
        $filesize = null;
        if (!empty($fileinfo->contenthash)) {
            // contenthash returned, file already in moodle
            $contenthash = $fileinfo->contenthash;
            $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($handle, 8192);
            }
            fclose($fileinfo->handle);
            list($contenthash, $filesize, $newfile) = $fs->add_string_to_pool($content);
        } 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)) {
            return false;
        }

        $now = time();
        // update files table
        $sql = "UPDATE {files} SET contenthash = :contenthash, filesize = :filesize, referencelastsync = :now, referencelifetime = :lifetime, timemodified = :now2 WHERE referencefileid = :referencefileid AND contenthash <> :contenthash2";
        $params = array('contenthash'=>$contenthash, 'filesize'=>$filesize, 'now'=>$now, 'lifetime'=>$reference->lifetime,
            'now2'=>$now, 'referencefileid'=>$reference->id, 'contenthash2'=>$contenthash);
        $DB->execute($sql, $params);

        $DB->set_field('files_reference', 'lastsync', $now, array('id'=>$reference->id));

        return true;
    }
Ejemplo n.º 4
0
 /**
  * Replaces the fields that might have changed when file was overriden in filepicker:
  * reference, contenthash, filesize, userid
  *
  * Note that field 'source' must be updated separately because
  * it has different format for draft and non-draft areas and
  * this function will usually be used to replace non-draft area
  * file with draft area file.
  *
  * @param stored_file $newfile
  * @throws coding_exception
  */
 public function replace_file_with(stored_file $newfile)
 {
     if ($newfile->get_referencefileid() && $this->fs->get_references_count_by_storedfile($this)) {
         // The new file is a reference.
         // The current file has other local files referencing to it.
         // Double reference is not allowed.
         throw new moodle_exception('errordoublereference', 'repository');
     }
     $filerecord = new stdClass();
     $contenthash = $newfile->get_contenthash();
     if ($this->fs->content_exists($contenthash)) {
         $filerecord->contenthash = $contenthash;
     } else {
         throw new file_exception('storedfileproblem', 'Invalid contenthash, content must be already in filepool', $contenthash);
     }
     $filerecord->filesize = $newfile->get_filesize();
     $filerecord->referencefileid = $newfile->get_referencefileid();
     $filerecord->userid = $newfile->get_userid();
     $this->update($filerecord);
 }
Ejemplo n.º 5
0
    /**
     * Call to request proxy file sync with repository source.
     *
     * @param stored_file $file
     * @param bool $resetsynchistory whether to reset all history of sync (used by phpunit)
     * @return bool success
     */
    public static function sync_external_file($file, $resetsynchistory = false) {
        global $DB;
        // TODO MDL-25290 static should be replaced with MUC code.
        static $synchronized = array();
        if ($resetsynchistory) {
            $synchronized = array();
        }

        $fs = get_file_storage();

        if (!$file || !$file->get_referencefileid()) {
            return false;
        }
        if (array_key_exists($file->get_id(), $synchronized)) {
            return $synchronized[$file->get_id()];
        }

        // remember that we already cached in current request to prevent from querying again
        $synchronized[$file->get_id()] = false;

        if (!$reference = $DB->get_record('files_reference', array('id'=>$file->get_referencefileid()))) {
            return false;
        }

        if (!empty($reference->lastsync) and ($reference->lastsync + $reference->lifetime > time())) {
            $synchronized[$file->get_id()] = true;
            return true;
        }

        if (!$repository = self::get_repository_by_id($reference->repositoryid, SYSCONTEXTID)) {
            return false;
        }

        if (!$repository->sync_individual_file($file)) {
            return false;
        }

        $fileinfo = $repository->get_file_by_reference($reference);
        if ($fileinfo === null) {
            // does not exist any more - set status to missing
            $file->set_missingsource();
            //TODO: purge content from pool if we set some other content hash and it is no used any more
            $synchronized[$file->get_id()] = true;
            return true;
        }

        $contenthash = null;
        $filesize = null;
        if (!empty($fileinfo->contenthash)) {
            // contenthash returned, file already in moodle
            $contenthash = $fileinfo->contenthash;
            $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($handle, 8192);
            }
            fclose($fileinfo->handle);
            list($contenthash, $filesize, $newfile) = $fs->add_string_to_pool($content);
        } 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)) {
            return false;
        }

        // update files table
        $file->set_synchronized($contenthash, $filesize);
        $synchronized[$file->get_id()] = true;
        return true;
    }