/** * 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; }
/** * Copies a file from somewhere else in moodle * to the portfolio temporary working directory * associated with this export * * @param stored_file $oldfile existing stored file object * @return stored_file|bool new file object */ public function copy_existing_file($oldfile) { if (array_key_exists($oldfile->get_contenthash(), $this->newfilehashes)) { return $this->newfilehashes[$oldfile->get_contenthash()]; } $fs = get_file_storage(); $file_record = $this->new_file_record_base($oldfile->get_filename()); if ($dir = $this->get('format')->get_file_directory()) { $file_record->filepath = '/' . $dir . '/'; } try { $newfile = $fs->create_file_from_storedfile($file_record, $oldfile->get_id()); $this->newfilehashes[$newfile->get_contenthash()] = $newfile; return $newfile; } catch (file_exception $e) { return false; } }
/** * Add new local file based on existing local file. * * @param stdClass|array $filerecord object or array describing changes * @param stored_file|int $fileorid id or stored_file instance of the existing local file * @return stored_file instance of newly created file */ public function create_file_from_storedfile($filerecord, $fileorid) { global $DB; if ($fileorid instanceof stored_file) { $fid = $fileorid->get_id(); } else { $fid = $fileorid; } $filerecord = (array) $filerecord; // We support arrays too, do not modify the submitted record! unset($filerecord['id']); unset($filerecord['filesize']); unset($filerecord['contenthash']); unset($filerecord['pathnamehash']); $sql = "SELECT " . self::instance_sql_fields('f', 'r') . "\n FROM {files} f\n LEFT JOIN {files_reference} r\n ON f.referencefileid = r.id\n WHERE f.id = ?"; if (!($newrecord = $DB->get_record_sql($sql, array($fid)))) { throw new file_exception('storedfileproblem', 'File does not exist'); } unset($newrecord->id); foreach ($filerecord as $key => $value) { // validate all parameters, we do not want any rubbish stored in database, right? if ($key == 'contextid' and (!is_number($value) or $value < 1)) { throw new file_exception('storedfileproblem', 'Invalid contextid'); } if ($key == 'component') { $value = clean_param($value, PARAM_COMPONENT); if (empty($value)) { throw new file_exception('storedfileproblem', 'Invalid component'); } } if ($key == 'filearea') { $value = clean_param($value, PARAM_AREA); if (empty($value)) { throw new file_exception('storedfileproblem', 'Invalid filearea'); } } if ($key == 'itemid' and (!is_number($value) or $value < 0)) { throw new file_exception('storedfileproblem', 'Invalid itemid'); } if ($key == 'filepath') { $value = clean_param($value, PARAM_PATH); if (strpos($value, '/') !== 0 or strrpos($value, '/') !== strlen($value) - 1) { // path must start and end with '/' throw new file_exception('storedfileproblem', 'Invalid file path'); } } if ($key == 'filename') { $value = clean_param($value, PARAM_FILE); if ($value === '') { // path must start and end with '/' throw new file_exception('storedfileproblem', 'Invalid file name'); } } if ($key === 'timecreated' or $key === 'timemodified') { if (!is_number($value)) { throw new file_exception('storedfileproblem', 'Invalid file ' . $key); } if ($value < 0) { //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak) $value = 0; } } if ($key == 'referencefileid' or $key == 'referencelastsync') { $value = clean_param($value, PARAM_INT); } $newrecord->{$key} = $value; } $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename); if ($newrecord->filename === '.') { // special case - only this function supports directories ;-) $directory = $this->create_directory($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); // update the existing directory with the new data $newrecord->id = $directory->get_id(); $DB->update_record('files', $newrecord); return $this->get_file_instance($newrecord); } // note: referencefileid is copied from the original file so that // creating a new file from an existing alias creates new alias implicitly. // here we just check the database consistency. if (!empty($newrecord->repositoryid)) { if ($newrecord->referencefileid != $this->get_referencefileid($newrecord->repositoryid, $newrecord->reference, MUST_EXIST)) { throw new file_reference_exception($newrecord->repositoryid, $newrecord->reference, $newrecord->referencefileid); } } try { $newrecord->id = $DB->insert_record('files', $newrecord); } catch (dml_exception $e) { throw new stored_file_creation_exception($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename, $e->debuginfo); } $this->create_directory($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); return $this->get_file_instance($newrecord); }
/** * Export the data for the given file in relation to this document. * * @param \stored_file $file The stored file we are talking about. * @return array */ public function export_file_for_engine($file) { $data = $this->export_for_engine(); // Content is index in the main document. unset($data['content']); unset($data['description1']); unset($data['description2']); // Going to append the fileid to give it a unique id. $data['id'] = $data['id'] . '-solrfile' . $file->get_id(); $data['type'] = \core_search\manager::TYPE_FILE; $data['solr_fileid'] = $file->get_id(); $data['solr_filecontenthash'] = $file->get_contenthash(); $data['solr_fileindexstatus'] = self::INDEXED_FILE_TRUE; $data['title'] = $file->get_filename(); $data['modified'] = self::format_time_for_engine($file->get_timemodified()); return $data; }
/** * Return the link to a file * * @param stored_file $file information for existing file * @param array $options array of options to pass. can contain: * attributes => hash of existing html attributes (eg title, height, width, etc) * @return string */ public static function file_output($file, $options = null) { $id = ''; if (!is_array($options)) { $options = array(); } if (!array_key_exists('entry', $options)) { $options['entry'] = true; } if (!empty($options['entry'])) { $path = 'portfolio:' . self::file_id_prefix() . $file->get_id(); } else { $path = self::get_file_directory() . $file->get_filename(); } $attributes = array(); if (!empty($options['attributes']) && is_array($options['attributes'])) { $attributes = $options['attributes']; } $attributes['rel'] = 'enclosure'; return self::make_tag($file, $path, $attributes); }
/** * Add new local file based on existing local file. * * @param stdClass|array $file_record object or array describing changes * @param stored_file|int $fileorid id or stored_file instance of the existing local file * @return stored_file instance of newly created file */ public function create_file_from_storedfile($file_record, $fileorid) { global $DB; if ($fileorid instanceof stored_file) { $fid = $fileorid->get_id(); } else { $fid = $fileorid; } $file_record = (array) $file_record; // we support arrays too, do not modify the submitted record! unset($file_record['id']); unset($file_record['filesize']); unset($file_record['contenthash']); unset($file_record['pathnamehash']); if (!($newrecord = $DB->get_record('files', array('id' => $fid)))) { throw new file_exception('storedfileproblem', 'File does not exist'); } unset($newrecord->id); foreach ($file_record as $key => $value) { // validate all parameters, we do not want any rubbish stored in database, right? if ($key == 'contextid' and (!is_number($value) or $value < 1)) { throw new file_exception('storedfileproblem', 'Invalid contextid'); } if ($key == 'component') { $value = clean_param($value, PARAM_COMPONENT); if (empty($value)) { throw new file_exception('storedfileproblem', 'Invalid component'); } } if ($key == 'filearea') { $value = clean_param($value, PARAM_AREA); if (empty($value)) { throw new file_exception('storedfileproblem', 'Invalid filearea'); } } if ($key == 'itemid' and (!is_number($value) or $value < 0)) { throw new file_exception('storedfileproblem', 'Invalid itemid'); } if ($key == 'filepath') { $value = clean_param($value, PARAM_PATH); if (strpos($value, '/') !== 0 or strrpos($value, '/') !== strlen($value) - 1) { // path must start and end with '/' throw new file_exception('storedfileproblem', 'Invalid file path'); } } if ($key == 'filename') { $value = clean_param($value, PARAM_FILE); if ($value === '') { // path must start and end with '/' throw new file_exception('storedfileproblem', 'Invalid file name'); } } if ($key === 'timecreated' or $key === 'timemodified') { if (!is_number($value)) { throw new file_exception('storedfileproblem', 'Invalid file ' . $key); } if ($value < 0) { //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak) $value = 0; } } $newrecord->{$key} = $value; } $newrecord->pathnamehash = $this->get_pathname_hash($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename); if ($newrecord->filename === '.') { // special case - only this function supports directories ;-) $directory = $this->create_directory($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); // update the existing directory with the new data $newrecord->id = $directory->get_id(); $DB->update_record('files', $newrecord); return $this->get_file_instance($newrecord); } try { $newrecord->id = $DB->insert_record('files', $newrecord); } catch (dml_exception $e) { throw new stored_file_creation_exception($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->filename, $e->debuginfo); } $this->create_directory($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); return $this->get_file_instance($newrecord); }
/** * 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; }
/** * overridden constructor to set up the file. * */ public function __construct($title, stored_file $file) { $id = portfolio_format_leap2a::file_id_prefix() . $file->get_id(); parent::__construct($id, $title, 'resource'); $this->referencedfile = $file; $this->published = $this->referencedfile->get_timecreated(); $this->updated = $this->referencedfile->get_timemodified(); $this->add_category('offline', 'resource_type'); }